1cb77f0d6SKamil Rytarowski#!/usr/bin/env perl 2882ea1d6SJoe Perches# SPDX-License-Identifier: GPL-2.0 3882ea1d6SJoe Perches# 4cb7301c7SJoe Perches# (c) 2007, Joe Perches <[email protected]> 5cb7301c7SJoe Perches# created from checkpatch.pl 6cb7301c7SJoe Perches# 7cb7301c7SJoe Perches# Print selected MAINTAINERS information for 8cb7301c7SJoe Perches# the files modified in a patch or for a file 9cb7301c7SJoe Perches# 103bd7bf5fSRoel Kluin# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch> 113bd7bf5fSRoel Kluin# perl scripts/get_maintainer.pl [OPTIONS] -f <file> 12cb7301c7SJoe Perches 13cb77f0d6SKamil Rytarowskiuse warnings; 14cb7301c7SJoe Perchesuse strict; 15cb7301c7SJoe Perches 16cb7301c7SJoe Perchesmy $P = $0; 177e1863afSJoe Perchesmy $V = '0.26'; 18cb7301c7SJoe Perches 19cb7301c7SJoe Perchesuse Getopt::Long qw(:config no_auto_abbrev); 20be17bddcSJoe Perchesuse Cwd; 216f7d98ecSJoe Perchesuse File::Find; 22e33c9fe8SJoe Perchesuse File::Spec::Functions; 239c334eb9SAlvin Šipragause open qw(:std :encoding(UTF-8)); 24cb7301c7SJoe Perches 25be17bddcSJoe Perchesmy $cur_path = fastgetcwd() . '/'; 26cb7301c7SJoe Perchesmy $lk_path = "./"; 27cb7301c7SJoe Perchesmy $email = 1; 28cb7301c7SJoe Perchesmy $email_usename = 1; 29cb7301c7SJoe Perchesmy $email_maintainer = 1; 30c1c3f2c9SJoe Perchesmy $email_reviewer = 1; 312f5bd343SJoe Perchesmy $email_fixes = 1; 32cb7301c7SJoe Perchesmy $email_list = 1; 3349662503SJoe Perchesmy $email_moderated_list = 1; 34cb7301c7SJoe Perchesmy $email_subscriber_list = 0; 35cb7301c7SJoe Perchesmy $email_git_penguin_chiefs = 0; 36e3e9d114SJoe Perchesmy $email_git = 0; 370fa05599SFlorian Micklermy $email_git_all_signature_types = 0; 3860db31acSJoe Perchesmy $email_git_blame = 0; 39683c6f8fSJoe Perchesmy $email_git_blame_signatures = 1; 40e3e9d114SJoe Perchesmy $email_git_fallback = 1; 41cb7301c7SJoe Perchesmy $email_git_min_signatures = 1; 42cb7301c7SJoe Perchesmy $email_git_max_maintainers = 5; 43afa81ee1SJoe Perchesmy $email_git_min_percent = 5; 44cb7301c7SJoe Perchesmy $email_git_since = "1-year-ago"; 4560db31acSJoe Perchesmy $email_hg_since = "-365"; 46dace8e30SFlorian Micklermy $interactive = 0; 4711ecf53cSJoe Perchesmy $email_remove_duplicates = 1; 48b9e2331dSJoe Perchesmy $email_use_mailmap = 1; 49cb7301c7SJoe Perchesmy $output_multiline = 1; 50cb7301c7SJoe Perchesmy $output_separator = ", "; 513c7385b8SJoe Perchesmy $output_roles = 0; 527e1863afSJoe Perchesmy $output_rolestats = 1; 539ad18c85SVlastimil Babkamy $output_substatus = undef; 54364f68dcSJoe Perchesmy $output_section_maxlen = 50; 55cb7301c7SJoe Perchesmy $scm = 0; 5631bb82c9SAntonio Nino Diazmy $tree = 1; 57cb7301c7SJoe Perchesmy $web = 0; 58033964f1SJani Nikulamy $bug = 0; 59cb7301c7SJoe Perchesmy $subsystem = 0; 60cb7301c7SJoe Perchesmy $status = 0; 6103aed214SJoe Perchesmy $letters = ""; 62dcf36a92SJoe Perchesmy $keywords = 1; 6371ca5ee1SJoe Perchesmy $keywords_in_file = 0; 644b76c9daSJoe Perchesmy $sections = 0; 650c78c013SJoe Perchesmy $email_file_emails = 0; 664a7fdb5fSJoe Perchesmy $from_filename = 0; 673fb55652SJoe Perchesmy $pattern_depth = 0; 68083bf9c5SJoe Perchesmy $self_test = undef; 69cb7301c7SJoe Perchesmy $version = 0; 70cb7301c7SJoe Perchesmy $help = 0; 716f7d98ecSJoe Perchesmy $find_maintainer_files = 0; 725f0baf95SJoe Perchesmy $maintainer_path; 73683c6f8fSJoe Perchesmy $vcs_used = 0; 74683c6f8fSJoe Perches 75cb7301c7SJoe Perchesmy $exit = 0; 76cb7301c7SJoe Perches 770c78c013SJoe Perchesmy @files = (); 780c78c013SJoe Perchesmy @fixes = (); # If a patch description includes Fixes: lines 790c78c013SJoe Perchesmy @range = (); 800c78c013SJoe Perchesmy @keyword_tvi = (); 810c78c013SJoe Perchesmy @file_emails = (); 820c78c013SJoe Perches 83683c6f8fSJoe Perchesmy %commit_author_hash; 84683c6f8fSJoe Perchesmy %commit_signer_hash; 85dace8e30SFlorian Mickler 86cb7301c7SJoe Perchesmy @penguin_chief = (); 87cb7301c7SJoe Perchespush(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org"); 88cb7301c7SJoe Perches#Andrew wants in on most everything - 2009/01/14 89cb7301c7SJoe Perches#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org"); 90cb7301c7SJoe Perches 91cb7301c7SJoe Perchesmy @penguin_chief_names = (); 92cb7301c7SJoe Perchesforeach my $chief (@penguin_chief) { 93cb7301c7SJoe Perches if ($chief =~ m/^(.*):(.*)/) { 94cb7301c7SJoe Perches my $chief_name = $1; 95cb7301c7SJoe Perches my $chief_addr = $2; 96cb7301c7SJoe Perches push(@penguin_chief_names, $chief_name); 97cb7301c7SJoe Perches } 98cb7301c7SJoe Perches} 99cb7301c7SJoe Perchesmy $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)"; 100cb7301c7SJoe Perches 101e4d26b02SJoe Perches# Signature types of people who are either 102e4d26b02SJoe Perches# a) responsible for the code in question, or 103e4d26b02SJoe Perches# b) familiar enough with it to give relevant feedback 104e4d26b02SJoe Perchesmy @signature_tags = (); 105e4d26b02SJoe Perchespush(@signature_tags, "Signed-off-by:"); 106e4d26b02SJoe Perchespush(@signature_tags, "Reviewed-by:"); 107e4d26b02SJoe Perchespush(@signature_tags, "Acked-by:"); 108e4d26b02SJoe Perches 1097dea2681SJoe Perchesmy $signature_pattern = "\(" . join("|", @signature_tags) . "\)"; 1107dea2681SJoe Perches 1115f2441e9SJoe Perches# rfc822 email address - preloaded methods go here. 1121b5e1cf6SJoe Perchesmy $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])"; 113df4cc036SJoe Perchesmy $rfc822_char = '[\\000-\\377]'; 1141b5e1cf6SJoe Perches 11560db31acSJoe Perches# VCS command support: class-like functions and strings 11660db31acSJoe Perches 11760db31acSJoe Perchesmy %VCS_cmds; 11860db31acSJoe Perches 11960db31acSJoe Perchesmy %VCS_cmds_git = ( 12060db31acSJoe Perches "execute_cmd" => \&git_execute_cmd, 121ec83b616SRichard Genoud "available" => '(which("git") ne "") && (-e ".git")', 122683c6f8fSJoe Perches "find_signers_cmd" => 123ed128feaSIan Campbell "git log --no-color --follow --since=\$email_git_since " . 124c9ecefeaSJoe Perches '--numstat --no-merges ' . 125683c6f8fSJoe Perches '--format="GitCommit: %H%n' . 126683c6f8fSJoe Perches 'GitAuthor: %an <%ae>%n' . 127683c6f8fSJoe Perches 'GitDate: %aD%n' . 128683c6f8fSJoe Perches 'GitSubject: %s%n' . 129683c6f8fSJoe Perches '%b%n"' . 130683c6f8fSJoe Perches " -- \$file", 131683c6f8fSJoe Perches "find_commit_signers_cmd" => 132683c6f8fSJoe Perches "git log --no-color " . 133c9ecefeaSJoe Perches '--numstat ' . 134683c6f8fSJoe Perches '--format="GitCommit: %H%n' . 135683c6f8fSJoe Perches 'GitAuthor: %an <%ae>%n' . 136683c6f8fSJoe Perches 'GitDate: %aD%n' . 137683c6f8fSJoe Perches 'GitSubject: %s%n' . 138683c6f8fSJoe Perches '%b%n"' . 139683c6f8fSJoe Perches " -1 \$commit", 140683c6f8fSJoe Perches "find_commit_author_cmd" => 141683c6f8fSJoe Perches "git log --no-color " . 142c9ecefeaSJoe Perches '--numstat ' . 143683c6f8fSJoe Perches '--format="GitCommit: %H%n' . 144683c6f8fSJoe Perches 'GitAuthor: %an <%ae>%n' . 145683c6f8fSJoe Perches 'GitDate: %aD%n' . 146683c6f8fSJoe Perches 'GitSubject: %s%n"' . 147683c6f8fSJoe Perches " -1 \$commit", 14860db31acSJoe Perches "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file", 14960db31acSJoe Perches "blame_file_cmd" => "git blame -l \$file", 150683c6f8fSJoe Perches "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})", 151dace8e30SFlorian Mickler "blame_commit_pattern" => "^([0-9a-f]+) ", 152683c6f8fSJoe Perches "author_pattern" => "^GitAuthor: (.*)", 153683c6f8fSJoe Perches "subject_pattern" => "^GitSubject: (.*)", 154c9ecefeaSJoe Perches "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$", 1554cad35a7SJoe Perches "file_exists_cmd" => "git ls-files \$file", 156e1f75904STom Saeger "list_files_cmd" => "git ls-files \$file", 15760db31acSJoe Perches); 15860db31acSJoe Perches 15960db31acSJoe Perchesmy %VCS_cmds_hg = ( 16060db31acSJoe Perches "execute_cmd" => \&hg_execute_cmd, 16160db31acSJoe Perches "available" => '(which("hg") ne "") && (-d ".hg")', 16260db31acSJoe Perches "find_signers_cmd" => 16360db31acSJoe Perches "hg log --date=\$email_hg_since " . 164683c6f8fSJoe Perches "--template='HgCommit: {node}\\n" . 165683c6f8fSJoe Perches "HgAuthor: {author}\\n" . 166683c6f8fSJoe Perches "HgSubject: {desc}\\n'" . 167683c6f8fSJoe Perches " -- \$file", 168683c6f8fSJoe Perches "find_commit_signers_cmd" => 169683c6f8fSJoe Perches "hg log " . 170683c6f8fSJoe Perches "--template='HgSubject: {desc}\\n'" . 171683c6f8fSJoe Perches " -r \$commit", 172683c6f8fSJoe Perches "find_commit_author_cmd" => 173683c6f8fSJoe Perches "hg log " . 174683c6f8fSJoe Perches "--template='HgCommit: {node}\\n" . 175683c6f8fSJoe Perches "HgAuthor: {author}\\n" . 176683c6f8fSJoe Perches "HgSubject: {desc|firstline}\\n'" . 177683c6f8fSJoe Perches " -r \$commit", 17860db31acSJoe Perches "blame_range_cmd" => "", # not supported 179683c6f8fSJoe Perches "blame_file_cmd" => "hg blame -n \$file", 180683c6f8fSJoe Perches "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})", 181dace8e30SFlorian Mickler "blame_commit_pattern" => "^([ 0-9a-f]+):", 182683c6f8fSJoe Perches "author_pattern" => "^HgAuthor: (.*)", 183683c6f8fSJoe Perches "subject_pattern" => "^HgSubject: (.*)", 184c9ecefeaSJoe Perches "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$", 1854cad35a7SJoe Perches "file_exists_cmd" => "hg files \$file", 186e1f75904STom Saeger "list_files_cmd" => "hg manifest -R \$file", 18760db31acSJoe Perches); 18860db31acSJoe Perches 189bcde44edSJoe Perchesmy $conf = which_conf(".get_maintainer.conf"); 190bcde44edSJoe Perchesif (-f $conf) { 191368669daSJoe Perches my @conf_args; 192bcde44edSJoe Perches open(my $conffile, '<', "$conf") 193bcde44edSJoe Perches or warn "$P: Can't find a readable .get_maintainer.conf file $!\n"; 194bcde44edSJoe Perches 195368669daSJoe Perches while (<$conffile>) { 196368669daSJoe Perches my $line = $_; 197368669daSJoe Perches 198368669daSJoe Perches $line =~ s/\s*\n?$//g; 199368669daSJoe Perches $line =~ s/^\s*//g; 200368669daSJoe Perches $line =~ s/\s+/ /g; 201368669daSJoe Perches 202368669daSJoe Perches next if ($line =~ m/^\s*#/); 203368669daSJoe Perches next if ($line =~ m/^\s*$/); 204368669daSJoe Perches 205368669daSJoe Perches my @words = split(" ", $line); 206368669daSJoe Perches foreach my $word (@words) { 207368669daSJoe Perches last if ($word =~ m/^#/); 208368669daSJoe Perches push (@conf_args, $word); 209368669daSJoe Perches } 210368669daSJoe Perches } 211368669daSJoe Perches close($conffile); 212368669daSJoe Perches unshift(@ARGV, @conf_args) if @conf_args; 213368669daSJoe Perches} 214368669daSJoe Perches 215435de078SJoe Perchesmy @ignore_emails = (); 216435de078SJoe Perchesmy $ignore_file = which_conf(".get_maintainer.ignore"); 217435de078SJoe Perchesif (-f $ignore_file) { 218435de078SJoe Perches open(my $ignore, '<', "$ignore_file") 219435de078SJoe Perches or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n"; 220435de078SJoe Perches while (<$ignore>) { 221435de078SJoe Perches my $line = $_; 222435de078SJoe Perches 223435de078SJoe Perches $line =~ s/\s*\n?$//; 224435de078SJoe Perches $line =~ s/^\s*//; 225435de078SJoe Perches $line =~ s/\s+$//; 226435de078SJoe Perches $line =~ s/#.*$//; 227435de078SJoe Perches 228435de078SJoe Perches next if ($line =~ m/^\s*$/); 229435de078SJoe Perches if (rfc822_valid($line)) { 230435de078SJoe Perches push(@ignore_emails, $line); 231435de078SJoe Perches } 232435de078SJoe Perches } 233435de078SJoe Perches close($ignore); 234435de078SJoe Perches} 235435de078SJoe Perches 236e1f75904STom Saegerif ($#ARGV > 0) { 237e1f75904STom Saeger foreach (@ARGV) { 238083bf9c5SJoe Perches if ($_ =~ /^-{1,2}self-test(?:=|$)/) { 239e1f75904STom Saeger die "$P: using --self-test does not allow any other option or argument\n"; 240e1f75904STom Saeger } 241e1f75904STom Saeger } 242e1f75904STom Saeger} 243e1f75904STom Saeger 244cb7301c7SJoe Perchesif (!GetOptions( 245cb7301c7SJoe Perches 'email!' => \$email, 246cb7301c7SJoe Perches 'git!' => \$email_git, 247e4d26b02SJoe Perches 'git-all-signature-types!' => \$email_git_all_signature_types, 24860db31acSJoe Perches 'git-blame!' => \$email_git_blame, 249683c6f8fSJoe Perches 'git-blame-signatures!' => \$email_git_blame_signatures, 250e3e9d114SJoe Perches 'git-fallback!' => \$email_git_fallback, 251cb7301c7SJoe Perches 'git-chief-penguins!' => \$email_git_penguin_chiefs, 252cb7301c7SJoe Perches 'git-min-signatures=i' => \$email_git_min_signatures, 253cb7301c7SJoe Perches 'git-max-maintainers=i' => \$email_git_max_maintainers, 254afa81ee1SJoe Perches 'git-min-percent=i' => \$email_git_min_percent, 255cb7301c7SJoe Perches 'git-since=s' => \$email_git_since, 25660db31acSJoe Perches 'hg-since=s' => \$email_hg_since, 257dace8e30SFlorian Mickler 'i|interactive!' => \$interactive, 25811ecf53cSJoe Perches 'remove-duplicates!' => \$email_remove_duplicates, 259b9e2331dSJoe Perches 'mailmap!' => \$email_use_mailmap, 260cb7301c7SJoe Perches 'm!' => \$email_maintainer, 261c1c3f2c9SJoe Perches 'r!' => \$email_reviewer, 262cb7301c7SJoe Perches 'n!' => \$email_usename, 263cb7301c7SJoe Perches 'l!' => \$email_list, 2642f5bd343SJoe Perches 'fixes!' => \$email_fixes, 26549662503SJoe Perches 'moderated!' => \$email_moderated_list, 266cb7301c7SJoe Perches 's!' => \$email_subscriber_list, 267cb7301c7SJoe Perches 'multiline!' => \$output_multiline, 2683c7385b8SJoe Perches 'roles!' => \$output_roles, 2693c7385b8SJoe Perches 'rolestats!' => \$output_rolestats, 270cb7301c7SJoe Perches 'separator=s' => \$output_separator, 271cb7301c7SJoe Perches 'subsystem!' => \$subsystem, 272cb7301c7SJoe Perches 'status!' => \$status, 2739ad18c85SVlastimil Babka 'substatus!' => \$output_substatus, 274cb7301c7SJoe Perches 'scm!' => \$scm, 27531bb82c9SAntonio Nino Diaz 'tree!' => \$tree, 276cb7301c7SJoe Perches 'web!' => \$web, 277033964f1SJani Nikula 'bug!' => \$bug, 27803aed214SJoe Perches 'letters=s' => \$letters, 2793fb55652SJoe Perches 'pattern-depth=i' => \$pattern_depth, 280dcf36a92SJoe Perches 'k|keywords!' => \$keywords, 28171ca5ee1SJoe Perches 'kf|keywords-in-file!' => \$keywords_in_file, 2824b76c9daSJoe Perches 'sections!' => \$sections, 2830c78c013SJoe Perches 'fe|file-emails!' => \$email_file_emails, 2844a7fdb5fSJoe Perches 'f|file' => \$from_filename, 2856f7d98ecSJoe Perches 'find-maintainer-files' => \$find_maintainer_files, 2865f0baf95SJoe Perches 'mpath|maintainer-path=s' => \$maintainer_path, 287083bf9c5SJoe Perches 'self-test:s' => \$self_test, 288cb7301c7SJoe Perches 'v|version' => \$version, 28964f77f31SJoe Perches 'h|help|usage' => \$help, 290cb7301c7SJoe Perches )) { 2913c7385b8SJoe Perches die "$P: invalid argument - use --help if necessary\n"; 292cb7301c7SJoe Perches} 293cb7301c7SJoe Perches 294cb7301c7SJoe Perchesif ($help != 0) { 295cb7301c7SJoe Perches usage(); 296cb7301c7SJoe Perches exit 0; 297cb7301c7SJoe Perches} 298cb7301c7SJoe Perches 299cb7301c7SJoe Perchesif ($version != 0) { 300cb7301c7SJoe Perches print("${P} ${V}\n"); 301cb7301c7SJoe Perches exit 0; 302cb7301c7SJoe Perches} 303cb7301c7SJoe Perches 304083bf9c5SJoe Perchesif (defined $self_test) { 305e1f75904STom Saeger read_all_maintainer_files(); 306083bf9c5SJoe Perches self_test(); 307e1f75904STom Saeger exit 0; 308e1f75904STom Saeger} 309e1f75904STom Saeger 31064f77f31SJoe Perchesif (-t STDIN && !@ARGV) { 31164f77f31SJoe Perches # We're talking to a terminal, but have no command line arguments. 31264f77f31SJoe Perches die "$P: missing patchfile or -f file - use --help if necessary\n"; 313cb7301c7SJoe Perches} 314cb7301c7SJoe Perches 315683c6f8fSJoe Perches$output_multiline = 0 if ($output_separator ne ", "); 316683c6f8fSJoe Perches$output_rolestats = 1 if ($interactive); 317683c6f8fSJoe Perches$output_roles = 1 if ($output_rolestats); 3183c7385b8SJoe Perches 3199ad18c85SVlastimil Babkaif (!defined $output_substatus) { 3209ad18c85SVlastimil Babka $output_substatus = $email && $output_roles && -t STDOUT; 3219ad18c85SVlastimil Babka} 3229ad18c85SVlastimil Babka 32303aed214SJoe Perchesif ($sections || $letters ne "") { 32403aed214SJoe Perches $sections = 1; 3254b76c9daSJoe Perches $email = 0; 3264b76c9daSJoe Perches $email_list = 0; 3274b76c9daSJoe Perches $scm = 0; 3284b76c9daSJoe Perches $status = 0; 3294b76c9daSJoe Perches $subsystem = 0; 3304b76c9daSJoe Perches $web = 0; 331033964f1SJani Nikula $bug = 0; 3324b76c9daSJoe Perches $keywords = 0; 33371ca5ee1SJoe Perches $keywords_in_file = 0; 3346ef1c52eSJoe Perches $interactive = 0; 3354b76c9daSJoe Perches} else { 336033964f1SJani Nikula my $selections = $email + $scm + $status + $subsystem + $web + $bug; 337cb7301c7SJoe Perches if ($selections == 0) { 338033964f1SJani Nikula die "$P: Missing required option: email, scm, status, subsystem, web or bug\n"; 339cb7301c7SJoe Perches } 3404b76c9daSJoe Perches} 341cb7301c7SJoe Perches 342f5492666SJoe Perchesif ($email && 343c1c3f2c9SJoe Perches ($email_maintainer + $email_reviewer + 344c1c3f2c9SJoe Perches $email_list + $email_subscriber_list + 345f5492666SJoe Perches $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) { 346cb7301c7SJoe Perches die "$P: Please select at least 1 email option\n"; 347cb7301c7SJoe Perches} 348cb7301c7SJoe Perches 34931bb82c9SAntonio Nino Diazif ($tree && !top_of_kernel_tree($lk_path)) { 350cb7301c7SJoe Perches die "$P: The current directory does not appear to be " 351cb7301c7SJoe Perches . "a linux kernel source tree.\n"; 352cb7301c7SJoe Perches} 353cb7301c7SJoe Perches 354cb7301c7SJoe Perches## Read MAINTAINERS for type/value pairs 355cb7301c7SJoe Perches 356cb7301c7SJoe Perchesmy @typevalue = (); 357dcf36a92SJoe Perchesmy %keyword_hash; 3586f7d98ecSJoe Perchesmy @mfiles = (); 359083bf9c5SJoe Perchesmy @self_test_info = (); 360dcf36a92SJoe Perches 3616f7d98ecSJoe Perchessub read_maintainer_file { 3626f7d98ecSJoe Perches my ($file) = @_; 3636f7d98ecSJoe Perches 3646f7d98ecSJoe Perches open (my $maint, '<', "$file") 3656f7d98ecSJoe Perches or die "$P: Can't open MAINTAINERS file '$file': $!\n"; 366e1f75904STom Saeger my $i = 1; 36722dd5b0cSStephen Hemminger while (<$maint>) { 368cb7301c7SJoe Perches my $line = $_; 369083bf9c5SJoe Perches chomp $line; 370cb7301c7SJoe Perches 371ce8155f7SJoe Perches if ($line =~ m/^([A-Z]):\s*(.*)/) { 372cb7301c7SJoe Perches my $type = $1; 373cb7301c7SJoe Perches my $value = $2; 374cb7301c7SJoe Perches 375cb7301c7SJoe Perches ##Filename pattern matching 376cb7301c7SJoe Perches if ($type eq "F" || $type eq "X") { 377cb7301c7SJoe Perches $value =~ s@\.@\\\.@g; ##Convert . to \. 378cb7301c7SJoe Perches $value =~ s/\*/\.\*/g; ##Convert * to .* 379cb7301c7SJoe Perches $value =~ s/\?/\./g; ##Convert ? to . 380870020f9SJoe Perches ##if pattern is a directory and it lacks a trailing slash, add one 381870020f9SJoe Perches if ((-d $value)) { 382870020f9SJoe Perches $value =~ s@([^/])$@$1/@; 383870020f9SJoe Perches } 384dcf36a92SJoe Perches } elsif ($type eq "K") { 385dcf36a92SJoe Perches $keyword_hash{@typevalue} = $value; 386cb7301c7SJoe Perches } 387cb7301c7SJoe Perches push(@typevalue, "$type:$value"); 3886f7d98ecSJoe Perches } elsif (!(/^\s*$/ || /^\s*\#/)) { 389cb7301c7SJoe Perches push(@typevalue, $line); 390cb7301c7SJoe Perches } 391083bf9c5SJoe Perches if (defined $self_test) { 392083bf9c5SJoe Perches push(@self_test_info, {file=>$file, linenr=>$i, line=>$line}); 393083bf9c5SJoe Perches } 394e1f75904STom Saeger $i++; 395cb7301c7SJoe Perches } 39622dd5b0cSStephen Hemminger close($maint); 3976f7d98ecSJoe Perches} 398cb7301c7SJoe Perches 3996f7d98ecSJoe Perchessub find_is_maintainer_file { 4006f7d98ecSJoe Perches my ($file) = $_; 4016f7d98ecSJoe Perches return if ($file !~ m@/MAINTAINERS$@); 4026f7d98ecSJoe Perches $file = $File::Find::name; 4036f7d98ecSJoe Perches return if (! -f $file); 4046f7d98ecSJoe Perches push(@mfiles, $file); 4056f7d98ecSJoe Perches} 4066f7d98ecSJoe Perches 4076f7d98ecSJoe Perchessub find_ignore_git { 4086f7d98ecSJoe Perches return grep { $_ !~ /^\.git$/; } @_; 4096f7d98ecSJoe Perches} 4106f7d98ecSJoe Perches 411e1f75904STom Saegerread_all_maintainer_files(); 412e1f75904STom Saeger 413e1f75904STom Saegersub read_all_maintainer_files { 4145f0baf95SJoe Perches my $path = "${lk_path}MAINTAINERS"; 4155f0baf95SJoe Perches if (defined $maintainer_path) { 4165f0baf95SJoe Perches $path = $maintainer_path; 4175f0baf95SJoe Perches # Perl Cookbook tilde expansion if necessary 4185f0baf95SJoe Perches $path =~ s@^~([^/]*)@ $1 ? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7])@ex; 4195f0baf95SJoe Perches } 4205f0baf95SJoe Perches 4215f0baf95SJoe Perches if (-d $path) { 4225f0baf95SJoe Perches $path .= '/' if ($path !~ m@/$@); 4230fbd75fdSJoe Perches if ($find_maintainer_files) { 4240fbd75fdSJoe Perches find( { wanted => \&find_is_maintainer_file, 4250fbd75fdSJoe Perches preprocess => \&find_ignore_git, 4260fbd75fdSJoe Perches no_chdir => 1, 4270fbd75fdSJoe Perches }, "$path"); 4280fbd75fdSJoe Perches } else { 4295f0baf95SJoe Perches opendir(DIR, "$path") or die $!; 4306f7d98ecSJoe Perches my @files = readdir(DIR); 4316f7d98ecSJoe Perches closedir(DIR); 4326f7d98ecSJoe Perches foreach my $file (@files) { 4335f0baf95SJoe Perches push(@mfiles, "$path$file") if ($file !~ /^\./); 4346f7d98ecSJoe Perches } 4356f7d98ecSJoe Perches } 4365f0baf95SJoe Perches } elsif (-f "$path") { 4375f0baf95SJoe Perches push(@mfiles, "$path"); 4385f0baf95SJoe Perches } else { 4395f0baf95SJoe Perches die "$P: MAINTAINER file not found '$path'\n"; 4405f0baf95SJoe Perches } 4415f0baf95SJoe Perches die "$P: No MAINTAINER files found in '$path'\n" if (scalar(@mfiles) == 0); 4426f7d98ecSJoe Perches foreach my $file (@mfiles) { 4436f7d98ecSJoe Perches read_maintainer_file("$file"); 4446f7d98ecSJoe Perches } 445e1f75904STom Saeger} 4468cbb3a77SJoe Perches 4470c78c013SJoe Perchessub maintainers_in_file { 4480c78c013SJoe Perches my ($file) = @_; 4490c78c013SJoe Perches 4500c78c013SJoe Perches return if ($file =~ m@\bMAINTAINERS$@); 4510c78c013SJoe Perches 4520c78c013SJoe Perches if (-f $file && ($email_file_emails || $file =~ /\.yaml$/)) { 4530c78c013SJoe Perches open(my $f, '<', $file) 4540c78c013SJoe Perches or die "$P: Can't open $file: $!\n"; 4550c78c013SJoe Perches my $text = do { local($/) ; <$f> }; 4560c78c013SJoe Perches close($f); 4570c78c013SJoe Perches 4589c334eb9SAlvin Šipraga my @poss_addr = $text =~ m$[\p{L}\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g; 4590c78c013SJoe Perches push(@file_emails, clean_file_emails(@poss_addr)); 4600c78c013SJoe Perches } 4610c78c013SJoe Perches} 4620c78c013SJoe Perches 4637fa8ff2eSFlorian Mickler# 4647fa8ff2eSFlorian Mickler# Read mail address map 4657fa8ff2eSFlorian Mickler# 4667fa8ff2eSFlorian Mickler 467b9e2331dSJoe Perchesmy $mailmap; 468b9e2331dSJoe Perches 469b9e2331dSJoe Perchesread_mailmap(); 4707fa8ff2eSFlorian Mickler 4717fa8ff2eSFlorian Micklersub read_mailmap { 472b9e2331dSJoe Perches $mailmap = { 4737fa8ff2eSFlorian Mickler names => {}, 4747fa8ff2eSFlorian Mickler addresses => {} 4757fa8ff2eSFlorian Mickler }; 4767fa8ff2eSFlorian Mickler 477b9e2331dSJoe Perches return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap")); 4787fa8ff2eSFlorian Mickler 4797fa8ff2eSFlorian Mickler open(my $mailmap_file, '<', "${lk_path}.mailmap") 48022dd5b0cSStephen Hemminger or warn "$P: Can't open .mailmap: $!\n"; 4818cbb3a77SJoe Perches 4827fa8ff2eSFlorian Mickler while (<$mailmap_file>) { 4837fa8ff2eSFlorian Mickler s/#.*$//; #strip comments 4847fa8ff2eSFlorian Mickler s/^\s+|\s+$//g; #trim 4858cbb3a77SJoe Perches 4867fa8ff2eSFlorian Mickler next if (/^\s*$/); #skip empty lines 4877fa8ff2eSFlorian Mickler #entries have one of the following formats: 4887fa8ff2eSFlorian Mickler # name1 <mail1> 4897fa8ff2eSFlorian Mickler # <mail1> <mail2> 4907fa8ff2eSFlorian Mickler # name1 <mail1> <mail2> 4917fa8ff2eSFlorian Mickler # name1 <mail1> name2 <mail2> 4927fa8ff2eSFlorian Mickler # (see man git-shortlog) 4930334b382SJoe Perches 4940334b382SJoe Perches if (/^([^<]+)<([^>]+)>$/) { 4957fa8ff2eSFlorian Mickler my $real_name = $1; 4967fa8ff2eSFlorian Mickler my $address = $2; 4978cbb3a77SJoe Perches 4987fa8ff2eSFlorian Mickler $real_name =~ s/\s+$//; 499b9e2331dSJoe Perches ($real_name, $address) = parse_email("$real_name <$address>"); 5007fa8ff2eSFlorian Mickler $mailmap->{names}->{$address} = $real_name; 5018cbb3a77SJoe Perches 5020334b382SJoe Perches } elsif (/^<([^>]+)>\s*<([^>]+)>$/) { 5037fa8ff2eSFlorian Mickler my $real_address = $1; 5047fa8ff2eSFlorian Mickler my $wrong_address = $2; 5057fa8ff2eSFlorian Mickler 5067fa8ff2eSFlorian Mickler $mailmap->{addresses}->{$wrong_address} = $real_address; 5077fa8ff2eSFlorian Mickler 5080334b382SJoe Perches } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) { 5097fa8ff2eSFlorian Mickler my $real_name = $1; 5107fa8ff2eSFlorian Mickler my $real_address = $2; 5117fa8ff2eSFlorian Mickler my $wrong_address = $3; 5127fa8ff2eSFlorian Mickler 5137fa8ff2eSFlorian Mickler $real_name =~ s/\s+$//; 514b9e2331dSJoe Perches ($real_name, $real_address) = 515b9e2331dSJoe Perches parse_email("$real_name <$real_address>"); 5167fa8ff2eSFlorian Mickler $mailmap->{names}->{$wrong_address} = $real_name; 5177fa8ff2eSFlorian Mickler $mailmap->{addresses}->{$wrong_address} = $real_address; 5187fa8ff2eSFlorian Mickler 5190334b382SJoe Perches } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) { 5207fa8ff2eSFlorian Mickler my $real_name = $1; 5217fa8ff2eSFlorian Mickler my $real_address = $2; 5227fa8ff2eSFlorian Mickler my $wrong_name = $3; 5237fa8ff2eSFlorian Mickler my $wrong_address = $4; 5247fa8ff2eSFlorian Mickler 5257fa8ff2eSFlorian Mickler $real_name =~ s/\s+$//; 526b9e2331dSJoe Perches ($real_name, $real_address) = 527b9e2331dSJoe Perches parse_email("$real_name <$real_address>"); 5287fa8ff2eSFlorian Mickler 529b9e2331dSJoe Perches $wrong_name =~ s/\s+$//; 530b9e2331dSJoe Perches ($wrong_name, $wrong_address) = 531b9e2331dSJoe Perches parse_email("$wrong_name <$wrong_address>"); 532b9e2331dSJoe Perches 533b9e2331dSJoe Perches my $wrong_email = format_email($wrong_name, $wrong_address, 1); 534b9e2331dSJoe Perches $mailmap->{names}->{$wrong_email} = $real_name; 535b9e2331dSJoe Perches $mailmap->{addresses}->{$wrong_email} = $real_address; 5368cbb3a77SJoe Perches } 5378cbb3a77SJoe Perches } 5387fa8ff2eSFlorian Mickler close($mailmap_file); 5398cbb3a77SJoe Perches} 5408cbb3a77SJoe Perches 5414a7fdb5fSJoe Perches## use the filenames on the command line or find the filenames in the patchfiles 542cb7301c7SJoe Perches 54364f77f31SJoe Perchesif (!@ARGV) { 54464f77f31SJoe Perches push(@ARGV, "&STDIN"); 54564f77f31SJoe Perches} 54664f77f31SJoe Perches 5474a7fdb5fSJoe Perchesforeach my $file (@ARGV) { 54864f77f31SJoe Perches if ($file ne "&STDIN") { 549e33c9fe8SJoe Perches $file = canonpath($file); 550870020f9SJoe Perches ##if $file is a directory and it lacks a trailing slash, add one 551870020f9SJoe Perches if ((-d $file)) { 552870020f9SJoe Perches $file =~ s@([^/])$@$1/@; 553870020f9SJoe Perches } elsif (!(-f $file)) { 5544a7fdb5fSJoe Perches die "$P: file '${file}' not found\n"; 555cb7301c7SJoe Perches } 55664f77f31SJoe Perches } 557cdfe2d22SJoe Perches if ($from_filename && (vcs_exists() && !vcs_file_exists($file))) { 558cdfe2d22SJoe Perches warn "$P: file '$file' not found in version control $!\n"; 559cdfe2d22SJoe Perches } 560aec742e8SJoe Perches if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) { 561be17bddcSJoe Perches $file =~ s/^\Q${cur_path}\E//; #strip any absolute path 562be17bddcSJoe Perches $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree 5634a7fdb5fSJoe Perches push(@files, $file); 56471ca5ee1SJoe Perches if ($file ne "MAINTAINERS" && -f $file && $keywords && $keywords_in_file) { 56522dd5b0cSStephen Hemminger open(my $f, '<', $file) 56622dd5b0cSStephen Hemminger or die "$P: Can't open $file: $!\n"; 56722dd5b0cSStephen Hemminger my $text = do { local($/) ; <$f> }; 56822dd5b0cSStephen Hemminger close($f); 569dcf36a92SJoe Perches foreach my $line (keys %keyword_hash) { 570a8af2430SJoe Perches if ($text =~ m/$keyword_hash{$line}/x) { 571dcf36a92SJoe Perches push(@keyword_tvi, $line); 572dcf36a92SJoe Perches } 573dcf36a92SJoe Perches } 57403372dbbSJoe Perches } 575cb7301c7SJoe Perches } else { 5764a7fdb5fSJoe Perches my $file_cnt = @files; 577f5492666SJoe Perches my $lastfile; 57822dd5b0cSStephen Hemminger 5793a4df13dSWolfram Sang open(my $patch, "< $file") 58022dd5b0cSStephen Hemminger or die "$P: Can't open $file: $!\n"; 5817764dcb5SJoe Perches 5827764dcb5SJoe Perches # We can check arbitrary information before the patch 5837764dcb5SJoe Perches # like the commit message, mail headers, etc... 5847764dcb5SJoe Perches # This allows us to match arbitrary keywords against any part 5857764dcb5SJoe Perches # of a git format-patch generated file (subject tags, etc...) 5867764dcb5SJoe Perches 5877764dcb5SJoe Perches my $patch_prefix = ""; #Parsing the intro 5887764dcb5SJoe Perches 58922dd5b0cSStephen Hemminger while (<$patch>) { 590dcf36a92SJoe Perches my $patch_line = $_; 5910455c747SJoe Perches if (m/^ mode change [0-7]+ => [0-7]+ (\S+)\s*$/) { 5920455c747SJoe Perches my $filename = $1; 5930455c747SJoe Perches push(@files, $filename); 5940455c747SJoe Perches } elsif (m/^rename (?:from|to) (\S+)\s*$/) { 5950455c747SJoe Perches my $filename = $1; 5960455c747SJoe Perches push(@files, $filename); 5970455c747SJoe Perches } elsif (m/^diff --git a\/(\S+) b\/(\S+)\s*$/) { 5980455c747SJoe Perches my $filename1 = $1; 5990455c747SJoe Perches my $filename2 = $2; 6000455c747SJoe Perches push(@files, $filename1); 6010455c747SJoe Perches push(@files, $filename2); 6022f5bd343SJoe Perches } elsif (m/^Fixes:\s+([0-9a-fA-F]{6,40})/) { 6032f5bd343SJoe Perches push(@fixes, $1) if ($email_fixes); 6040455c747SJoe Perches } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) { 6054a7fdb5fSJoe Perches my $filename = $1; 6064a7fdb5fSJoe Perches $filename =~ s@^[^/]*/@@; 6074a7fdb5fSJoe Perches $filename =~ s@\n@@; 608f5492666SJoe Perches $lastfile = $filename; 6094a7fdb5fSJoe Perches push(@files, $filename); 6107764dcb5SJoe Perches $patch_prefix = "^[+-].*"; #Now parsing the actual patch 611f5492666SJoe Perches } elsif (m/^\@\@ -(\d+),(\d+)/) { 612f5492666SJoe Perches if ($email_git_blame) { 613f5492666SJoe Perches push(@range, "$lastfile:$1:$2"); 614f5492666SJoe Perches } 615dcf36a92SJoe Perches } elsif ($keywords) { 616dcf36a92SJoe Perches foreach my $line (keys %keyword_hash) { 6177764dcb5SJoe Perches if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) { 618dcf36a92SJoe Perches push(@keyword_tvi, $line); 619dcf36a92SJoe Perches } 620dcf36a92SJoe Perches } 621cb7301c7SJoe Perches } 622cb7301c7SJoe Perches } 62322dd5b0cSStephen Hemminger close($patch); 62422dd5b0cSStephen Hemminger 6254a7fdb5fSJoe Perches if ($file_cnt == @files) { 6267f29fd27SJoe Perches warn "$P: file '${file}' doesn't appear to be a patch. " 627cb7301c7SJoe Perches . "Add -f to options?\n"; 628cb7301c7SJoe Perches } 629cb7301c7SJoe Perches @files = sort_and_uniq(@files); 630cb7301c7SJoe Perches } 6314a7fdb5fSJoe Perches} 632cb7301c7SJoe Perches 63303372dbbSJoe Perches@file_emails = uniq(@file_emails); 6342f5bd343SJoe Perches@fixes = uniq(@fixes); 63503372dbbSJoe Perches 636683c6f8fSJoe Perchesmy %email_hash_name; 637683c6f8fSJoe Perchesmy %email_hash_address; 638cb7301c7SJoe Perchesmy @email_to = (); 639683c6f8fSJoe Perchesmy %hash_list_to; 640290603c1SJoe Perchesmy @list_to = (); 641cb7301c7SJoe Perchesmy @scm = (); 642cb7301c7SJoe Perchesmy @web = (); 643033964f1SJani Nikulamy @bug = (); 644cb7301c7SJoe Perchesmy @subsystem = (); 645cb7301c7SJoe Perchesmy @status = (); 6469ad18c85SVlastimil Babkamy @substatus = (); 647b9e2331dSJoe Perchesmy %deduplicate_name_hash = (); 648b9e2331dSJoe Perchesmy %deduplicate_address_hash = (); 649683c6f8fSJoe Perches 6506ef1c52eSJoe Perchesmy @maintainers = get_maintainers(); 6516ef1c52eSJoe Perchesif (@maintainers) { 6526ef1c52eSJoe Perches @maintainers = merge_email(@maintainers); 6536ef1c52eSJoe Perches output(@maintainers); 6546ef1c52eSJoe Perches} 655683c6f8fSJoe Perches 656683c6f8fSJoe Perchesif ($scm) { 657683c6f8fSJoe Perches @scm = uniq(@scm); 658683c6f8fSJoe Perches output(@scm); 659683c6f8fSJoe Perches} 660683c6f8fSJoe Perches 6619ad18c85SVlastimil Babkaif ($output_substatus) { 6629ad18c85SVlastimil Babka @substatus = uniq(@substatus); 6639ad18c85SVlastimil Babka output(@substatus); 6649ad18c85SVlastimil Babka} 6659ad18c85SVlastimil Babka 666683c6f8fSJoe Perchesif ($status) { 667683c6f8fSJoe Perches @status = uniq(@status); 668683c6f8fSJoe Perches output(@status); 669683c6f8fSJoe Perches} 670683c6f8fSJoe Perches 671683c6f8fSJoe Perchesif ($subsystem) { 672683c6f8fSJoe Perches @subsystem = uniq(@subsystem); 673683c6f8fSJoe Perches output(@subsystem); 674683c6f8fSJoe Perches} 675683c6f8fSJoe Perches 676683c6f8fSJoe Perchesif ($web) { 677683c6f8fSJoe Perches @web = uniq(@web); 678683c6f8fSJoe Perches output(@web); 679683c6f8fSJoe Perches} 680683c6f8fSJoe Perches 681033964f1SJani Nikulaif ($bug) { 682033964f1SJani Nikula @bug = uniq(@bug); 683033964f1SJani Nikula output(@bug); 684033964f1SJani Nikula} 685033964f1SJani Nikula 686683c6f8fSJoe Perchesexit($exit); 687683c6f8fSJoe Perches 688083bf9c5SJoe Perchessub self_test { 689e1f75904STom Saeger my @lsfiles = (); 690083bf9c5SJoe Perches my @good_links = (); 691083bf9c5SJoe Perches my @bad_links = (); 692083bf9c5SJoe Perches my @section_headers = (); 693083bf9c5SJoe Perches my $index = 0; 694e1f75904STom Saeger 695e1f75904STom Saeger @lsfiles = vcs_list_files($lk_path); 696e1f75904STom Saeger 697083bf9c5SJoe Perches for my $x (@self_test_info) { 698083bf9c5SJoe Perches $index++; 699083bf9c5SJoe Perches 700083bf9c5SJoe Perches ## Section header duplication and missing section content 701083bf9c5SJoe Perches if (($self_test eq "" || $self_test =~ /\bsections\b/) && 702083bf9c5SJoe Perches $x->{line} =~ /^\S[^:]/ && 703083bf9c5SJoe Perches defined $self_test_info[$index] && 704083bf9c5SJoe Perches $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) { 705083bf9c5SJoe Perches my $has_S = 0; 706083bf9c5SJoe Perches my $has_F = 0; 707083bf9c5SJoe Perches my $has_ML = 0; 708083bf9c5SJoe Perches my $status = ""; 709083bf9c5SJoe Perches if (grep(m@^\Q$x->{line}\E@, @section_headers)) { 710083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n"); 711083bf9c5SJoe Perches } else { 712083bf9c5SJoe Perches push(@section_headers, $x->{line}); 713083bf9c5SJoe Perches } 714083bf9c5SJoe Perches my $nextline = $index; 715083bf9c5SJoe Perches while (defined $self_test_info[$nextline] && 716083bf9c5SJoe Perches $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) { 717083bf9c5SJoe Perches my $type = $1; 718083bf9c5SJoe Perches my $value = $2; 719083bf9c5SJoe Perches if ($type eq "S") { 720083bf9c5SJoe Perches $has_S = 1; 721083bf9c5SJoe Perches $status = $value; 722083bf9c5SJoe Perches } elsif ($type eq "F" || $type eq "N") { 723083bf9c5SJoe Perches $has_F = 1; 724083bf9c5SJoe Perches } elsif ($type eq "M" || $type eq "R" || $type eq "L") { 725083bf9c5SJoe Perches $has_ML = 1; 726083bf9c5SJoe Perches } 727083bf9c5SJoe Perches $nextline++; 728083bf9c5SJoe Perches } 729083bf9c5SJoe Perches if (!$has_ML && $status !~ /orphan|obsolete/i) { 730083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n"); 731083bf9c5SJoe Perches } 732083bf9c5SJoe Perches if (!$has_S) { 733083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n"); 734083bf9c5SJoe Perches } 735083bf9c5SJoe Perches if (!$has_F) { 736083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n"); 737083bf9c5SJoe Perches } 738083bf9c5SJoe Perches } 739083bf9c5SJoe Perches 740083bf9c5SJoe Perches next if ($x->{line} !~ /^([A-Z]):\s*(.*)/); 741083bf9c5SJoe Perches 742083bf9c5SJoe Perches my $type = $1; 743083bf9c5SJoe Perches my $value = $2; 744083bf9c5SJoe Perches 745083bf9c5SJoe Perches ## Filename pattern matching 746083bf9c5SJoe Perches if (($type eq "F" || $type eq "X") && 747083bf9c5SJoe Perches ($self_test eq "" || $self_test =~ /\bpatterns\b/)) { 748083bf9c5SJoe Perches $value =~ s@\.@\\\.@g; ##Convert . to \. 749083bf9c5SJoe Perches $value =~ s/\*/\.\*/g; ##Convert * to .* 750083bf9c5SJoe Perches $value =~ s/\?/\./g; ##Convert ? to . 751083bf9c5SJoe Perches ##if pattern is a directory and it lacks a trailing slash, add one 752083bf9c5SJoe Perches if ((-d $value)) { 753083bf9c5SJoe Perches $value =~ s@([^/])$@$1/@; 754083bf9c5SJoe Perches } 755083bf9c5SJoe Perches if (!grep(m@^$value@, @lsfiles)) { 756083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n"); 757083bf9c5SJoe Perches } 758083bf9c5SJoe Perches 759083bf9c5SJoe Perches ## Link reachability 760083bf9c5SJoe Perches } elsif (($type eq "W" || $type eq "Q" || $type eq "B") && 761083bf9c5SJoe Perches $value =~ /^https?:/ && 762083bf9c5SJoe Perches ($self_test eq "" || $self_test =~ /\blinks\b/)) { 763083bf9c5SJoe Perches next if (grep(m@^\Q$value\E$@, @good_links)); 764083bf9c5SJoe Perches my $isbad = 0; 765083bf9c5SJoe Perches if (grep(m@^\Q$value\E$@, @bad_links)) { 766083bf9c5SJoe Perches $isbad = 1; 767083bf9c5SJoe Perches } else { 768083bf9c5SJoe Perches my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`; 769083bf9c5SJoe Perches if ($? == 0) { 770083bf9c5SJoe Perches push(@good_links, $value); 771083bf9c5SJoe Perches } else { 772083bf9c5SJoe Perches push(@bad_links, $value); 773083bf9c5SJoe Perches $isbad = 1; 774083bf9c5SJoe Perches } 775083bf9c5SJoe Perches } 776083bf9c5SJoe Perches if ($isbad) { 777083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n"); 778083bf9c5SJoe Perches } 779083bf9c5SJoe Perches 780083bf9c5SJoe Perches ## SCM reachability 781083bf9c5SJoe Perches } elsif ($type eq "T" && 782083bf9c5SJoe Perches ($self_test eq "" || $self_test =~ /\bscm\b/)) { 783083bf9c5SJoe Perches next if (grep(m@^\Q$value\E$@, @good_links)); 784083bf9c5SJoe Perches my $isbad = 0; 785083bf9c5SJoe Perches if (grep(m@^\Q$value\E$@, @bad_links)) { 786083bf9c5SJoe Perches $isbad = 1; 787083bf9c5SJoe Perches } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) { 788083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n"); 789083bf9c5SJoe Perches } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) { 790083bf9c5SJoe Perches my $url = $1; 791083bf9c5SJoe Perches my $branch = ""; 792083bf9c5SJoe Perches $branch = $3 if $3; 793083bf9c5SJoe Perches my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`; 794083bf9c5SJoe Perches if ($? == 0) { 795083bf9c5SJoe Perches push(@good_links, $value); 796083bf9c5SJoe Perches } else { 797083bf9c5SJoe Perches push(@bad_links, $value); 798083bf9c5SJoe Perches $isbad = 1; 799083bf9c5SJoe Perches } 800083bf9c5SJoe Perches } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) { 801083bf9c5SJoe Perches my $url = $1; 802083bf9c5SJoe Perches my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`; 803083bf9c5SJoe Perches if ($? == 0) { 804083bf9c5SJoe Perches push(@good_links, $value); 805083bf9c5SJoe Perches } else { 806083bf9c5SJoe Perches push(@bad_links, $value); 807083bf9c5SJoe Perches $isbad = 1; 808083bf9c5SJoe Perches } 809083bf9c5SJoe Perches } 810083bf9c5SJoe Perches if ($isbad) { 811083bf9c5SJoe Perches print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n"); 812083bf9c5SJoe Perches } 813e1f75904STom Saeger } 814e1f75904STom Saeger } 815e1f75904STom Saeger} 816e1f75904STom Saeger 817435de078SJoe Perchessub ignore_email_address { 818435de078SJoe Perches my ($address) = @_; 819435de078SJoe Perches 820435de078SJoe Perches foreach my $ignore (@ignore_emails) { 821435de078SJoe Perches return 1 if ($ignore eq $address); 822435de078SJoe Perches } 823435de078SJoe Perches 824435de078SJoe Perches return 0; 825435de078SJoe Perches} 826435de078SJoe Perches 827ab6c937dSJoe Perchessub range_is_maintained { 828ab6c937dSJoe Perches my ($start, $end) = @_; 829ab6c937dSJoe Perches 830ab6c937dSJoe Perches for (my $i = $start; $i < $end; $i++) { 831ab6c937dSJoe Perches my $line = $typevalue[$i]; 832ce8155f7SJoe Perches if ($line =~ m/^([A-Z]):\s*(.*)/) { 833ab6c937dSJoe Perches my $type = $1; 834ab6c937dSJoe Perches my $value = $2; 835ab6c937dSJoe Perches if ($type eq 'S') { 836ab6c937dSJoe Perches if ($value =~ /(maintain|support)/i) { 837ab6c937dSJoe Perches return 1; 838ab6c937dSJoe Perches } 839ab6c937dSJoe Perches } 840ab6c937dSJoe Perches } 841ab6c937dSJoe Perches } 842ab6c937dSJoe Perches return 0; 843ab6c937dSJoe Perches} 844ab6c937dSJoe Perches 845ab6c937dSJoe Perchessub range_has_maintainer { 846ab6c937dSJoe Perches my ($start, $end) = @_; 847ab6c937dSJoe Perches 848ab6c937dSJoe Perches for (my $i = $start; $i < $end; $i++) { 849ab6c937dSJoe Perches my $line = $typevalue[$i]; 850ce8155f7SJoe Perches if ($line =~ m/^([A-Z]):\s*(.*)/) { 851ab6c937dSJoe Perches my $type = $1; 852ab6c937dSJoe Perches my $value = $2; 853ab6c937dSJoe Perches if ($type eq 'M') { 854ab6c937dSJoe Perches return 1; 855ab6c937dSJoe Perches } 856ab6c937dSJoe Perches } 857ab6c937dSJoe Perches } 858ab6c937dSJoe Perches return 0; 859ab6c937dSJoe Perches} 860ab6c937dSJoe Perches 8616ef1c52eSJoe Perchessub get_maintainers { 862683c6f8fSJoe Perches %email_hash_name = (); 863683c6f8fSJoe Perches %email_hash_address = (); 864683c6f8fSJoe Perches %commit_author_hash = (); 865683c6f8fSJoe Perches %commit_signer_hash = (); 866683c6f8fSJoe Perches @email_to = (); 867683c6f8fSJoe Perches %hash_list_to = (); 868683c6f8fSJoe Perches @list_to = (); 869683c6f8fSJoe Perches @scm = (); 870683c6f8fSJoe Perches @web = (); 871033964f1SJani Nikula @bug = (); 872683c6f8fSJoe Perches @subsystem = (); 873683c6f8fSJoe Perches @status = (); 8749ad18c85SVlastimil Babka @substatus = (); 875b9e2331dSJoe Perches %deduplicate_name_hash = (); 876b9e2331dSJoe Perches %deduplicate_address_hash = (); 877683c6f8fSJoe Perches if ($email_git_all_signature_types) { 878683c6f8fSJoe Perches $signature_pattern = "(.+?)[Bb][Yy]:"; 879683c6f8fSJoe Perches } else { 880683c6f8fSJoe Perches $signature_pattern = "\(" . join("|", @signature_tags) . "\)"; 881683c6f8fSJoe Perches } 882cb7301c7SJoe Perches 883cb7301c7SJoe Perches # Find responsible parties 884cb7301c7SJoe Perches 885b9e2331dSJoe Perches my %exact_pattern_match_hash = (); 8866ef1c52eSJoe Perches 887cb7301c7SJoe Perches foreach my $file (@files) { 888cb7301c7SJoe Perches 889272a8979SJoe Perches my %hash; 890272a8979SJoe Perches my $tvi = find_first_section(); 891272a8979SJoe Perches while ($tvi < @typevalue) { 892272a8979SJoe Perches my $start = find_starting_index($tvi); 893272a8979SJoe Perches my $end = find_ending_index($tvi); 894272a8979SJoe Perches my $exclude = 0; 895272a8979SJoe Perches my $i; 896272a8979SJoe Perches 897cb7301c7SJoe Perches #Do not match excluded file patterns 898cb7301c7SJoe Perches 899272a8979SJoe Perches for ($i = $start; $i < $end; $i++) { 900272a8979SJoe Perches my $line = $typevalue[$i]; 901ce8155f7SJoe Perches if ($line =~ m/^([A-Z]):\s*(.*)/) { 902cb7301c7SJoe Perches my $type = $1; 903cb7301c7SJoe Perches my $value = $2; 904cb7301c7SJoe Perches if ($type eq 'X') { 905cb7301c7SJoe Perches if (file_match_pattern($file, $value)) { 906cb7301c7SJoe Perches $exclude = 1; 9073c840c18SJoe Perches last; 908cb7301c7SJoe Perches } 909cb7301c7SJoe Perches } 910cb7301c7SJoe Perches } 911cb7301c7SJoe Perches } 912cb7301c7SJoe Perches 913cb7301c7SJoe Perches if (!$exclude) { 914272a8979SJoe Perches for ($i = $start; $i < $end; $i++) { 915272a8979SJoe Perches my $line = $typevalue[$i]; 916ce8155f7SJoe Perches if ($line =~ m/^([A-Z]):\s*(.*)/) { 917cb7301c7SJoe Perches my $type = $1; 918cb7301c7SJoe Perches my $value = $2; 919cb7301c7SJoe Perches if ($type eq 'F') { 920cb7301c7SJoe Perches if (file_match_pattern($file, $value)) { 9213fb55652SJoe Perches my $value_pd = ($value =~ tr@/@@); 9223fb55652SJoe Perches my $file_pd = ($file =~ tr@/@@); 9233fb55652SJoe Perches $value_pd++ if (substr($value,-1,1) ne "/"); 924e3e9d114SJoe Perches $value_pd = -1 if ($value =~ /^\.\*/); 925ab6c937dSJoe Perches if ($value_pd >= $file_pd && 926ab6c937dSJoe Perches range_is_maintained($start, $end) && 927ab6c937dSJoe Perches range_has_maintainer($start, $end)) { 9286ef1c52eSJoe Perches $exact_pattern_match_hash{$file} = 1; 9296ef1c52eSJoe Perches } 9303fb55652SJoe Perches if ($pattern_depth == 0 || 9313fb55652SJoe Perches (($file_pd - $value_pd) < $pattern_depth)) { 9323fb55652SJoe Perches $hash{$tvi} = $value_pd; 9333fb55652SJoe Perches } 934cb7301c7SJoe Perches } 935bbbe96edSStephen Warren } elsif ($type eq 'N') { 936eb90d085SStephen Warren if ($file =~ m/$value/x) { 937eb90d085SStephen Warren $hash{$tvi} = 0; 938eb90d085SStephen Warren } 939cb7301c7SJoe Perches } 940cb7301c7SJoe Perches } 941cb7301c7SJoe Perches } 942272a8979SJoe Perches } 9433c840c18SJoe Perches $tvi = $end + 1; 944272a8979SJoe Perches } 945272a8979SJoe Perches 9461d606b4eSJoe Perches foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { 94771ca5ee1SJoe Perches add_categories($line, ""); 9484b76c9daSJoe Perches if ($sections) { 9494b76c9daSJoe Perches my $i; 9504b76c9daSJoe Perches my $start = find_starting_index($line); 9514b76c9daSJoe Perches my $end = find_ending_index($line); 9524b76c9daSJoe Perches for ($i = $start; $i < $end; $i++) { 9534b76c9daSJoe Perches my $line = $typevalue[$i]; 9544b76c9daSJoe Perches if ($line =~ /^[FX]:/) { ##Restore file patterns 9554b76c9daSJoe Perches $line =~ s/([^\\])\.([^\*])/$1\?$2/g; 9564b76c9daSJoe Perches $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ? 9574b76c9daSJoe Perches $line =~ s/\\\./\./g; ##Convert \. to . 9584b76c9daSJoe Perches $line =~ s/\.\*/\*/g; ##Convert .* to * 9594b76c9daSJoe Perches } 96003aed214SJoe Perches my $count = $line =~ s/^([A-Z]):/$1:\t/g; 96103aed214SJoe Perches if ($letters eq "" || (!$count || $letters =~ /$1/i)) { 9624b76c9daSJoe Perches print("$line\n"); 9634b76c9daSJoe Perches } 96403aed214SJoe Perches } 965f11e9a15SJoe Perches print("\n"); 9664b76c9daSJoe Perches } 9671d606b4eSJoe Perches } 9680c78c013SJoe Perches 9690c78c013SJoe Perches maintainers_in_file($file); 970cb7301c7SJoe Perches } 971cb7301c7SJoe Perches 972dcf36a92SJoe Perches if ($keywords) { 973dcf36a92SJoe Perches @keyword_tvi = sort_and_uniq(@keyword_tvi); 974dcf36a92SJoe Perches foreach my $line (@keyword_tvi) { 97571ca5ee1SJoe Perches add_categories($line, ":Keyword:$keyword_hash{$line}"); 976dcf36a92SJoe Perches } 977dcf36a92SJoe Perches } 978dcf36a92SJoe Perches 979b9e2331dSJoe Perches foreach my $email (@email_to, @list_to) { 980b9e2331dSJoe Perches $email->[0] = deduplicate_email($email->[0]); 981b9e2331dSJoe Perches } 9826ef1c52eSJoe Perches 9836ef1c52eSJoe Perches foreach my $file (@files) { 9846ef1c52eSJoe Perches if ($email && 9856343f6b7SJoe Perches ($email_git || 9866343f6b7SJoe Perches ($email_git_fallback && 9876343f6b7SJoe Perches $file !~ /MAINTAINERS$/ && 9886ef1c52eSJoe Perches !$exact_pattern_match_hash{$file}))) { 9896ef1c52eSJoe Perches vcs_file_signoffs($file); 9906ef1c52eSJoe Perches } 9916ef1c52eSJoe Perches if ($email && $email_git_blame) { 9926ef1c52eSJoe Perches vcs_file_blame($file); 9936ef1c52eSJoe Perches } 9946ef1c52eSJoe Perches } 9956ef1c52eSJoe Perches 996f5f5078dSJoe Perches if ($email) { 997cb7301c7SJoe Perches foreach my $chief (@penguin_chief) { 998cb7301c7SJoe Perches if ($chief =~ m/^(.*):(.*)/) { 999f5f5078dSJoe Perches my $email_address; 10000e70e83dSJoe Perches 1001a8af2430SJoe Perches $email_address = format_email($1, $2, $email_usename); 1002f5f5078dSJoe Perches if ($email_git_penguin_chiefs) { 10033c7385b8SJoe Perches push(@email_to, [$email_address, 'chief penguin']); 1004f5f5078dSJoe Perches } else { 10053c7385b8SJoe Perches @email_to = grep($_->[0] !~ /${email_address}/, @email_to); 1006cb7301c7SJoe Perches } 1007cb7301c7SJoe Perches } 1008cb7301c7SJoe Perches } 100903372dbbSJoe Perches 101003372dbbSJoe Perches foreach my $email (@file_emails) { 101111fb4896SRob Herring $email = mailmap_email($email); 101203372dbbSJoe Perches my ($name, $address) = parse_email($email); 101303372dbbSJoe Perches 101403372dbbSJoe Perches my $tmp_email = format_email($name, $address, $email_usename); 101503372dbbSJoe Perches push_email_address($tmp_email, ''); 101603372dbbSJoe Perches add_role($tmp_email, 'in file'); 101703372dbbSJoe Perches } 1018cb7301c7SJoe Perches } 1019cb7301c7SJoe Perches 10200ef82fceSDouglas Anderson foreach my $fix (@fixes) { 10210ef82fceSDouglas Anderson vcs_add_commit_signers($fix, "blamed_fixes"); 10220ef82fceSDouglas Anderson } 10230ef82fceSDouglas Anderson 1024290603c1SJoe Perches my @to = (); 1025683c6f8fSJoe Perches if ($email || $email_list) { 1026cb7301c7SJoe Perches if ($email) { 1027290603c1SJoe Perches @to = (@to, @email_to); 1028cb7301c7SJoe Perches } 1029290603c1SJoe Perches if ($email_list) { 1030290603c1SJoe Perches @to = (@to, @list_to); 1031290603c1SJoe Perches } 1032cb7301c7SJoe Perches } 1033cb7301c7SJoe Perches 10346ef1c52eSJoe Perches if ($interactive) { 1035b9e2331dSJoe Perches @to = interactive_get_maintainers(\@to); 10366ef1c52eSJoe Perches } 1037cb7301c7SJoe Perches 1038683c6f8fSJoe Perches return @to; 1039cb7301c7SJoe Perches} 1040cb7301c7SJoe Perches 1041cb7301c7SJoe Perchessub file_match_pattern { 1042cb7301c7SJoe Perches my ($file, $pattern) = @_; 1043cb7301c7SJoe Perches if (substr($pattern, -1) eq "/") { 1044cb7301c7SJoe Perches if ($file =~ m@^$pattern@) { 1045cb7301c7SJoe Perches return 1; 1046cb7301c7SJoe Perches } 1047cb7301c7SJoe Perches } else { 1048cb7301c7SJoe Perches if ($file =~ m@^$pattern@) { 1049cb7301c7SJoe Perches my $s1 = ($file =~ tr@/@@); 1050cb7301c7SJoe Perches my $s2 = ($pattern =~ tr@/@@); 1051cb7301c7SJoe Perches if ($s1 == $s2) { 1052cb7301c7SJoe Perches return 1; 1053cb7301c7SJoe Perches } 1054cb7301c7SJoe Perches } 1055cb7301c7SJoe Perches } 1056cb7301c7SJoe Perches return 0; 1057cb7301c7SJoe Perches} 1058cb7301c7SJoe Perches 1059cb7301c7SJoe Perchessub usage { 1060cb7301c7SJoe Perches print <<EOT; 1061cb7301c7SJoe Perchesusage: $P [options] patchfile 1062870020f9SJoe Perches $P [options] -f file|directory 1063cb7301c7SJoe Perchesversion: $V 1064cb7301c7SJoe Perches 1065cb7301c7SJoe PerchesMAINTAINER field selection options: 1066cb7301c7SJoe Perches --email => print email address(es) if any 1067cb7301c7SJoe Perches --git => include recent git \*-by: signers 1068e4d26b02SJoe Perches --git-all-signature-types => include signers regardless of signature type 1069683c6f8fSJoe Perches or use only ${signature_pattern} signers (default: $email_git_all_signature_types) 1070e3e9d114SJoe Perches --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback) 1071cb7301c7SJoe Perches --git-chief-penguins => include ${penguin_chiefs} 1072e4d26b02SJoe Perches --git-min-signatures => number of signatures required (default: $email_git_min_signatures) 1073e4d26b02SJoe Perches --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers) 1074e4d26b02SJoe Perches --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent) 1075f5492666SJoe Perches --git-blame => use git blame to find modified commits for patch or file 10763cbcca8aSBrian Norris --git-blame-signatures => when used with --git-blame, also include all commit signers 1077e4d26b02SJoe Perches --git-since => git history to use (default: $email_git_since) 1078e4d26b02SJoe Perches --hg-since => hg history to use (default: $email_hg_since) 1079dace8e30SFlorian Mickler --interactive => display a menu (mostly useful if used with the --git option) 1080cb7301c7SJoe Perches --m => include maintainer(s) if any 1081c1c3f2c9SJoe Perches --r => include reviewer(s) if any 1082cb7301c7SJoe Perches --n => include name 'Full Name <addr\@domain.tld>' 1083cb7301c7SJoe Perches --l => include list(s) if any 108449662503SJoe Perches --moderated => include moderated lists(s) if any (default: true) 108549662503SJoe Perches --s => include subscriber only list(s) if any (default: false) 108611ecf53cSJoe Perches --remove-duplicates => minimize duplicate email names/addresses 1087*6ba31721SVlastimil Babka --roles => show roles (role:subsystem, git-signer, list, etc...) 10883c7385b8SJoe Perches --rolestats => show roles and statistics (commits/total_commits, %) 10899ad18c85SVlastimil Babka --substatus => show subsystem status if not Maintained (default: match --roles when output is tty)" 109003372dbbSJoe Perches --file-emails => add email addresses found in -f file (default: 0 (off)) 10912f5bd343SJoe Perches --fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on)) 1092cb7301c7SJoe Perches --scm => print SCM tree(s) if any 1093cb7301c7SJoe Perches --status => print status if any 1094cb7301c7SJoe Perches --subsystem => print subsystem name if any 1095cb7301c7SJoe Perches --web => print website(s) if any 1096033964f1SJani Nikula --bug => print bug reporting info if any 1097cb7301c7SJoe Perches 1098cb7301c7SJoe PerchesOutput type options: 1099cb7301c7SJoe Perches --separator [, ] => separator for multiple entries on 1 line 110042498316SJoe Perches using --separator also sets --nomultiline if --separator is not [, ] 1101cb7301c7SJoe Perches --multiline => print 1 entry per line 1102cb7301c7SJoe Perches 1103cb7301c7SJoe PerchesOther options: 11043fb55652SJoe Perches --pattern-depth => Number of pattern directory traversals (default: 0 (all)) 1105b9e2331dSJoe Perches --keywords => scan patch for keywords (default: $keywords) 110671ca5ee1SJoe Perches --keywords-in-file => scan file for keywords (default: $keywords_in_file) 1107b9e2331dSJoe Perches --sections => print all of the subsystem sections with pattern matches 110803aed214SJoe Perches --letters => print all matching 'letter' types from all matching sections 1109b9e2331dSJoe Perches --mailmap => use .mailmap file (default: $email_use_mailmap) 111031bb82c9SAntonio Nino Diaz --no-tree => run without a kernel tree 1111e1f75904STom Saeger --self-test => show potential issues with MAINTAINERS file content 1112f5f5078dSJoe Perches --version => show version 1113cb7301c7SJoe Perches --help => show this help information 1114cb7301c7SJoe Perches 11153fb55652SJoe PerchesDefault options: 111631bb82c9SAntonio Nino Diaz [--email --tree --nogit --git-fallback --m --r --n --l --multiline 111771ca5ee1SJoe Perches --pattern-depth=0 --remove-duplicates --rolestats --keywords] 11183fb55652SJoe Perches 1119870020f9SJoe PerchesNotes: 1120870020f9SJoe Perches Using "-f directory" may give unexpected results: 1121870020f9SJoe Perches Used with "--git", git signators for _all_ files in and below 1122870020f9SJoe Perches directory are examined as git recurses directories. 1123870020f9SJoe Perches Any specified X: (exclude) pattern matches are _not_ ignored. 1124870020f9SJoe Perches Used with "--nogit", directory is used as a pattern match, 1125870020f9SJoe Perches no individual file within the directory or subdirectory 1126870020f9SJoe Perches is matched. 1127f5492666SJoe Perches Used with "--git-blame", does not iterate all files in directory 1128f5492666SJoe Perches Using "--git-blame" is slow and may add old committers and authors 1129f5492666SJoe Perches that are no longer active maintainers to the output. 11303c7385b8SJoe Perches Using "--roles" or "--rolestats" with git send-email --cc-cmd or any 11313c7385b8SJoe Perches other automated tools that expect only ["name"] <email address> 11323c7385b8SJoe Perches may not work because of additional output after <email address>. 11333c7385b8SJoe Perches Using "--rolestats" and "--git-blame" shows the #/total=% commits, 11343c7385b8SJoe Perches not the percentage of the entire file authored. # of commits is 11353c7385b8SJoe Perches not a good measure of amount of code authored. 1 major commit may 11363c7385b8SJoe Perches contain a thousand lines, 5 trivial commits may modify a single line. 113760db31acSJoe Perches If git is not installed, but mercurial (hg) is installed and an .hg 113860db31acSJoe Perches repository exists, the following options apply to mercurial: 113960db31acSJoe Perches --git, 114060db31acSJoe Perches --git-min-signatures, --git-max-maintainers, --git-min-percent, and 114160db31acSJoe Perches --git-blame 114260db31acSJoe Perches Use --hg-since not --git-since to control date selection 1143368669daSJoe Perches File ".get_maintainer.conf", if it exists in the linux kernel source root 1144368669daSJoe Perches directory, can change whatever get_maintainer defaults are desired. 1145368669daSJoe Perches Entries in this file can be any command line argument. 1146368669daSJoe Perches This file is prepended to any additional command line arguments. 1147368669daSJoe Perches Multiple lines and # comments are allowed. 1148b1312bfeSBrian Norris Most options have both positive and negative forms. 1149b1312bfeSBrian Norris The negative forms for --<foo> are --no<foo> and --no-<foo>. 1150b1312bfeSBrian Norris 1151cb7301c7SJoe PerchesEOT 1152cb7301c7SJoe Perches} 1153cb7301c7SJoe Perches 1154cb7301c7SJoe Perchessub top_of_kernel_tree { 1155cb7301c7SJoe Perches my ($lk_path) = @_; 1156cb7301c7SJoe Perches 1157cb7301c7SJoe Perches if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") { 1158cb7301c7SJoe Perches $lk_path .= "/"; 1159cb7301c7SJoe Perches } 1160cb7301c7SJoe Perches if ( (-f "${lk_path}COPYING") 1161cb7301c7SJoe Perches && (-f "${lk_path}CREDITS") 1162cb7301c7SJoe Perches && (-f "${lk_path}Kbuild") 11636f7d98ecSJoe Perches && (-e "${lk_path}MAINTAINERS") 1164cb7301c7SJoe Perches && (-f "${lk_path}Makefile") 1165cb7301c7SJoe Perches && (-f "${lk_path}README") 1166cb7301c7SJoe Perches && (-d "${lk_path}Documentation") 1167cb7301c7SJoe Perches && (-d "${lk_path}arch") 1168cb7301c7SJoe Perches && (-d "${lk_path}include") 1169cb7301c7SJoe Perches && (-d "${lk_path}drivers") 1170cb7301c7SJoe Perches && (-d "${lk_path}fs") 1171cb7301c7SJoe Perches && (-d "${lk_path}init") 1172cb7301c7SJoe Perches && (-d "${lk_path}ipc") 1173cb7301c7SJoe Perches && (-d "${lk_path}kernel") 1174cb7301c7SJoe Perches && (-d "${lk_path}lib") 1175cb7301c7SJoe Perches && (-d "${lk_path}scripts")) { 1176cb7301c7SJoe Perches return 1; 1177cb7301c7SJoe Perches } 1178cb7301c7SJoe Perches return 0; 1179cb7301c7SJoe Perches} 1180cb7301c7SJoe Perches 11819c334eb9SAlvin Šipragasub escape_name { 11829c334eb9SAlvin Šipraga my ($name) = @_; 11839c334eb9SAlvin Šipraga 11849c334eb9SAlvin Šipraga if ($name =~ /[^\w \-]/ai) { ##has "must quote" chars 11859c334eb9SAlvin Šipraga $name =~ s/(?<!\\)"/\\"/g; ##escape quotes 11869c334eb9SAlvin Šipraga $name = "\"$name\""; 11879c334eb9SAlvin Šipraga } 11889c334eb9SAlvin Šipraga 11899c334eb9SAlvin Šipraga return $name; 11909c334eb9SAlvin Šipraga} 11919c334eb9SAlvin Šipraga 11920e70e83dSJoe Perchessub parse_email { 11930e70e83dSJoe Perches my ($formatted_email) = @_; 11940e70e83dSJoe Perches 11950e70e83dSJoe Perches my $name = ""; 11960e70e83dSJoe Perches my $address = ""; 11970e70e83dSJoe Perches 119811ecf53cSJoe Perches if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) { 11990e70e83dSJoe Perches $name = $1; 12000e70e83dSJoe Perches $address = $2; 120111ecf53cSJoe Perches } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) { 12020e70e83dSJoe Perches $address = $1; 1203b781655aSJoe Perches } elsif ($formatted_email =~ /^(.+\@\S*).*$/) { 12040e70e83dSJoe Perches $address = $1; 12050e70e83dSJoe Perches } 1206cb7301c7SJoe Perches 1207cb7301c7SJoe Perches $name =~ s/^\s+|\s+$//g; 1208d789504aSJoe Perches $name =~ s/^\"|\"$//g; 12099c334eb9SAlvin Šipraga $name = escape_name($name); 12100e70e83dSJoe Perches $address =~ s/^\s+|\s+$//g; 1211cb7301c7SJoe Perches 12120e70e83dSJoe Perches return ($name, $address); 12130e70e83dSJoe Perches} 12140e70e83dSJoe Perches 12150e70e83dSJoe Perchessub format_email { 1216a8af2430SJoe Perches my ($name, $address, $usename) = @_; 12170e70e83dSJoe Perches 12180e70e83dSJoe Perches my $formatted_email; 12190e70e83dSJoe Perches 12200e70e83dSJoe Perches $name =~ s/^\s+|\s+$//g; 12210e70e83dSJoe Perches $name =~ s/^\"|\"$//g; 12229c334eb9SAlvin Šipraga $name = escape_name($name); 12230e70e83dSJoe Perches $address =~ s/^\s+|\s+$//g; 12240e70e83dSJoe Perches 1225a8af2430SJoe Perches if ($usename) { 12260e70e83dSJoe Perches if ("$name" eq "") { 12270e70e83dSJoe Perches $formatted_email = "$address"; 12280e70e83dSJoe Perches } else { 1229a8af2430SJoe Perches $formatted_email = "$name <$address>"; 12300e70e83dSJoe Perches } 12310e70e83dSJoe Perches } else { 12320e70e83dSJoe Perches $formatted_email = $address; 12330e70e83dSJoe Perches } 12340e70e83dSJoe Perches 1235cb7301c7SJoe Perches return $formatted_email; 1236cb7301c7SJoe Perches} 1237cb7301c7SJoe Perches 1238272a8979SJoe Perchessub find_first_section { 1239272a8979SJoe Perches my $index = 0; 1240272a8979SJoe Perches 1241272a8979SJoe Perches while ($index < @typevalue) { 1242272a8979SJoe Perches my $tv = $typevalue[$index]; 1243ce8155f7SJoe Perches if (($tv =~ m/^([A-Z]):\s*(.*)/)) { 1244272a8979SJoe Perches last; 1245272a8979SJoe Perches } 1246272a8979SJoe Perches $index++; 1247272a8979SJoe Perches } 1248272a8979SJoe Perches 1249272a8979SJoe Perches return $index; 1250272a8979SJoe Perches} 1251272a8979SJoe Perches 1252b781655aSJoe Perchessub find_starting_index { 1253b781655aSJoe Perches my ($index) = @_; 1254b781655aSJoe Perches 1255b781655aSJoe Perches while ($index > 0) { 1256b781655aSJoe Perches my $tv = $typevalue[$index]; 1257ce8155f7SJoe Perches if (!($tv =~ m/^([A-Z]):\s*(.*)/)) { 1258b781655aSJoe Perches last; 1259b781655aSJoe Perches } 1260b781655aSJoe Perches $index--; 1261b781655aSJoe Perches } 1262b781655aSJoe Perches 1263b781655aSJoe Perches return $index; 1264b781655aSJoe Perches} 1265b781655aSJoe Perches 1266b781655aSJoe Perchessub find_ending_index { 1267b781655aSJoe Perches my ($index) = @_; 1268b781655aSJoe Perches 1269b781655aSJoe Perches while ($index < @typevalue) { 1270b781655aSJoe Perches my $tv = $typevalue[$index]; 1271ce8155f7SJoe Perches if (!($tv =~ m/^([A-Z]):\s*(.*)/)) { 1272b781655aSJoe Perches last; 1273b781655aSJoe Perches } 1274b781655aSJoe Perches $index++; 1275b781655aSJoe Perches } 1276b781655aSJoe Perches 1277b781655aSJoe Perches return $index; 1278b781655aSJoe Perches} 1279b781655aSJoe Perches 12802a7cb1dcSJoe Perchessub get_subsystem_name { 12812a7cb1dcSJoe Perches my ($index) = @_; 12822a7cb1dcSJoe Perches 12832a7cb1dcSJoe Perches my $start = find_starting_index($index); 12842a7cb1dcSJoe Perches 12852a7cb1dcSJoe Perches my $subsystem = $typevalue[$start]; 12862a7cb1dcSJoe Perches if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) { 12872a7cb1dcSJoe Perches $subsystem = substr($subsystem, 0, $output_section_maxlen - 3); 12882a7cb1dcSJoe Perches $subsystem =~ s/\s*$//; 12892a7cb1dcSJoe Perches $subsystem = $subsystem . "..."; 12902a7cb1dcSJoe Perches } 12912a7cb1dcSJoe Perches return $subsystem; 12922a7cb1dcSJoe Perches} 12932a7cb1dcSJoe Perches 12943c7385b8SJoe Perchessub get_maintainer_role { 12953c7385b8SJoe Perches my ($index) = @_; 12963c7385b8SJoe Perches 12973c7385b8SJoe Perches my $i; 12983c7385b8SJoe Perches my $start = find_starting_index($index); 12993c7385b8SJoe Perches my $end = find_ending_index($index); 13003c7385b8SJoe Perches 1301*6ba31721SVlastimil Babka my $role = "maintainer"; 13022a7cb1dcSJoe Perches my $subsystem = get_subsystem_name($index); 1303*6ba31721SVlastimil Babka my $status = "unknown"; 13043c7385b8SJoe Perches 13053c7385b8SJoe Perches for ($i = $start + 1; $i < $end; $i++) { 13063c7385b8SJoe Perches my $tv = $typevalue[$i]; 1307ce8155f7SJoe Perches if ($tv =~ m/^([A-Z]):\s*(.*)/) { 13083c7385b8SJoe Perches my $ptype = $1; 13093c7385b8SJoe Perches my $pvalue = $2; 13103c7385b8SJoe Perches if ($ptype eq "S") { 1311*6ba31721SVlastimil Babka $status = $pvalue; 13123c7385b8SJoe Perches } 13133c7385b8SJoe Perches } 13143c7385b8SJoe Perches } 13153c7385b8SJoe Perches 1316*6ba31721SVlastimil Babka $status = lc($status); 1317*6ba31721SVlastimil Babka if ($status eq "buried alive in reporters") { 13183c7385b8SJoe Perches $role = "chief penguin"; 13193c7385b8SJoe Perches } 13203c7385b8SJoe Perches 13213c7385b8SJoe Perches return $role . ":" . $subsystem; 13223c7385b8SJoe Perches} 13233c7385b8SJoe Perches 13243c7385b8SJoe Perchessub get_list_role { 13253c7385b8SJoe Perches my ($index) = @_; 13263c7385b8SJoe Perches 13272a7cb1dcSJoe Perches my $subsystem = get_subsystem_name($index); 13283c7385b8SJoe Perches 13293c7385b8SJoe Perches if ($subsystem eq "THE REST") { 13303c7385b8SJoe Perches $subsystem = ""; 13313c7385b8SJoe Perches } 13323c7385b8SJoe Perches 13333c7385b8SJoe Perches return $subsystem; 13343c7385b8SJoe Perches} 13353c7385b8SJoe Perches 1336cb7301c7SJoe Perchessub add_categories { 133771ca5ee1SJoe Perches my ($index, $suffix) = @_; 1338cb7301c7SJoe Perches 1339b781655aSJoe Perches my $i; 1340b781655aSJoe Perches my $start = find_starting_index($index); 1341b781655aSJoe Perches my $end = find_ending_index($index); 1342b781655aSJoe Perches 13439ad18c85SVlastimil Babka my $subsystem = $typevalue[$start]; 13449ad18c85SVlastimil Babka push(@subsystem, $subsystem); 13459ad18c85SVlastimil Babka my $status = "Unknown"; 1346b781655aSJoe Perches 1347b781655aSJoe Perches for ($i = $start + 1; $i < $end; $i++) { 1348b781655aSJoe Perches my $tv = $typevalue[$i]; 1349ce8155f7SJoe Perches if ($tv =~ m/^([A-Z]):\s*(.*)/) { 1350cb7301c7SJoe Perches my $ptype = $1; 1351cb7301c7SJoe Perches my $pvalue = $2; 1352cb7301c7SJoe Perches if ($ptype eq "L") { 1353290603c1SJoe Perches my $list_address = $pvalue; 1354290603c1SJoe Perches my $list_additional = ""; 13553c7385b8SJoe Perches my $list_role = get_list_role($i); 13563c7385b8SJoe Perches 13573c7385b8SJoe Perches if ($list_role ne "") { 13583c7385b8SJoe Perches $list_role = ":" . $list_role; 13593c7385b8SJoe Perches } 1360290603c1SJoe Perches if ($list_address =~ m/([^\s]+)\s+(.*)$/) { 1361290603c1SJoe Perches $list_address = $1; 1362290603c1SJoe Perches $list_additional = $2; 1363290603c1SJoe Perches } 1364bdf7c685SJoe Perches if ($list_additional =~ m/subscribers-only/) { 1365cb7301c7SJoe Perches if ($email_subscriber_list) { 13666ef1c52eSJoe Perches if (!$hash_list_to{lc($list_address)}) { 13676ef1c52eSJoe Perches $hash_list_to{lc($list_address)} = 1; 1368683c6f8fSJoe Perches push(@list_to, [$list_address, 136971ca5ee1SJoe Perches "subscriber list${list_role}" . $suffix]); 1370683c6f8fSJoe Perches } 1371cb7301c7SJoe Perches } 1372cb7301c7SJoe Perches } else { 1373cb7301c7SJoe Perches if ($email_list) { 13746ef1c52eSJoe Perches if (!$hash_list_to{lc($list_address)}) { 1375728f5a94SRichard Weinberger if ($list_additional =~ m/moderated/) { 137649662503SJoe Perches if ($email_moderated_list) { 137749662503SJoe Perches $hash_list_to{lc($list_address)} = 1; 1378728f5a94SRichard Weinberger push(@list_to, [$list_address, 137971ca5ee1SJoe Perches "moderated list${list_role}" . $suffix]); 138049662503SJoe Perches } 1381728f5a94SRichard Weinberger } else { 138249662503SJoe Perches $hash_list_to{lc($list_address)} = 1; 1383683c6f8fSJoe Perches push(@list_to, [$list_address, 138471ca5ee1SJoe Perches "open list${list_role}" . $suffix]); 1385683c6f8fSJoe Perches } 1386cb7301c7SJoe Perches } 1387cb7301c7SJoe Perches } 1388728f5a94SRichard Weinberger } 1389cb7301c7SJoe Perches } elsif ($ptype eq "M") { 13900e70e83dSJoe Perches if ($email_maintainer) { 13913c7385b8SJoe Perches my $role = get_maintainer_role($i); 139271ca5ee1SJoe Perches push_email_addresses($pvalue, $role . $suffix); 1393cb7301c7SJoe Perches } 1394c1c3f2c9SJoe Perches } elsif ($ptype eq "R") { 1395c1c3f2c9SJoe Perches if ($email_reviewer) { 13969ad18c85SVlastimil Babka my $subs = get_subsystem_name($i); 13979ad18c85SVlastimil Babka push_email_addresses($pvalue, "reviewer:$subs" . $suffix); 1398c1c3f2c9SJoe Perches } 1399cb7301c7SJoe Perches } elsif ($ptype eq "T") { 140071ca5ee1SJoe Perches push(@scm, $pvalue . $suffix); 1401cb7301c7SJoe Perches } elsif ($ptype eq "W") { 140271ca5ee1SJoe Perches push(@web, $pvalue . $suffix); 1403033964f1SJani Nikula } elsif ($ptype eq "B") { 1404033964f1SJani Nikula push(@bug, $pvalue . $suffix); 1405cb7301c7SJoe Perches } elsif ($ptype eq "S") { 140671ca5ee1SJoe Perches push(@status, $pvalue . $suffix); 14079ad18c85SVlastimil Babka $status = $pvalue; 1408cb7301c7SJoe Perches } 1409cb7301c7SJoe Perches } 1410cb7301c7SJoe Perches } 14119ad18c85SVlastimil Babka 14129ad18c85SVlastimil Babka if ($subsystem ne "THE REST" and $status ne "Maintained") { 14139ad18c85SVlastimil Babka push(@substatus, $subsystem . " status: " . $status . $suffix) 14149ad18c85SVlastimil Babka } 1415cb7301c7SJoe Perches} 1416cb7301c7SJoe Perches 141711ecf53cSJoe Perchessub email_inuse { 141811ecf53cSJoe Perches my ($name, $address) = @_; 14190e70e83dSJoe Perches 142011ecf53cSJoe Perches return 1 if (($name eq "") && ($address eq "")); 14216ef1c52eSJoe Perches return 1 if (($name ne "") && exists($email_hash_name{lc($name)})); 14226ef1c52eSJoe Perches return 1 if (($address ne "") && exists($email_hash_address{lc($address)})); 142311ecf53cSJoe Perches 14240e70e83dSJoe Perches return 0; 14250e70e83dSJoe Perches} 14260e70e83dSJoe Perches 14271b5e1cf6SJoe Perchessub push_email_address { 14283c7385b8SJoe Perches my ($line, $role) = @_; 14291b5e1cf6SJoe Perches 14300e70e83dSJoe Perches my ($name, $address) = parse_email($line); 1431f5492666SJoe Perches 1432b781655aSJoe Perches if ($address eq "") { 1433b781655aSJoe Perches return 0; 1434b781655aSJoe Perches } 1435b781655aSJoe Perches 143611ecf53cSJoe Perches if (!$email_remove_duplicates) { 1437a8af2430SJoe Perches push(@email_to, [format_email($name, $address, $email_usename), $role]); 143811ecf53cSJoe Perches } elsif (!email_inuse($name, $address)) { 1439a8af2430SJoe Perches push(@email_to, [format_email($name, $address, $email_usename), $role]); 1440fae99206SJoe Perches $email_hash_name{lc($name)}++ if ($name ne ""); 14416ef1c52eSJoe Perches $email_hash_address{lc($address)}++; 14421b5e1cf6SJoe Perches } 1443b781655aSJoe Perches 1444b781655aSJoe Perches return 1; 14450a79c492SJoe Perches} 14461b5e1cf6SJoe Perches 14471b5e1cf6SJoe Perchessub push_email_addresses { 14483c7385b8SJoe Perches my ($address, $role) = @_; 14491b5e1cf6SJoe Perches 14501b5e1cf6SJoe Perches my @address_list = (); 14511b5e1cf6SJoe Perches 14525f2441e9SJoe Perches if (rfc822_valid($address)) { 14533c7385b8SJoe Perches push_email_address($address, $role); 14545f2441e9SJoe Perches } elsif (@address_list = rfc822_validlist($address)) { 14551b5e1cf6SJoe Perches my $array_count = shift(@address_list); 14561b5e1cf6SJoe Perches while (my $entry = shift(@address_list)) { 14573c7385b8SJoe Perches push_email_address($entry, $role); 14581b5e1cf6SJoe Perches } 14595f2441e9SJoe Perches } else { 14603c7385b8SJoe Perches if (!push_email_address($address, $role)) { 14615f2441e9SJoe Perches warn("Invalid MAINTAINERS address: '" . $address . "'\n"); 14621b5e1cf6SJoe Perches } 14631b5e1cf6SJoe Perches } 1464b781655aSJoe Perches} 14651b5e1cf6SJoe Perches 14663c7385b8SJoe Perchessub add_role { 14673c7385b8SJoe Perches my ($line, $role) = @_; 14683c7385b8SJoe Perches 14693c7385b8SJoe Perches my ($name, $address) = parse_email($line); 1470a8af2430SJoe Perches my $email = format_email($name, $address, $email_usename); 14713c7385b8SJoe Perches 14723c7385b8SJoe Perches foreach my $entry (@email_to) { 14733c7385b8SJoe Perches if ($email_remove_duplicates) { 14743c7385b8SJoe Perches my ($entry_name, $entry_address) = parse_email($entry->[0]); 147503372dbbSJoe Perches if (($name eq $entry_name || $address eq $entry_address) 147603372dbbSJoe Perches && ($role eq "" || !($entry->[1] =~ m/$role/)) 147703372dbbSJoe Perches ) { 14783c7385b8SJoe Perches if ($entry->[1] eq "") { 14793c7385b8SJoe Perches $entry->[1] = "$role"; 14803c7385b8SJoe Perches } else { 14813c7385b8SJoe Perches $entry->[1] = "$entry->[1],$role"; 14823c7385b8SJoe Perches } 14833c7385b8SJoe Perches } 14843c7385b8SJoe Perches } else { 148503372dbbSJoe Perches if ($email eq $entry->[0] 148603372dbbSJoe Perches && ($role eq "" || !($entry->[1] =~ m/$role/)) 148703372dbbSJoe Perches ) { 14883c7385b8SJoe Perches if ($entry->[1] eq "") { 14893c7385b8SJoe Perches $entry->[1] = "$role"; 14903c7385b8SJoe Perches } else { 14913c7385b8SJoe Perches $entry->[1] = "$entry->[1],$role"; 14923c7385b8SJoe Perches } 14933c7385b8SJoe Perches } 14943c7385b8SJoe Perches } 14953c7385b8SJoe Perches } 14963c7385b8SJoe Perches} 14973c7385b8SJoe Perches 1498cb7301c7SJoe Perchessub which { 1499cb7301c7SJoe Perches my ($bin) = @_; 1500cb7301c7SJoe Perches 1501f5f5078dSJoe Perches foreach my $path (split(/:/, $ENV{PATH})) { 1502cb7301c7SJoe Perches if (-e "$path/$bin") { 1503cb7301c7SJoe Perches return "$path/$bin"; 1504cb7301c7SJoe Perches } 1505cb7301c7SJoe Perches } 1506cb7301c7SJoe Perches 1507cb7301c7SJoe Perches return ""; 1508cb7301c7SJoe Perches} 1509cb7301c7SJoe Perches 1510bcde44edSJoe Perchessub which_conf { 1511bcde44edSJoe Perches my ($conf) = @_; 1512bcde44edSJoe Perches 1513bcde44edSJoe Perches foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { 1514bcde44edSJoe Perches if (-e "$path/$conf") { 1515bcde44edSJoe Perches return "$path/$conf"; 1516bcde44edSJoe Perches } 1517bcde44edSJoe Perches } 1518bcde44edSJoe Perches 1519bcde44edSJoe Perches return ""; 1520bcde44edSJoe Perches} 1521bcde44edSJoe Perches 15227fa8ff2eSFlorian Micklersub mailmap_email { 1523b9e2331dSJoe Perches my ($line) = @_; 15248cbb3a77SJoe Perches 15258cbb3a77SJoe Perches my ($name, $address) = parse_email($line); 15267fa8ff2eSFlorian Mickler my $email = format_email($name, $address, 1); 15277fa8ff2eSFlorian Mickler my $real_name = $name; 15287fa8ff2eSFlorian Mickler my $real_address = $address; 15297fa8ff2eSFlorian Mickler 153047abc722SJoe Perches if (exists $mailmap->{names}->{$email} || 153147abc722SJoe Perches exists $mailmap->{addresses}->{$email}) { 15327fa8ff2eSFlorian Mickler if (exists $mailmap->{names}->{$email}) { 15337fa8ff2eSFlorian Mickler $real_name = $mailmap->{names}->{$email}; 15348cbb3a77SJoe Perches } 15357fa8ff2eSFlorian Mickler if (exists $mailmap->{addresses}->{$email}) { 15367fa8ff2eSFlorian Mickler $real_address = $mailmap->{addresses}->{$email}; 15377fa8ff2eSFlorian Mickler } 15387fa8ff2eSFlorian Mickler } else { 15397fa8ff2eSFlorian Mickler if (exists $mailmap->{names}->{$address}) { 15407fa8ff2eSFlorian Mickler $real_name = $mailmap->{names}->{$address}; 15417fa8ff2eSFlorian Mickler } 15427fa8ff2eSFlorian Mickler if (exists $mailmap->{addresses}->{$address}) { 15437fa8ff2eSFlorian Mickler $real_address = $mailmap->{addresses}->{$address}; 15448cbb3a77SJoe Perches } 15458cbb3a77SJoe Perches } 15467fa8ff2eSFlorian Mickler return format_email($real_name, $real_address, 1); 15477fa8ff2eSFlorian Mickler} 15487fa8ff2eSFlorian Mickler 15497fa8ff2eSFlorian Micklersub mailmap { 15507fa8ff2eSFlorian Mickler my (@addresses) = @_; 15517fa8ff2eSFlorian Mickler 1552b9e2331dSJoe Perches my @mapped_emails = (); 15537fa8ff2eSFlorian Mickler foreach my $line (@addresses) { 1554b9e2331dSJoe Perches push(@mapped_emails, mailmap_email($line)); 15557fa8ff2eSFlorian Mickler } 1556b9e2331dSJoe Perches merge_by_realname(@mapped_emails) if ($email_use_mailmap); 1557b9e2331dSJoe Perches return @mapped_emails; 15587fa8ff2eSFlorian Mickler} 15597fa8ff2eSFlorian Mickler 15607fa8ff2eSFlorian Micklersub merge_by_realname { 15617fa8ff2eSFlorian Mickler my %address_map; 15627fa8ff2eSFlorian Mickler my (@emails) = @_; 1563b9e2331dSJoe Perches 15647fa8ff2eSFlorian Mickler foreach my $email (@emails) { 15657fa8ff2eSFlorian Mickler my ($name, $address) = parse_email($email); 1566b9e2331dSJoe Perches if (exists $address_map{$name}) { 15677fa8ff2eSFlorian Mickler $address = $address_map{$name}; 15687fa8ff2eSFlorian Mickler $email = format_email($name, $address, 1); 1569b9e2331dSJoe Perches } else { 1570b9e2331dSJoe Perches $address_map{$name} = $address; 15718cbb3a77SJoe Perches } 15728cbb3a77SJoe Perches } 15738cbb3a77SJoe Perches} 15748cbb3a77SJoe Perches 157560db31acSJoe Perchessub git_execute_cmd { 1576a8af2430SJoe Perches my ($cmd) = @_; 157760db31acSJoe Perches my @lines = (); 1578a8af2430SJoe Perches 157960db31acSJoe Perches my $output = `$cmd`; 158060db31acSJoe Perches $output =~ s/^\s*//gm; 158160db31acSJoe Perches @lines = split("\n", $output); 158260db31acSJoe Perches 158360db31acSJoe Perches return @lines; 158460db31acSJoe Perches} 158560db31acSJoe Perches 158660db31acSJoe Perchessub hg_execute_cmd { 158760db31acSJoe Perches my ($cmd) = @_; 158860db31acSJoe Perches my @lines = (); 158960db31acSJoe Perches 159060db31acSJoe Perches my $output = `$cmd`; 159160db31acSJoe Perches @lines = split("\n", $output); 159260db31acSJoe Perches 159360db31acSJoe Perches return @lines; 159460db31acSJoe Perches} 159560db31acSJoe Perches 1596683c6f8fSJoe Perchessub extract_formatted_signatures { 1597683c6f8fSJoe Perches my (@signature_lines) = @_; 1598683c6f8fSJoe Perches 1599683c6f8fSJoe Perches my @type = @signature_lines; 1600683c6f8fSJoe Perches 1601683c6f8fSJoe Perches s/\s*(.*):.*/$1/ for (@type); 1602683c6f8fSJoe Perches 1603683c6f8fSJoe Perches # cut -f2- -d":" 1604683c6f8fSJoe Perches s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines); 1605683c6f8fSJoe Perches 1606683c6f8fSJoe Perches## Reformat email addresses (with names) to avoid badly written signatures 1607683c6f8fSJoe Perches 1608683c6f8fSJoe Perches foreach my $signer (@signature_lines) { 1609b9e2331dSJoe Perches $signer = deduplicate_email($signer); 1610683c6f8fSJoe Perches } 1611683c6f8fSJoe Perches 1612683c6f8fSJoe Perches return (\@type, \@signature_lines); 1613683c6f8fSJoe Perches} 1614683c6f8fSJoe Perches 161560db31acSJoe Perchessub vcs_find_signers { 1616c9ecefeaSJoe Perches my ($cmd, $file) = @_; 1617a8af2430SJoe Perches my $commits; 1618683c6f8fSJoe Perches my @lines = (); 1619683c6f8fSJoe Perches my @signatures = (); 1620c9ecefeaSJoe Perches my @authors = (); 1621c9ecefeaSJoe Perches my @stats = (); 1622a8af2430SJoe Perches 162360db31acSJoe Perches @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 1624cb7301c7SJoe Perches 162560db31acSJoe Perches my $pattern = $VCS_cmds{"commit_pattern"}; 1626c9ecefeaSJoe Perches my $author_pattern = $VCS_cmds{"author_pattern"}; 1627c9ecefeaSJoe Perches my $stat_pattern = $VCS_cmds{"stat_pattern"}; 1628c9ecefeaSJoe Perches 1629c9ecefeaSJoe Perches $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern 1630cb7301c7SJoe Perches 163160db31acSJoe Perches $commits = grep(/$pattern/, @lines); # of commits 1632afa81ee1SJoe Perches 1633c9ecefeaSJoe Perches @authors = grep(/$author_pattern/, @lines); 1634683c6f8fSJoe Perches @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines); 1635c9ecefeaSJoe Perches @stats = grep(/$stat_pattern/, @lines); 1636683c6f8fSJoe Perches 1637c9ecefeaSJoe Perches# print("stats: <@stats>\n"); 1638c9ecefeaSJoe Perches 1639c9ecefeaSJoe Perches return (0, \@signatures, \@authors, \@stats) if !@signatures; 1640683c6f8fSJoe Perches 1641683c6f8fSJoe Perches save_commits_by_author(@lines) if ($interactive); 1642683c6f8fSJoe Perches save_commits_by_signer(@lines) if ($interactive); 1643683c6f8fSJoe Perches 16440e70e83dSJoe Perches if (!$email_git_penguin_chiefs) { 1645683c6f8fSJoe Perches @signatures = grep(!/${penguin_chiefs}/i, @signatures); 1646afa81ee1SJoe Perches } 164763ab52dbSJoe Perches 1648c9ecefeaSJoe Perches my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors); 1649683c6f8fSJoe Perches my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); 165063ab52dbSJoe Perches 1651c9ecefeaSJoe Perches return ($commits, $signers_ref, $authors_ref, \@stats); 1652a8af2430SJoe Perches} 1653a8af2430SJoe Perches 165463ab52dbSJoe Perchessub vcs_find_author { 165563ab52dbSJoe Perches my ($cmd) = @_; 165663ab52dbSJoe Perches my @lines = (); 165763ab52dbSJoe Perches 165863ab52dbSJoe Perches @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 165963ab52dbSJoe Perches 166063ab52dbSJoe Perches if (!$email_git_penguin_chiefs) { 166163ab52dbSJoe Perches @lines = grep(!/${penguin_chiefs}/i, @lines); 166263ab52dbSJoe Perches } 166363ab52dbSJoe Perches 166463ab52dbSJoe Perches return @lines if !@lines; 166563ab52dbSJoe Perches 1666683c6f8fSJoe Perches my @authors = (); 166763ab52dbSJoe Perches foreach my $line (@lines) { 1668683c6f8fSJoe Perches if ($line =~ m/$VCS_cmds{"author_pattern"}/) { 1669683c6f8fSJoe Perches my $author = $1; 1670683c6f8fSJoe Perches my ($name, $address) = parse_email($author); 1671683c6f8fSJoe Perches $author = format_email($name, $address, 1); 1672683c6f8fSJoe Perches push(@authors, $author); 1673683c6f8fSJoe Perches } 167463ab52dbSJoe Perches } 167563ab52dbSJoe Perches 1676683c6f8fSJoe Perches save_commits_by_author(@lines) if ($interactive); 1677683c6f8fSJoe Perches save_commits_by_signer(@lines) if ($interactive); 1678683c6f8fSJoe Perches 1679683c6f8fSJoe Perches return @authors; 168063ab52dbSJoe Perches} 168163ab52dbSJoe Perches 168260db31acSJoe Perchessub vcs_save_commits { 168360db31acSJoe Perches my ($cmd) = @_; 168460db31acSJoe Perches my @lines = (); 168560db31acSJoe Perches my @commits = (); 168660db31acSJoe Perches 168760db31acSJoe Perches @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 168860db31acSJoe Perches 168960db31acSJoe Perches foreach my $line (@lines) { 169060db31acSJoe Perches if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) { 169160db31acSJoe Perches push(@commits, $1); 169260db31acSJoe Perches } 169360db31acSJoe Perches } 169460db31acSJoe Perches 169560db31acSJoe Perches return @commits; 169660db31acSJoe Perches} 169760db31acSJoe Perches 169860db31acSJoe Perchessub vcs_blame { 169960db31acSJoe Perches my ($file) = @_; 170060db31acSJoe Perches my $cmd; 170160db31acSJoe Perches my @commits = (); 170260db31acSJoe Perches 170360db31acSJoe Perches return @commits if (!(-f $file)); 170460db31acSJoe Perches 170560db31acSJoe Perches if (@range && $VCS_cmds{"blame_range_cmd"} eq "") { 170660db31acSJoe Perches my @all_commits = (); 170760db31acSJoe Perches 170860db31acSJoe Perches $cmd = $VCS_cmds{"blame_file_cmd"}; 170960db31acSJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 171060db31acSJoe Perches @all_commits = vcs_save_commits($cmd); 171160db31acSJoe Perches 171260db31acSJoe Perches foreach my $file_range_diff (@range) { 171360db31acSJoe Perches next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); 171460db31acSJoe Perches my $diff_file = $1; 171560db31acSJoe Perches my $diff_start = $2; 171660db31acSJoe Perches my $diff_length = $3; 171760db31acSJoe Perches next if ("$file" ne "$diff_file"); 171860db31acSJoe Perches for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) { 171960db31acSJoe Perches push(@commits, $all_commits[$i]); 172060db31acSJoe Perches } 172160db31acSJoe Perches } 172260db31acSJoe Perches } elsif (@range) { 172360db31acSJoe Perches foreach my $file_range_diff (@range) { 172460db31acSJoe Perches next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); 172560db31acSJoe Perches my $diff_file = $1; 172660db31acSJoe Perches my $diff_start = $2; 172760db31acSJoe Perches my $diff_length = $3; 172860db31acSJoe Perches next if ("$file" ne "$diff_file"); 172960db31acSJoe Perches $cmd = $VCS_cmds{"blame_range_cmd"}; 173060db31acSJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 173160db31acSJoe Perches push(@commits, vcs_save_commits($cmd)); 173260db31acSJoe Perches } 173360db31acSJoe Perches } else { 173460db31acSJoe Perches $cmd = $VCS_cmds{"blame_file_cmd"}; 173560db31acSJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 173660db31acSJoe Perches @commits = vcs_save_commits($cmd); 173760db31acSJoe Perches } 173860db31acSJoe Perches 173963ab52dbSJoe Perches foreach my $commit (@commits) { 174063ab52dbSJoe Perches $commit =~ s/^\^//g; 174163ab52dbSJoe Perches } 174263ab52dbSJoe Perches 174360db31acSJoe Perches return @commits; 174460db31acSJoe Perches} 174560db31acSJoe Perches 174660db31acSJoe Perchesmy $printed_novcs = 0; 174760db31acSJoe Perchessub vcs_exists { 174860db31acSJoe Perches %VCS_cmds = %VCS_cmds_git; 174960db31acSJoe Perches return 1 if eval $VCS_cmds{"available"}; 175060db31acSJoe Perches %VCS_cmds = %VCS_cmds_hg; 1751683c6f8fSJoe Perches return 2 if eval $VCS_cmds{"available"}; 175260db31acSJoe Perches %VCS_cmds = (); 175326d98e9fSRandy Dunlap if (!$printed_novcs && $email_git) { 175460db31acSJoe Perches warn("$P: No supported VCS found. Add --nogit to options?\n"); 175560db31acSJoe Perches warn("Using a git repository produces better results.\n"); 175660db31acSJoe Perches warn("Try Linus Torvalds' latest git repository using:\n"); 17573d1c2f72SRalf Thielow warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n"); 175860db31acSJoe Perches $printed_novcs = 1; 175960db31acSJoe Perches } 176060db31acSJoe Perches return 0; 176160db31acSJoe Perches} 176260db31acSJoe Perches 1763683c6f8fSJoe Perchessub vcs_is_git { 1764b9e2331dSJoe Perches vcs_exists(); 1765683c6f8fSJoe Perches return $vcs_used == 1; 1766683c6f8fSJoe Perches} 1767683c6f8fSJoe Perches 1768683c6f8fSJoe Perchessub vcs_is_hg { 1769683c6f8fSJoe Perches return $vcs_used == 2; 1770683c6f8fSJoe Perches} 1771683c6f8fSJoe Perches 17722f5bd343SJoe Perchessub vcs_add_commit_signers { 17732f5bd343SJoe Perches return if (!vcs_exists()); 17742f5bd343SJoe Perches 17752f5bd343SJoe Perches my ($commit, $desc) = @_; 17762f5bd343SJoe Perches my $commit_count = 0; 17772f5bd343SJoe Perches my $commit_authors_ref; 17782f5bd343SJoe Perches my $commit_signers_ref; 17792f5bd343SJoe Perches my $stats_ref; 17802f5bd343SJoe Perches my @commit_authors = (); 17812f5bd343SJoe Perches my @commit_signers = (); 17822f5bd343SJoe Perches my $cmd; 17832f5bd343SJoe Perches 17842f5bd343SJoe Perches $cmd = $VCS_cmds{"find_commit_signers_cmd"}; 17852f5bd343SJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd 17862f5bd343SJoe Perches 17872f5bd343SJoe Perches ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, ""); 17882f5bd343SJoe Perches @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; 17892f5bd343SJoe Perches @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; 17902f5bd343SJoe Perches 17912f5bd343SJoe Perches foreach my $signer (@commit_signers) { 17922f5bd343SJoe Perches $signer = deduplicate_email($signer); 17932f5bd343SJoe Perches } 17942f5bd343SJoe Perches 17952f5bd343SJoe Perches vcs_assign($desc, 1, @commit_signers); 17962f5bd343SJoe Perches} 17972f5bd343SJoe Perches 17986ef1c52eSJoe Perchessub interactive_get_maintainers { 1799683c6f8fSJoe Perches my ($list_ref) = @_; 1800dace8e30SFlorian Mickler my @list = @$list_ref; 1801dace8e30SFlorian Mickler 1802683c6f8fSJoe Perches vcs_exists(); 1803dace8e30SFlorian Mickler 1804dace8e30SFlorian Mickler my %selected; 1805683c6f8fSJoe Perches my %authored; 1806683c6f8fSJoe Perches my %signed; 1807dace8e30SFlorian Mickler my $count = 0; 18086ef1c52eSJoe Perches my $maintained = 0; 1809dace8e30SFlorian Mickler foreach my $entry (@list) { 1810b9e2331dSJoe Perches $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i); 1811b9e2331dSJoe Perches $selected{$count} = 1; 1812683c6f8fSJoe Perches $authored{$count} = 0; 1813683c6f8fSJoe Perches $signed{$count} = 0; 1814dace8e30SFlorian Mickler $count++; 1815dace8e30SFlorian Mickler } 1816dace8e30SFlorian Mickler 1817dace8e30SFlorian Mickler #menu loop 1818683c6f8fSJoe Perches my $done = 0; 1819683c6f8fSJoe Perches my $print_options = 0; 1820683c6f8fSJoe Perches my $redraw = 1; 1821683c6f8fSJoe Perches while (!$done) { 1822683c6f8fSJoe Perches $count = 0; 1823683c6f8fSJoe Perches if ($redraw) { 18246ef1c52eSJoe Perches printf STDERR "\n%1s %2s %-65s", 1825683c6f8fSJoe Perches "*", "#", "email/list and role:stats"; 18266ef1c52eSJoe Perches if ($email_git || 18276ef1c52eSJoe Perches ($email_git_fallback && !$maintained) || 18286ef1c52eSJoe Perches $email_git_blame) { 18296ef1c52eSJoe Perches print STDERR "auth sign"; 18306ef1c52eSJoe Perches } 18316ef1c52eSJoe Perches print STDERR "\n"; 1832dace8e30SFlorian Mickler foreach my $entry (@list) { 1833dace8e30SFlorian Mickler my $email = $entry->[0]; 1834dace8e30SFlorian Mickler my $role = $entry->[1]; 1835683c6f8fSJoe Perches my $sel = ""; 1836683c6f8fSJoe Perches $sel = "*" if ($selected{$count}); 1837683c6f8fSJoe Perches my $commit_author = $commit_author_hash{$email}; 1838683c6f8fSJoe Perches my $commit_signer = $commit_signer_hash{$email}; 1839683c6f8fSJoe Perches my $authored = 0; 1840683c6f8fSJoe Perches my $signed = 0; 1841683c6f8fSJoe Perches $authored++ for (@{$commit_author}); 1842683c6f8fSJoe Perches $signed++ for (@{$commit_signer}); 1843683c6f8fSJoe Perches printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email; 1844683c6f8fSJoe Perches printf STDERR "%4d %4d", $authored, $signed 1845683c6f8fSJoe Perches if ($authored > 0 || $signed > 0); 1846683c6f8fSJoe Perches printf STDERR "\n %s\n", $role; 1847683c6f8fSJoe Perches if ($authored{$count}) { 1848683c6f8fSJoe Perches my $commit_author = $commit_author_hash{$email}; 1849683c6f8fSJoe Perches foreach my $ref (@{$commit_author}) { 1850683c6f8fSJoe Perches print STDERR " Author: @{$ref}[1]\n"; 1851dace8e30SFlorian Mickler } 1852dace8e30SFlorian Mickler } 1853683c6f8fSJoe Perches if ($signed{$count}) { 1854683c6f8fSJoe Perches my $commit_signer = $commit_signer_hash{$email}; 1855683c6f8fSJoe Perches foreach my $ref (@{$commit_signer}) { 1856683c6f8fSJoe Perches print STDERR " @{$ref}[2]: @{$ref}[1]\n"; 1857683c6f8fSJoe Perches } 1858683c6f8fSJoe Perches } 1859683c6f8fSJoe Perches 1860dace8e30SFlorian Mickler $count++; 1861dace8e30SFlorian Mickler } 1862683c6f8fSJoe Perches } 1863683c6f8fSJoe Perches my $date_ref = \$email_git_since; 1864683c6f8fSJoe Perches $date_ref = \$email_hg_since if (vcs_is_hg()); 1865683c6f8fSJoe Perches if ($print_options) { 1866683c6f8fSJoe Perches $print_options = 0; 1867683c6f8fSJoe Perches if (vcs_exists()) { 1868b9e2331dSJoe Perches print STDERR <<EOT 1869b9e2331dSJoe Perches 1870b9e2331dSJoe PerchesVersion Control options: 1871b9e2331dSJoe Perchesg use git history [$email_git] 1872b9e2331dSJoe Perchesgf use git-fallback [$email_git_fallback] 1873b9e2331dSJoe Perchesb use git blame [$email_git_blame] 1874b9e2331dSJoe Perchesbs use blame signatures [$email_git_blame_signatures] 1875b9e2331dSJoe Perchesc# minimum commits [$email_git_min_signatures] 1876b9e2331dSJoe Perches%# min percent [$email_git_min_percent] 1877b9e2331dSJoe Perchesd# history to use [$$date_ref] 1878b9e2331dSJoe Perchesx# max maintainers [$email_git_max_maintainers] 1879b9e2331dSJoe Perchest all signature types [$email_git_all_signature_types] 1880b9e2331dSJoe Perchesm use .mailmap [$email_use_mailmap] 1881b9e2331dSJoe PerchesEOT 1882683c6f8fSJoe Perches } 1883b9e2331dSJoe Perches print STDERR <<EOT 1884b9e2331dSJoe Perches 1885b9e2331dSJoe PerchesAdditional options: 1886b9e2331dSJoe Perches0 toggle all 1887b9e2331dSJoe Perchestm toggle maintainers 1888b9e2331dSJoe Perchestg toggle git entries 1889b9e2331dSJoe Perchestl toggle open list entries 1890b9e2331dSJoe Perchests toggle subscriber list entries 18910c78c013SJoe Perchesf emails in file [$email_file_emails] 1892b9e2331dSJoe Perchesk keywords in file [$keywords] 1893b9e2331dSJoe Perchesr remove duplicates [$email_remove_duplicates] 1894b9e2331dSJoe Perchesp# pattern match depth [$pattern_depth] 1895b9e2331dSJoe PerchesEOT 1896683c6f8fSJoe Perches } 1897683c6f8fSJoe Perches print STDERR 1898683c6f8fSJoe Perches"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): "; 1899683c6f8fSJoe Perches 1900683c6f8fSJoe Perches my $input = <STDIN>; 1901dace8e30SFlorian Mickler chomp($input); 1902dace8e30SFlorian Mickler 1903683c6f8fSJoe Perches $redraw = 1; 1904683c6f8fSJoe Perches my $rerun = 0; 1905dace8e30SFlorian Mickler my @wish = split(/[, ]+/, $input); 1906dace8e30SFlorian Mickler foreach my $nr (@wish) { 1907683c6f8fSJoe Perches $nr = lc($nr); 1908683c6f8fSJoe Perches my $sel = substr($nr, 0, 1); 1909683c6f8fSJoe Perches my $str = substr($nr, 1); 1910683c6f8fSJoe Perches my $val = 0; 1911683c6f8fSJoe Perches $val = $1 if $str =~ /^(\d+)$/; 1912683c6f8fSJoe Perches 1913683c6f8fSJoe Perches if ($sel eq "y") { 1914683c6f8fSJoe Perches $interactive = 0; 1915683c6f8fSJoe Perches $done = 1; 1916683c6f8fSJoe Perches $output_rolestats = 0; 1917683c6f8fSJoe Perches $output_roles = 0; 19189ad18c85SVlastimil Babka $output_substatus = 0; 1919683c6f8fSJoe Perches last; 1920683c6f8fSJoe Perches } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) { 1921683c6f8fSJoe Perches $selected{$nr - 1} = !$selected{$nr - 1}; 1922683c6f8fSJoe Perches } elsif ($sel eq "*" || $sel eq '^') { 1923683c6f8fSJoe Perches my $toggle = 0; 1924683c6f8fSJoe Perches $toggle = 1 if ($sel eq '*'); 1925683c6f8fSJoe Perches for (my $i = 0; $i < $count; $i++) { 1926683c6f8fSJoe Perches $selected{$i} = $toggle; 1927dace8e30SFlorian Mickler } 1928683c6f8fSJoe Perches } elsif ($sel eq "0") { 1929683c6f8fSJoe Perches for (my $i = 0; $i < $count; $i++) { 1930683c6f8fSJoe Perches $selected{$i} = !$selected{$i}; 1931683c6f8fSJoe Perches } 1932b9e2331dSJoe Perches } elsif ($sel eq "t") { 1933b9e2331dSJoe Perches if (lc($str) eq "m") { 1934b9e2331dSJoe Perches for (my $i = 0; $i < $count; $i++) { 1935b9e2331dSJoe Perches $selected{$i} = !$selected{$i} 1936b9e2331dSJoe Perches if ($list[$i]->[1] =~ /^(maintainer|supporter)/i); 1937b9e2331dSJoe Perches } 1938b9e2331dSJoe Perches } elsif (lc($str) eq "g") { 1939b9e2331dSJoe Perches for (my $i = 0; $i < $count; $i++) { 1940b9e2331dSJoe Perches $selected{$i} = !$selected{$i} 1941b9e2331dSJoe Perches if ($list[$i]->[1] =~ /^(author|commit|signer)/i); 1942b9e2331dSJoe Perches } 1943b9e2331dSJoe Perches } elsif (lc($str) eq "l") { 1944b9e2331dSJoe Perches for (my $i = 0; $i < $count; $i++) { 1945b9e2331dSJoe Perches $selected{$i} = !$selected{$i} 1946b9e2331dSJoe Perches if ($list[$i]->[1] =~ /^(open list)/i); 1947b9e2331dSJoe Perches } 1948b9e2331dSJoe Perches } elsif (lc($str) eq "s") { 1949b9e2331dSJoe Perches for (my $i = 0; $i < $count; $i++) { 1950b9e2331dSJoe Perches $selected{$i} = !$selected{$i} 1951b9e2331dSJoe Perches if ($list[$i]->[1] =~ /^(subscriber list)/i); 1952b9e2331dSJoe Perches } 1953b9e2331dSJoe Perches } 1954683c6f8fSJoe Perches } elsif ($sel eq "a") { 1955683c6f8fSJoe Perches if ($val > 0 && $val <= $count) { 1956683c6f8fSJoe Perches $authored{$val - 1} = !$authored{$val - 1}; 1957683c6f8fSJoe Perches } elsif ($str eq '*' || $str eq '^') { 1958683c6f8fSJoe Perches my $toggle = 0; 1959683c6f8fSJoe Perches $toggle = 1 if ($str eq '*'); 1960683c6f8fSJoe Perches for (my $i = 0; $i < $count; $i++) { 1961683c6f8fSJoe Perches $authored{$i} = $toggle; 1962683c6f8fSJoe Perches } 1963683c6f8fSJoe Perches } 1964683c6f8fSJoe Perches } elsif ($sel eq "s") { 1965683c6f8fSJoe Perches if ($val > 0 && $val <= $count) { 1966683c6f8fSJoe Perches $signed{$val - 1} = !$signed{$val - 1}; 1967683c6f8fSJoe Perches } elsif ($str eq '*' || $str eq '^') { 1968683c6f8fSJoe Perches my $toggle = 0; 1969683c6f8fSJoe Perches $toggle = 1 if ($str eq '*'); 1970683c6f8fSJoe Perches for (my $i = 0; $i < $count; $i++) { 1971683c6f8fSJoe Perches $signed{$i} = $toggle; 1972683c6f8fSJoe Perches } 1973683c6f8fSJoe Perches } 1974683c6f8fSJoe Perches } elsif ($sel eq "o") { 1975683c6f8fSJoe Perches $print_options = 1; 1976683c6f8fSJoe Perches $redraw = 1; 1977683c6f8fSJoe Perches } elsif ($sel eq "g") { 1978683c6f8fSJoe Perches if ($str eq "f") { 1979683c6f8fSJoe Perches bool_invert(\$email_git_fallback); 1980dace8e30SFlorian Mickler } else { 1981683c6f8fSJoe Perches bool_invert(\$email_git); 1982683c6f8fSJoe Perches } 1983683c6f8fSJoe Perches $rerun = 1; 1984683c6f8fSJoe Perches } elsif ($sel eq "b") { 1985683c6f8fSJoe Perches if ($str eq "s") { 1986683c6f8fSJoe Perches bool_invert(\$email_git_blame_signatures); 1987683c6f8fSJoe Perches } else { 1988683c6f8fSJoe Perches bool_invert(\$email_git_blame); 1989683c6f8fSJoe Perches } 1990683c6f8fSJoe Perches $rerun = 1; 1991683c6f8fSJoe Perches } elsif ($sel eq "c") { 1992683c6f8fSJoe Perches if ($val > 0) { 1993683c6f8fSJoe Perches $email_git_min_signatures = $val; 1994683c6f8fSJoe Perches $rerun = 1; 1995683c6f8fSJoe Perches } 1996683c6f8fSJoe Perches } elsif ($sel eq "x") { 1997683c6f8fSJoe Perches if ($val > 0) { 1998683c6f8fSJoe Perches $email_git_max_maintainers = $val; 1999683c6f8fSJoe Perches $rerun = 1; 2000683c6f8fSJoe Perches } 2001683c6f8fSJoe Perches } elsif ($sel eq "%") { 2002683c6f8fSJoe Perches if ($str ne "" && $val >= 0) { 2003683c6f8fSJoe Perches $email_git_min_percent = $val; 2004683c6f8fSJoe Perches $rerun = 1; 2005683c6f8fSJoe Perches } 2006683c6f8fSJoe Perches } elsif ($sel eq "d") { 2007683c6f8fSJoe Perches if (vcs_is_git()) { 2008683c6f8fSJoe Perches $email_git_since = $str; 2009683c6f8fSJoe Perches } elsif (vcs_is_hg()) { 2010683c6f8fSJoe Perches $email_hg_since = $str; 2011683c6f8fSJoe Perches } 2012683c6f8fSJoe Perches $rerun = 1; 2013683c6f8fSJoe Perches } elsif ($sel eq "t") { 2014683c6f8fSJoe Perches bool_invert(\$email_git_all_signature_types); 2015683c6f8fSJoe Perches $rerun = 1; 2016683c6f8fSJoe Perches } elsif ($sel eq "f") { 20170c78c013SJoe Perches bool_invert(\$email_file_emails); 2018683c6f8fSJoe Perches $rerun = 1; 2019683c6f8fSJoe Perches } elsif ($sel eq "r") { 2020683c6f8fSJoe Perches bool_invert(\$email_remove_duplicates); 2021683c6f8fSJoe Perches $rerun = 1; 2022b9e2331dSJoe Perches } elsif ($sel eq "m") { 2023b9e2331dSJoe Perches bool_invert(\$email_use_mailmap); 2024b9e2331dSJoe Perches read_mailmap(); 2025b9e2331dSJoe Perches $rerun = 1; 2026683c6f8fSJoe Perches } elsif ($sel eq "k") { 2027683c6f8fSJoe Perches bool_invert(\$keywords); 2028683c6f8fSJoe Perches $rerun = 1; 2029683c6f8fSJoe Perches } elsif ($sel eq "p") { 2030683c6f8fSJoe Perches if ($str ne "" && $val >= 0) { 2031683c6f8fSJoe Perches $pattern_depth = $val; 2032683c6f8fSJoe Perches $rerun = 1; 2033683c6f8fSJoe Perches } 20346ef1c52eSJoe Perches } elsif ($sel eq "h" || $sel eq "?") { 20356ef1c52eSJoe Perches print STDERR <<EOT 20366ef1c52eSJoe Perches 20376ef1c52eSJoe PerchesInteractive mode allows you to select the various maintainers, submitters, 20386ef1c52eSJoe Perchescommit signers and mailing lists that could be CC'd on a patch. 20396ef1c52eSJoe Perches 20406ef1c52eSJoe PerchesAny *'d entry is selected. 20416ef1c52eSJoe Perches 204247abc722SJoe PerchesIf you have git or hg installed, you can choose to summarize the commit 20436ef1c52eSJoe Percheshistory of files in the patch. Also, each line of the current file can 20446ef1c52eSJoe Perchesbe matched to its commit author and that commits signers with blame. 20456ef1c52eSJoe Perches 20466ef1c52eSJoe PerchesVarious knobs exist to control the length of time for active commit 20476ef1c52eSJoe Perchestracking, the maximum number of commit authors and signers to add, 20486ef1c52eSJoe Perchesand such. 20496ef1c52eSJoe Perches 20506ef1c52eSJoe PerchesEnter selections at the prompt until you are satisfied that the selected 20516ef1c52eSJoe Perchesmaintainers are appropriate. You may enter multiple selections separated 20526ef1c52eSJoe Perchesby either commas or spaces. 20536ef1c52eSJoe Perches 20546ef1c52eSJoe PerchesEOT 2055683c6f8fSJoe Perches } else { 2056683c6f8fSJoe Perches print STDERR "invalid option: '$nr'\n"; 2057683c6f8fSJoe Perches $redraw = 0; 2058dace8e30SFlorian Mickler } 2059dace8e30SFlorian Mickler } 2060683c6f8fSJoe Perches if ($rerun) { 2061683c6f8fSJoe Perches print STDERR "git-blame can be very slow, please have patience..." 2062683c6f8fSJoe Perches if ($email_git_blame); 20636ef1c52eSJoe Perches goto &get_maintainers; 2064683c6f8fSJoe Perches } 2065683c6f8fSJoe Perches } 2066dace8e30SFlorian Mickler 2067dace8e30SFlorian Mickler #drop not selected entries 2068dace8e30SFlorian Mickler $count = 0; 2069683c6f8fSJoe Perches my @new_emailto = (); 2070dace8e30SFlorian Mickler foreach my $entry (@list) { 2071dace8e30SFlorian Mickler if ($selected{$count}) { 2072dace8e30SFlorian Mickler push(@new_emailto, $list[$count]); 2073dace8e30SFlorian Mickler } 2074dace8e30SFlorian Mickler $count++; 2075dace8e30SFlorian Mickler } 2076683c6f8fSJoe Perches return @new_emailto; 2077dace8e30SFlorian Mickler} 2078dace8e30SFlorian Mickler 2079683c6f8fSJoe Perchessub bool_invert { 2080683c6f8fSJoe Perches my ($bool_ref) = @_; 2081683c6f8fSJoe Perches 2082683c6f8fSJoe Perches if ($$bool_ref) { 2083683c6f8fSJoe Perches $$bool_ref = 0; 2084683c6f8fSJoe Perches } else { 2085683c6f8fSJoe Perches $$bool_ref = 1; 2086683c6f8fSJoe Perches } 2087dace8e30SFlorian Mickler} 2088dace8e30SFlorian Mickler 2089b9e2331dSJoe Perchessub deduplicate_email { 2090b9e2331dSJoe Perches my ($email) = @_; 2091b9e2331dSJoe Perches 2092b9e2331dSJoe Perches my $matched = 0; 2093b9e2331dSJoe Perches my ($name, $address) = parse_email($email); 2094b9e2331dSJoe Perches $email = format_email($name, $address, 1); 2095b9e2331dSJoe Perches $email = mailmap_email($email); 2096b9e2331dSJoe Perches 2097b9e2331dSJoe Perches return $email if (!$email_remove_duplicates); 2098b9e2331dSJoe Perches 2099b9e2331dSJoe Perches ($name, $address) = parse_email($email); 2100b9e2331dSJoe Perches 2101fae99206SJoe Perches if ($name ne "" && $deduplicate_name_hash{lc($name)}) { 2102b9e2331dSJoe Perches $name = $deduplicate_name_hash{lc($name)}->[0]; 2103b9e2331dSJoe Perches $address = $deduplicate_name_hash{lc($name)}->[1]; 2104b9e2331dSJoe Perches $matched = 1; 2105b9e2331dSJoe Perches } elsif ($deduplicate_address_hash{lc($address)}) { 2106b9e2331dSJoe Perches $name = $deduplicate_address_hash{lc($address)}->[0]; 2107b9e2331dSJoe Perches $address = $deduplicate_address_hash{lc($address)}->[1]; 2108b9e2331dSJoe Perches $matched = 1; 2109b9e2331dSJoe Perches } 2110b9e2331dSJoe Perches if (!$matched) { 2111b9e2331dSJoe Perches $deduplicate_name_hash{lc($name)} = [ $name, $address ]; 2112b9e2331dSJoe Perches $deduplicate_address_hash{lc($address)} = [ $name, $address ]; 2113b9e2331dSJoe Perches } 2114b9e2331dSJoe Perches $email = format_email($name, $address, 1); 2115b9e2331dSJoe Perches $email = mailmap_email($email); 2116b9e2331dSJoe Perches return $email; 2117b9e2331dSJoe Perches} 2118b9e2331dSJoe Perches 2119683c6f8fSJoe Perchessub save_commits_by_author { 2120683c6f8fSJoe Perches my (@lines) = @_; 2121683c6f8fSJoe Perches 2122683c6f8fSJoe Perches my @authors = (); 2123683c6f8fSJoe Perches my @commits = (); 2124683c6f8fSJoe Perches my @subjects = (); 2125683c6f8fSJoe Perches 2126683c6f8fSJoe Perches foreach my $line (@lines) { 2127683c6f8fSJoe Perches if ($line =~ m/$VCS_cmds{"author_pattern"}/) { 2128683c6f8fSJoe Perches my $author = $1; 2129b9e2331dSJoe Perches $author = deduplicate_email($author); 2130683c6f8fSJoe Perches push(@authors, $author); 2131dace8e30SFlorian Mickler } 2132683c6f8fSJoe Perches push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/); 2133683c6f8fSJoe Perches push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/); 2134dace8e30SFlorian Mickler } 2135dace8e30SFlorian Mickler 2136683c6f8fSJoe Perches for (my $i = 0; $i < @authors; $i++) { 2137683c6f8fSJoe Perches my $exists = 0; 2138683c6f8fSJoe Perches foreach my $ref(@{$commit_author_hash{$authors[$i]}}) { 2139683c6f8fSJoe Perches if (@{$ref}[0] eq $commits[$i] && 2140683c6f8fSJoe Perches @{$ref}[1] eq $subjects[$i]) { 2141683c6f8fSJoe Perches $exists = 1; 2142683c6f8fSJoe Perches last; 2143683c6f8fSJoe Perches } 2144683c6f8fSJoe Perches } 2145683c6f8fSJoe Perches if (!$exists) { 2146683c6f8fSJoe Perches push(@{$commit_author_hash{$authors[$i]}}, 2147683c6f8fSJoe Perches [ ($commits[$i], $subjects[$i]) ]); 2148683c6f8fSJoe Perches } 2149683c6f8fSJoe Perches } 2150683c6f8fSJoe Perches} 2151dace8e30SFlorian Mickler 2152683c6f8fSJoe Perchessub save_commits_by_signer { 2153683c6f8fSJoe Perches my (@lines) = @_; 2154683c6f8fSJoe Perches 2155683c6f8fSJoe Perches my $commit = ""; 2156683c6f8fSJoe Perches my $subject = ""; 2157683c6f8fSJoe Perches 2158683c6f8fSJoe Perches foreach my $line (@lines) { 2159683c6f8fSJoe Perches $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/); 2160683c6f8fSJoe Perches $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/); 2161683c6f8fSJoe Perches if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) { 2162683c6f8fSJoe Perches my @signatures = ($line); 2163683c6f8fSJoe Perches my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); 2164683c6f8fSJoe Perches my @types = @$types_ref; 2165683c6f8fSJoe Perches my @signers = @$signers_ref; 2166683c6f8fSJoe Perches 2167683c6f8fSJoe Perches my $type = $types[0]; 2168683c6f8fSJoe Perches my $signer = $signers[0]; 2169683c6f8fSJoe Perches 2170b9e2331dSJoe Perches $signer = deduplicate_email($signer); 21716ef1c52eSJoe Perches 2172683c6f8fSJoe Perches my $exists = 0; 2173683c6f8fSJoe Perches foreach my $ref(@{$commit_signer_hash{$signer}}) { 2174683c6f8fSJoe Perches if (@{$ref}[0] eq $commit && 2175683c6f8fSJoe Perches @{$ref}[1] eq $subject && 2176683c6f8fSJoe Perches @{$ref}[2] eq $type) { 2177683c6f8fSJoe Perches $exists = 1; 2178683c6f8fSJoe Perches last; 2179683c6f8fSJoe Perches } 2180683c6f8fSJoe Perches } 2181683c6f8fSJoe Perches if (!$exists) { 2182683c6f8fSJoe Perches push(@{$commit_signer_hash{$signer}}, 2183683c6f8fSJoe Perches [ ($commit, $subject, $type) ]); 2184683c6f8fSJoe Perches } 2185683c6f8fSJoe Perches } 2186683c6f8fSJoe Perches } 2187dace8e30SFlorian Mickler} 2188dace8e30SFlorian Mickler 218960db31acSJoe Perchessub vcs_assign { 2190a8af2430SJoe Perches my ($role, $divisor, @lines) = @_; 2191a8af2430SJoe Perches 2192a8af2430SJoe Perches my %hash; 2193a8af2430SJoe Perches my $count = 0; 2194a8af2430SJoe Perches 2195a8af2430SJoe Perches return if (@lines <= 0); 2196a8af2430SJoe Perches 2197a8af2430SJoe Perches if ($divisor <= 0) { 219860db31acSJoe Perches warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n"); 2199a8af2430SJoe Perches $divisor = 1; 22003c7385b8SJoe Perches } 22018cbb3a77SJoe Perches 22020e70e83dSJoe Perches @lines = mailmap(@lines); 22030e70e83dSJoe Perches 220463ab52dbSJoe Perches return if (@lines <= 0); 220563ab52dbSJoe Perches 22060e70e83dSJoe Perches @lines = sort(@lines); 2207afa81ee1SJoe Perches 220811ecf53cSJoe Perches # uniq -c 220911ecf53cSJoe Perches $hash{$_}++ for @lines; 221011ecf53cSJoe Perches 221111ecf53cSJoe Perches # sort -rn 221211ecf53cSJoe Perches foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { 221311ecf53cSJoe Perches my $sign_offs = $hash{$line}; 2214a8af2430SJoe Perches my $percent = $sign_offs * 100 / $divisor; 22153c7385b8SJoe Perches 2216a8af2430SJoe Perches $percent = 100 if ($percent > 100); 2217435de078SJoe Perches next if (ignore_email_address($line)); 2218cb7301c7SJoe Perches $count++; 221911ecf53cSJoe Perches last if ($sign_offs < $email_git_min_signatures || 2220afa81ee1SJoe Perches $count > $email_git_max_maintainers || 2221a8af2430SJoe Perches $percent < $email_git_min_percent); 22223c7385b8SJoe Perches push_email_address($line, ''); 22233c7385b8SJoe Perches if ($output_rolestats) { 2224a8af2430SJoe Perches my $fmt_percent = sprintf("%.0f", $percent); 2225a8af2430SJoe Perches add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%"); 2226a8af2430SJoe Perches } else { 22273c7385b8SJoe Perches add_role($line, $role); 2228cb7301c7SJoe Perches } 2229cb7301c7SJoe Perches } 2230a8af2430SJoe Perches} 2231a8af2430SJoe Perches 223260db31acSJoe Perchessub vcs_file_signoffs { 2233a8af2430SJoe Perches my ($file) = @_; 2234a8af2430SJoe Perches 2235c9ecefeaSJoe Perches my $authors_ref; 2236c9ecefeaSJoe Perches my $signers_ref; 2237c9ecefeaSJoe Perches my $stats_ref; 2238c9ecefeaSJoe Perches my @authors = (); 2239a8af2430SJoe Perches my @signers = (); 2240c9ecefeaSJoe Perches my @stats = (); 224160db31acSJoe Perches my $commits; 2242a8af2430SJoe Perches 2243683c6f8fSJoe Perches $vcs_used = vcs_exists(); 2244683c6f8fSJoe Perches return if (!$vcs_used); 2245a8af2430SJoe Perches 224660db31acSJoe Perches my $cmd = $VCS_cmds{"find_signers_cmd"}; 224760db31acSJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd 224860db31acSJoe Perches 2249c9ecefeaSJoe Perches ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); 2250c9ecefeaSJoe Perches 2251c9ecefeaSJoe Perches @signers = @{$signers_ref} if defined $signers_ref; 2252c9ecefeaSJoe Perches @authors = @{$authors_ref} if defined $authors_ref; 2253c9ecefeaSJoe Perches @stats = @{$stats_ref} if defined $stats_ref; 2254c9ecefeaSJoe Perches 2255c9ecefeaSJoe Perches# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n"); 2256b9e2331dSJoe Perches 2257b9e2331dSJoe Perches foreach my $signer (@signers) { 2258b9e2331dSJoe Perches $signer = deduplicate_email($signer); 2259b9e2331dSJoe Perches } 2260b9e2331dSJoe Perches 226160db31acSJoe Perches vcs_assign("commit_signer", $commits, @signers); 2262c9ecefeaSJoe Perches vcs_assign("authored", $commits, @authors); 2263c9ecefeaSJoe Perches if ($#authors == $#stats) { 2264c9ecefeaSJoe Perches my $stat_pattern = $VCS_cmds{"stat_pattern"}; 2265c9ecefeaSJoe Perches $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern 2266c9ecefeaSJoe Perches 2267c9ecefeaSJoe Perches my $added = 0; 2268c9ecefeaSJoe Perches my $deleted = 0; 2269c9ecefeaSJoe Perches for (my $i = 0; $i <= $#stats; $i++) { 2270c9ecefeaSJoe Perches if ($stats[$i] =~ /$stat_pattern/) { 2271c9ecefeaSJoe Perches $added += $1; 2272c9ecefeaSJoe Perches $deleted += $2; 2273c9ecefeaSJoe Perches } 2274c9ecefeaSJoe Perches } 2275c9ecefeaSJoe Perches my @tmp_authors = uniq(@authors); 2276c9ecefeaSJoe Perches foreach my $author (@tmp_authors) { 2277c9ecefeaSJoe Perches $author = deduplicate_email($author); 2278c9ecefeaSJoe Perches } 2279c9ecefeaSJoe Perches @tmp_authors = uniq(@tmp_authors); 2280c9ecefeaSJoe Perches my @list_added = (); 2281c9ecefeaSJoe Perches my @list_deleted = (); 2282c9ecefeaSJoe Perches foreach my $author (@tmp_authors) { 2283c9ecefeaSJoe Perches my $auth_added = 0; 2284c9ecefeaSJoe Perches my $auth_deleted = 0; 2285c9ecefeaSJoe Perches for (my $i = 0; $i <= $#stats; $i++) { 2286c9ecefeaSJoe Perches if ($author eq deduplicate_email($authors[$i]) && 2287c9ecefeaSJoe Perches $stats[$i] =~ /$stat_pattern/) { 2288c9ecefeaSJoe Perches $auth_added += $1; 2289c9ecefeaSJoe Perches $auth_deleted += $2; 2290c9ecefeaSJoe Perches } 2291c9ecefeaSJoe Perches } 2292c9ecefeaSJoe Perches for (my $i = 0; $i < $auth_added; $i++) { 2293c9ecefeaSJoe Perches push(@list_added, $author); 2294c9ecefeaSJoe Perches } 2295c9ecefeaSJoe Perches for (my $i = 0; $i < $auth_deleted; $i++) { 2296c9ecefeaSJoe Perches push(@list_deleted, $author); 2297c9ecefeaSJoe Perches } 2298c9ecefeaSJoe Perches } 2299c9ecefeaSJoe Perches vcs_assign("added_lines", $added, @list_added); 2300c9ecefeaSJoe Perches vcs_assign("removed_lines", $deleted, @list_deleted); 2301c9ecefeaSJoe Perches } 2302a8af2430SJoe Perches} 2303f5492666SJoe Perches 230460db31acSJoe Perchessub vcs_file_blame { 2305f5492666SJoe Perches my ($file) = @_; 2306f5492666SJoe Perches 230760db31acSJoe Perches my @signers = (); 230863ab52dbSJoe Perches my @all_commits = (); 2309a8af2430SJoe Perches my @commits = (); 2310a8af2430SJoe Perches my $total_commits; 231163ab52dbSJoe Perches my $total_lines; 2312f5492666SJoe Perches 2313683c6f8fSJoe Perches $vcs_used = vcs_exists(); 2314683c6f8fSJoe Perches return if (!$vcs_used); 2315f5492666SJoe Perches 231663ab52dbSJoe Perches @all_commits = vcs_blame($file); 231763ab52dbSJoe Perches @commits = uniq(@all_commits); 2318a8af2430SJoe Perches $total_commits = @commits; 231963ab52dbSJoe Perches $total_lines = @all_commits; 2320a8af2430SJoe Perches 2321683c6f8fSJoe Perches if ($email_git_blame_signatures) { 2322683c6f8fSJoe Perches if (vcs_is_hg()) { 2323683c6f8fSJoe Perches my $commit_count; 2324c9ecefeaSJoe Perches my $commit_authors_ref; 2325c9ecefeaSJoe Perches my $commit_signers_ref; 2326c9ecefeaSJoe Perches my $stats_ref; 2327c9ecefeaSJoe Perches my @commit_authors = (); 2328683c6f8fSJoe Perches my @commit_signers = (); 2329683c6f8fSJoe Perches my $commit = join(" -r ", @commits); 2330683c6f8fSJoe Perches my $cmd; 2331683c6f8fSJoe Perches 2332683c6f8fSJoe Perches $cmd = $VCS_cmds{"find_commit_signers_cmd"}; 2333683c6f8fSJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd 2334683c6f8fSJoe Perches 2335c9ecefeaSJoe Perches ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); 2336c9ecefeaSJoe Perches @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; 2337c9ecefeaSJoe Perches @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; 2338683c6f8fSJoe Perches 2339683c6f8fSJoe Perches push(@signers, @commit_signers); 2340683c6f8fSJoe Perches } else { 2341f5492666SJoe Perches foreach my $commit (@commits) { 2342a8af2430SJoe Perches my $commit_count; 2343c9ecefeaSJoe Perches my $commit_authors_ref; 2344c9ecefeaSJoe Perches my $commit_signers_ref; 2345c9ecefeaSJoe Perches my $stats_ref; 2346c9ecefeaSJoe Perches my @commit_authors = (); 2347a8af2430SJoe Perches my @commit_signers = (); 2348683c6f8fSJoe Perches my $cmd; 2349f5492666SJoe Perches 2350683c6f8fSJoe Perches $cmd = $VCS_cmds{"find_commit_signers_cmd"}; 2351dace8e30SFlorian Mickler $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd 235260db31acSJoe Perches 2353c9ecefeaSJoe Perches ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); 2354c9ecefeaSJoe Perches @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; 2355c9ecefeaSJoe Perches @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; 235663ab52dbSJoe Perches 235760db31acSJoe Perches push(@signers, @commit_signers); 23580e70e83dSJoe Perches } 2359683c6f8fSJoe Perches } 2360683c6f8fSJoe Perches } 23618cbb3a77SJoe Perches 23623c7385b8SJoe Perches if ($from_filename) { 236363ab52dbSJoe Perches if ($output_rolestats) { 236463ab52dbSJoe Perches my @blame_signers; 2365683c6f8fSJoe Perches if (vcs_is_hg()) {{ # Double brace for last exit 2366683c6f8fSJoe Perches my $commit_count; 2367683c6f8fSJoe Perches my @commit_signers = (); 2368683c6f8fSJoe Perches @commits = uniq(@commits); 2369683c6f8fSJoe Perches @commits = sort(@commits); 2370683c6f8fSJoe Perches my $commit = join(" -r ", @commits); 2371683c6f8fSJoe Perches my $cmd; 2372683c6f8fSJoe Perches 2373683c6f8fSJoe Perches $cmd = $VCS_cmds{"find_commit_author_cmd"}; 2374683c6f8fSJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd 2375683c6f8fSJoe Perches 2376683c6f8fSJoe Perches my @lines = (); 2377683c6f8fSJoe Perches 2378683c6f8fSJoe Perches @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); 2379683c6f8fSJoe Perches 2380683c6f8fSJoe Perches if (!$email_git_penguin_chiefs) { 2381683c6f8fSJoe Perches @lines = grep(!/${penguin_chiefs}/i, @lines); 2382683c6f8fSJoe Perches } 2383683c6f8fSJoe Perches 2384683c6f8fSJoe Perches last if !@lines; 2385683c6f8fSJoe Perches 2386683c6f8fSJoe Perches my @authors = (); 2387683c6f8fSJoe Perches foreach my $line (@lines) { 2388683c6f8fSJoe Perches if ($line =~ m/$VCS_cmds{"author_pattern"}/) { 2389683c6f8fSJoe Perches my $author = $1; 2390b9e2331dSJoe Perches $author = deduplicate_email($author); 2391b9e2331dSJoe Perches push(@authors, $author); 2392683c6f8fSJoe Perches } 2393683c6f8fSJoe Perches } 2394683c6f8fSJoe Perches 2395683c6f8fSJoe Perches save_commits_by_author(@lines) if ($interactive); 2396683c6f8fSJoe Perches save_commits_by_signer(@lines) if ($interactive); 2397683c6f8fSJoe Perches 2398683c6f8fSJoe Perches push(@signers, @authors); 2399683c6f8fSJoe Perches }} 2400683c6f8fSJoe Perches else { 240163ab52dbSJoe Perches foreach my $commit (@commits) { 240263ab52dbSJoe Perches my $i; 240363ab52dbSJoe Perches my $cmd = $VCS_cmds{"find_commit_author_cmd"}; 240463ab52dbSJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd 240563ab52dbSJoe Perches my @author = vcs_find_author($cmd); 240663ab52dbSJoe Perches next if !@author; 2407b9e2331dSJoe Perches 2408b9e2331dSJoe Perches my $formatted_author = deduplicate_email($author[0]); 2409b9e2331dSJoe Perches 241063ab52dbSJoe Perches my $count = grep(/$commit/, @all_commits); 241163ab52dbSJoe Perches for ($i = 0; $i < $count ; $i++) { 2412b9e2331dSJoe Perches push(@blame_signers, $formatted_author); 241363ab52dbSJoe Perches } 241463ab52dbSJoe Perches } 2415683c6f8fSJoe Perches } 241663ab52dbSJoe Perches if (@blame_signers) { 241763ab52dbSJoe Perches vcs_assign("authored lines", $total_lines, @blame_signers); 241863ab52dbSJoe Perches } 241963ab52dbSJoe Perches } 2420b9e2331dSJoe Perches foreach my $signer (@signers) { 2421b9e2331dSJoe Perches $signer = deduplicate_email($signer); 2422b9e2331dSJoe Perches } 242360db31acSJoe Perches vcs_assign("commits", $total_commits, @signers); 24243c7385b8SJoe Perches } else { 2425b9e2331dSJoe Perches foreach my $signer (@signers) { 2426b9e2331dSJoe Perches $signer = deduplicate_email($signer); 2427b9e2331dSJoe Perches } 242860db31acSJoe Perches vcs_assign("modified commits", $total_commits, @signers); 2429cb7301c7SJoe Perches } 2430cb7301c7SJoe Perches} 2431cb7301c7SJoe Perches 24324cad35a7SJoe Perchessub vcs_file_exists { 24334cad35a7SJoe Perches my ($file) = @_; 24344cad35a7SJoe Perches 24354cad35a7SJoe Perches my $exists; 24364cad35a7SJoe Perches 24374cad35a7SJoe Perches my $vcs_used = vcs_exists(); 24384cad35a7SJoe Perches return 0 if (!$vcs_used); 24394cad35a7SJoe Perches 24404cad35a7SJoe Perches my $cmd = $VCS_cmds{"file_exists_cmd"}; 24414cad35a7SJoe Perches $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd 24428582fb59SJoe Perches $cmd .= " 2>&1"; 24434cad35a7SJoe Perches $exists = &{$VCS_cmds{"execute_cmd"}}($cmd); 24444cad35a7SJoe Perches 24458582fb59SJoe Perches return 0 if ($? != 0); 24468582fb59SJoe Perches 24474cad35a7SJoe Perches return $exists; 24484cad35a7SJoe Perches} 24494cad35a7SJoe Perches 2450e1f75904STom Saegersub vcs_list_files { 2451e1f75904STom Saeger my ($file) = @_; 2452e1f75904STom Saeger 2453e1f75904STom Saeger my @lsfiles = (); 2454e1f75904STom Saeger 2455e1f75904STom Saeger my $vcs_used = vcs_exists(); 2456e1f75904STom Saeger return 0 if (!$vcs_used); 2457e1f75904STom Saeger 2458e1f75904STom Saeger my $cmd = $VCS_cmds{"list_files_cmd"}; 2459e1f75904STom Saeger $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd 2460e1f75904STom Saeger @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd); 2461e1f75904STom Saeger 2462e1f75904STom Saeger return () if ($? != 0); 2463e1f75904STom Saeger 2464e1f75904STom Saeger return @lsfiles; 2465e1f75904STom Saeger} 2466e1f75904STom Saeger 2467cb7301c7SJoe Perchessub uniq { 2468a8af2430SJoe Perches my (@parms) = @_; 2469cb7301c7SJoe Perches 2470cb7301c7SJoe Perches my %saw; 2471cb7301c7SJoe Perches @parms = grep(!$saw{$_}++, @parms); 2472cb7301c7SJoe Perches return @parms; 2473cb7301c7SJoe Perches} 2474cb7301c7SJoe Perches 2475cb7301c7SJoe Perchessub sort_and_uniq { 2476a8af2430SJoe Perches my (@parms) = @_; 2477cb7301c7SJoe Perches 2478cb7301c7SJoe Perches my %saw; 2479cb7301c7SJoe Perches @parms = sort @parms; 2480cb7301c7SJoe Perches @parms = grep(!$saw{$_}++, @parms); 2481cb7301c7SJoe Perches return @parms; 2482cb7301c7SJoe Perches} 2483cb7301c7SJoe Perches 248403372dbbSJoe Perchessub clean_file_emails { 248503372dbbSJoe Perches my (@file_emails) = @_; 248603372dbbSJoe Perches my @fmt_emails = (); 248703372dbbSJoe Perches 248803372dbbSJoe Perches foreach my $email (@file_emails) { 248903372dbbSJoe Perches $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g; 249003372dbbSJoe Perches my ($name, $address) = parse_email($email); 249103372dbbSJoe Perches 24922639772aSAlvin Šipraga # Strip quotes for easier processing, format_email will add them back 24932639772aSAlvin Šipraga $name =~ s/^"(.*)"$/$1/; 24942639772aSAlvin Šipraga 24952639772aSAlvin Šipraga # Split into name-like parts and remove stray punctuation particles 24969c334eb9SAlvin Šipraga my @nw = split(/[^\p{L}\'\,\.\+-]/, $name); 24972639772aSAlvin Šipraga @nw = grep(!/^[\'\,\.\+-]$/, @nw); 24982639772aSAlvin Šipraga 24992639772aSAlvin Šipraga # Make a best effort to extract the name, and only the name, by taking 25002639772aSAlvin Šipraga # only the last two names, or in the case of obvious initials, the last 25012639772aSAlvin Šipraga # three names. 250203372dbbSJoe Perches if (@nw > 2) { 250303372dbbSJoe Perches my $first = $nw[@nw - 3]; 250403372dbbSJoe Perches my $middle = $nw[@nw - 2]; 250503372dbbSJoe Perches my $last = $nw[@nw - 1]; 250603372dbbSJoe Perches 25079c334eb9SAlvin Šipraga if (((length($first) == 1 && $first =~ m/\p{L}/) || 250803372dbbSJoe Perches (length($first) == 2 && substr($first, -1) eq ".")) || 250903372dbbSJoe Perches (length($middle) == 1 || 251003372dbbSJoe Perches (length($middle) == 2 && substr($middle, -1) eq "."))) { 251103372dbbSJoe Perches $name = "$first $middle $last"; 251203372dbbSJoe Perches } else { 251303372dbbSJoe Perches $name = "$middle $last"; 251403372dbbSJoe Perches } 25152639772aSAlvin Šipraga } else { 25162639772aSAlvin Šipraga $name = "@nw"; 251703372dbbSJoe Perches } 251803372dbbSJoe Perches 251903372dbbSJoe Perches if (substr($name, -1) =~ /[,\.]/) { 252003372dbbSJoe Perches $name = substr($name, 0, length($name) - 1); 252103372dbbSJoe Perches } 252203372dbbSJoe Perches 252303372dbbSJoe Perches if (substr($name, 0, 1) =~ /[,\.]/) { 252403372dbbSJoe Perches $name = substr($name, 1, length($name) - 1); 252503372dbbSJoe Perches } 252603372dbbSJoe Perches 252703372dbbSJoe Perches my $fmt_email = format_email($name, $address, $email_usename); 252803372dbbSJoe Perches push(@fmt_emails, $fmt_email); 252903372dbbSJoe Perches } 253003372dbbSJoe Perches return @fmt_emails; 253103372dbbSJoe Perches} 253203372dbbSJoe Perches 25333c7385b8SJoe Perchessub merge_email { 25343c7385b8SJoe Perches my @lines; 25353c7385b8SJoe Perches my %saw; 25363c7385b8SJoe Perches 25373c7385b8SJoe Perches for (@_) { 25383c7385b8SJoe Perches my ($address, $role) = @$_; 25393c7385b8SJoe Perches if (!$saw{$address}) { 25403c7385b8SJoe Perches if ($output_roles) { 254160db31acSJoe Perches push(@lines, "$address ($role)"); 25423c7385b8SJoe Perches } else { 254360db31acSJoe Perches push(@lines, $address); 25443c7385b8SJoe Perches } 25453c7385b8SJoe Perches $saw{$address} = 1; 25463c7385b8SJoe Perches } 25473c7385b8SJoe Perches } 25483c7385b8SJoe Perches 25493c7385b8SJoe Perches return @lines; 25503c7385b8SJoe Perches} 25513c7385b8SJoe Perches 2552cb7301c7SJoe Perchessub output { 2553a8af2430SJoe Perches my (@parms) = @_; 2554cb7301c7SJoe Perches 2555cb7301c7SJoe Perches if ($output_multiline) { 2556cb7301c7SJoe Perches foreach my $line (@parms) { 2557cb7301c7SJoe Perches print("${line}\n"); 2558cb7301c7SJoe Perches } 2559cb7301c7SJoe Perches } else { 2560cb7301c7SJoe Perches print(join($output_separator, @parms)); 2561cb7301c7SJoe Perches print("\n"); 2562cb7301c7SJoe Perches } 2563cb7301c7SJoe Perches} 25641b5e1cf6SJoe Perches 25651b5e1cf6SJoe Perchesmy $rfc822re; 25661b5e1cf6SJoe Perches 25671b5e1cf6SJoe Perchessub make_rfc822re { 25681b5e1cf6SJoe Perches# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and 25691b5e1cf6SJoe Perches# comment. We must allow for rfc822_lwsp (or comments) after each of these. 25701b5e1cf6SJoe Perches# This regexp will only work on addresses which have had comments stripped 25711b5e1cf6SJoe Perches# and replaced with rfc822_lwsp. 25721b5e1cf6SJoe Perches 25731b5e1cf6SJoe Perches my $specials = '()<>@,;:\\\\".\\[\\]'; 25741b5e1cf6SJoe Perches my $controls = '\\000-\\037\\177'; 25751b5e1cf6SJoe Perches 25761b5e1cf6SJoe Perches my $dtext = "[^\\[\\]\\r\\\\]"; 25771b5e1cf6SJoe Perches my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*"; 25781b5e1cf6SJoe Perches 25791b5e1cf6SJoe Perches my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*"; 25801b5e1cf6SJoe Perches 25811b5e1cf6SJoe Perches# Use zero-width assertion to spot the limit of an atom. A simple 25821b5e1cf6SJoe Perches# $rfc822_lwsp* causes the regexp engine to hang occasionally. 25831b5e1cf6SJoe Perches my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))"; 25841b5e1cf6SJoe Perches my $word = "(?:$atom|$quoted_string)"; 25851b5e1cf6SJoe Perches my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*"; 25861b5e1cf6SJoe Perches 25871b5e1cf6SJoe Perches my $sub_domain = "(?:$atom|$domain_literal)"; 25881b5e1cf6SJoe Perches my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*"; 25891b5e1cf6SJoe Perches 25901b5e1cf6SJoe Perches my $addr_spec = "$localpart\@$rfc822_lwsp*$domain"; 25911b5e1cf6SJoe Perches 25921b5e1cf6SJoe Perches my $phrase = "$word*"; 25931b5e1cf6SJoe Perches my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)"; 25941b5e1cf6SJoe Perches my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*"; 25951b5e1cf6SJoe Perches my $mailbox = "(?:$addr_spec|$phrase$route_addr)"; 25961b5e1cf6SJoe Perches 25971b5e1cf6SJoe Perches my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*"; 25981b5e1cf6SJoe Perches my $address = "(?:$mailbox|$group)"; 25991b5e1cf6SJoe Perches 26001b5e1cf6SJoe Perches return "$rfc822_lwsp*$address"; 26011b5e1cf6SJoe Perches} 26021b5e1cf6SJoe Perches 26031b5e1cf6SJoe Perchessub rfc822_strip_comments { 26041b5e1cf6SJoe Perches my $s = shift; 26051b5e1cf6SJoe Perches# Recursively remove comments, and replace with a single space. The simpler 26061b5e1cf6SJoe Perches# regexps in the Email Addressing FAQ are imperfect - they will miss escaped 26071b5e1cf6SJoe Perches# chars in atoms, for example. 26081b5e1cf6SJoe Perches 26091b5e1cf6SJoe Perches while ($s =~ s/^((?:[^"\\]|\\.)* 26101b5e1cf6SJoe Perches (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*) 26111b5e1cf6SJoe Perches \((?:[^()\\]|\\.)*\)/$1 /osx) {} 26121b5e1cf6SJoe Perches return $s; 26131b5e1cf6SJoe Perches} 26141b5e1cf6SJoe Perches 26151b5e1cf6SJoe Perches# valid: returns true if the parameter is an RFC822 valid address 26161b5e1cf6SJoe Perches# 261722dd5b0cSStephen Hemmingersub rfc822_valid { 26181b5e1cf6SJoe Perches my $s = rfc822_strip_comments(shift); 26191b5e1cf6SJoe Perches 26201b5e1cf6SJoe Perches if (!$rfc822re) { 26211b5e1cf6SJoe Perches $rfc822re = make_rfc822re(); 26221b5e1cf6SJoe Perches } 26231b5e1cf6SJoe Perches 26241b5e1cf6SJoe Perches return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/; 26251b5e1cf6SJoe Perches} 26261b5e1cf6SJoe Perches 26271b5e1cf6SJoe Perches# validlist: In scalar context, returns true if the parameter is an RFC822 26281b5e1cf6SJoe Perches# valid list of addresses. 26291b5e1cf6SJoe Perches# 26301b5e1cf6SJoe Perches# In list context, returns an empty list on failure (an invalid 26311b5e1cf6SJoe Perches# address was found); otherwise a list whose first element is the 26321b5e1cf6SJoe Perches# number of addresses found and whose remaining elements are the 26331b5e1cf6SJoe Perches# addresses. This is needed to disambiguate failure (invalid) 26341b5e1cf6SJoe Perches# from success with no addresses found, because an empty string is 26351b5e1cf6SJoe Perches# a valid list. 26361b5e1cf6SJoe Perches 263722dd5b0cSStephen Hemmingersub rfc822_validlist { 26381b5e1cf6SJoe Perches my $s = rfc822_strip_comments(shift); 26391b5e1cf6SJoe Perches 26401b5e1cf6SJoe Perches if (!$rfc822re) { 26411b5e1cf6SJoe Perches $rfc822re = make_rfc822re(); 26421b5e1cf6SJoe Perches } 26431b5e1cf6SJoe Perches # * null list items are valid according to the RFC 26441b5e1cf6SJoe Perches # * the '1' business is to aid in distinguishing failure from no results 26451b5e1cf6SJoe Perches 26461b5e1cf6SJoe Perches my @r; 26471b5e1cf6SJoe Perches if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so && 26481b5e1cf6SJoe Perches $s =~ m/^$rfc822_char*$/) { 26491b5e1cf6SJoe Perches while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) { 265060db31acSJoe Perches push(@r, $1); 26511b5e1cf6SJoe Perches } 26521b5e1cf6SJoe Perches return wantarray ? (scalar(@r), @r) : 1; 26531b5e1cf6SJoe Perches } 26541b5e1cf6SJoe Perches return wantarray ? () : 0; 26551b5e1cf6SJoe Perches} 2656