blob: d7016279ec2b1b97ebf2bffd98c32e3836f86671 [file] [log] [blame]
Joe Perchescb7301c2009-04-07 20:40:12 -07001#!/usr/bin/perl -w
2# (c) 2007, Joe Perches <joe@perches.com>
3# created from checkpatch.pl
4#
5# Print selected MAINTAINERS information for
6# the files modified in a patch or for a file
7#
Roel Kluin3bd7bf52009-11-11 14:26:13 -08008# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
9# perl scripts/get_maintainer.pl [OPTIONS] -f <file>
Joe Perchescb7301c2009-04-07 20:40:12 -070010#
11# Licensed under the terms of the GNU GPL License version 2
12
13use strict;
14
15my $P = $0;
Joe Perches7e1863a2011-01-12 16:59:49 -080016my $V = '0.26';
Joe Perchescb7301c2009-04-07 20:40:12 -070017
18use Getopt::Long qw(:config no_auto_abbrev);
19
20my $lk_path = "./";
21my $email = 1;
22my $email_usename = 1;
23my $email_maintainer = 1;
Joe Perchesc1c3f2c2014-06-02 12:05:17 -070024my $email_reviewer = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070025my $email_list = 1;
26my $email_subscriber_list = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070027my $email_git_penguin_chiefs = 0;
Joe Perchese3e9d112010-10-26 14:22:53 -070028my $email_git = 0;
Florian Mickler0fa05592010-05-24 14:33:20 -070029my $email_git_all_signature_types = 0;
Joe Perches60db31a2009-12-14 18:00:50 -080030my $email_git_blame = 0;
Joe Perches683c6f82010-10-26 14:22:55 -070031my $email_git_blame_signatures = 1;
Joe Perchese3e9d112010-10-26 14:22:53 -070032my $email_git_fallback = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070033my $email_git_min_signatures = 1;
34my $email_git_max_maintainers = 5;
Joe Perchesafa81ee2009-07-29 15:04:28 -070035my $email_git_min_percent = 5;
Joe Perchescb7301c2009-04-07 20:40:12 -070036my $email_git_since = "1-year-ago";
Joe Perches60db31a2009-12-14 18:00:50 -080037my $email_hg_since = "-365";
Florian Micklerdace8e32010-10-26 14:22:54 -070038my $interactive = 0;
Joe Perches11ecf532009-09-21 17:04:22 -070039my $email_remove_duplicates = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -070040my $email_use_mailmap = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070041my $output_multiline = 1;
42my $output_separator = ", ";
Joe Perches3c7385b2009-12-14 18:00:46 -080043my $output_roles = 0;
Joe Perches7e1863a2011-01-12 16:59:49 -080044my $output_rolestats = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070045my $scm = 0;
46my $web = 0;
47my $subsystem = 0;
48my $status = 0;
Joe Perchesdcf36a92009-10-26 16:49:47 -070049my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080050my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080051my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070052my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070053my $pattern_depth = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070054my $version = 0;
55my $help = 0;
56
Joe Perches683c6f82010-10-26 14:22:55 -070057my $vcs_used = 0;
58
Joe Perchescb7301c2009-04-07 20:40:12 -070059my $exit = 0;
60
Joe Perches683c6f82010-10-26 14:22:55 -070061my %commit_author_hash;
62my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070063
Joe Perchescb7301c2009-04-07 20:40:12 -070064my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070065push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070066#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070067#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070068
69my @penguin_chief_names = ();
70foreach my $chief (@penguin_chief) {
71 if ($chief =~ m/^(.*):(.*)/) {
72 my $chief_name = $1;
73 my $chief_addr = $2;
74 push(@penguin_chief_names, $chief_name);
75 }
76}
Joe Perchese4d26b02010-05-24 14:33:17 -070077my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
78
79# Signature types of people who are either
80# a) responsible for the code in question, or
81# b) familiar enough with it to give relevant feedback
82my @signature_tags = ();
83push(@signature_tags, "Signed-off-by:");
84push(@signature_tags, "Reviewed-by:");
85push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070086
Joe Perches7dea2682012-06-20 12:53:02 -070087my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
88
Joe Perches5f2441e2009-06-16 15:34:02 -070089# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070090my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070091my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070092
Joe Perches60db31a2009-12-14 18:00:50 -080093# VCS command support: class-like functions and strings
94
95my %VCS_cmds;
96
97my %VCS_cmds_git = (
98 "execute_cmd" => \&git_execute_cmd,
Richard Genoudec83b612014-02-10 14:25:31 -080099 "available" => '(which("git") ne "") && (-e ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -0700100 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800101 "git log --no-color --follow --since=\$email_git_since " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800102 '--numstat --no-merges ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700103 '--format="GitCommit: %H%n' .
104 'GitAuthor: %an <%ae>%n' .
105 'GitDate: %aD%n' .
106 'GitSubject: %s%n' .
107 '%b%n"' .
108 " -- \$file",
109 "find_commit_signers_cmd" =>
110 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800111 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700112 '--format="GitCommit: %H%n' .
113 'GitAuthor: %an <%ae>%n' .
114 'GitDate: %aD%n' .
115 'GitSubject: %s%n' .
116 '%b%n"' .
117 " -1 \$commit",
118 "find_commit_author_cmd" =>
119 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800120 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700121 '--format="GitCommit: %H%n' .
122 'GitAuthor: %an <%ae>%n' .
123 'GitDate: %aD%n' .
124 'GitSubject: %s%n"' .
125 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800126 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
127 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700128 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700129 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700130 "author_pattern" => "^GitAuthor: (.*)",
131 "subject_pattern" => "^GitSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800132 "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
Joe Perches60db31a2009-12-14 18:00:50 -0800133);
134
135my %VCS_cmds_hg = (
136 "execute_cmd" => \&hg_execute_cmd,
137 "available" => '(which("hg") ne "") && (-d ".hg")',
138 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700139 "hg log --date=\$email_hg_since " .
140 "--template='HgCommit: {node}\\n" .
141 "HgAuthor: {author}\\n" .
142 "HgSubject: {desc}\\n'" .
143 " -- \$file",
144 "find_commit_signers_cmd" =>
145 "hg log " .
146 "--template='HgSubject: {desc}\\n'" .
147 " -r \$commit",
148 "find_commit_author_cmd" =>
149 "hg log " .
150 "--template='HgCommit: {node}\\n" .
151 "HgAuthor: {author}\\n" .
152 "HgSubject: {desc|firstline}\\n'" .
153 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800154 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700155 "blame_file_cmd" => "hg blame -n \$file",
156 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
157 "blame_commit_pattern" => "^([ 0-9a-f]+):",
158 "author_pattern" => "^HgAuthor: (.*)",
159 "subject_pattern" => "^HgSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800160 "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
Joe Perches60db31a2009-12-14 18:00:50 -0800161);
162
Joe Perchesbcde44e2010-10-26 14:22:53 -0700163my $conf = which_conf(".get_maintainer.conf");
164if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700165 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700166 open(my $conffile, '<', "$conf")
167 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
168
Joe Perches368669d2010-05-24 14:33:19 -0700169 while (<$conffile>) {
170 my $line = $_;
171
172 $line =~ s/\s*\n?$//g;
173 $line =~ s/^\s*//g;
174 $line =~ s/\s+/ /g;
175
176 next if ($line =~ m/^\s*#/);
177 next if ($line =~ m/^\s*$/);
178
179 my @words = split(" ", $line);
180 foreach my $word (@words) {
181 last if ($word =~ m/^#/);
182 push (@conf_args, $word);
183 }
184 }
185 close($conffile);
186 unshift(@ARGV, @conf_args) if @conf_args;
187}
188
Joe Perchescb7301c2009-04-07 20:40:12 -0700189if (!GetOptions(
190 'email!' => \$email,
191 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700192 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800193 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700194 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700195 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700196 'git-chief-penguins!' => \$email_git_penguin_chiefs,
197 'git-min-signatures=i' => \$email_git_min_signatures,
198 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700199 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700200 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800201 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700202 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700203 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700204 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700205 'm!' => \$email_maintainer,
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700206 'r!' => \$email_reviewer,
Joe Perchescb7301c2009-04-07 20:40:12 -0700207 'n!' => \$email_usename,
208 'l!' => \$email_list,
209 's!' => \$email_subscriber_list,
210 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800211 'roles!' => \$output_roles,
212 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700213 'separator=s' => \$output_separator,
214 'subsystem!' => \$subsystem,
215 'status!' => \$status,
216 'scm!' => \$scm,
217 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700218 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700219 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800220 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800221 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700222 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700223 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800224 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700225 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800226 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700227}
228
229if ($help != 0) {
230 usage();
231 exit 0;
232}
233
234if ($version != 0) {
235 print("${P} ${V}\n");
236 exit 0;
237}
238
Joe Perches64f77f32010-03-05 13:43:04 -0800239if (-t STDIN && !@ARGV) {
240 # We're talking to a terminal, but have no command line arguments.
241 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700242}
243
Joe Perches683c6f82010-10-26 14:22:55 -0700244$output_multiline = 0 if ($output_separator ne ", ");
245$output_rolestats = 1 if ($interactive);
246$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800247
Joe Perches4b76c9d2010-03-05 13:43:03 -0800248if ($sections) {
249 $email = 0;
250 $email_list = 0;
251 $scm = 0;
252 $status = 0;
253 $subsystem = 0;
254 $web = 0;
255 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700256 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800257} else {
258 my $selections = $email + $scm + $status + $subsystem + $web;
259 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800260 die "$P: Missing required option: email, scm, status, subsystem or web\n";
261 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700262}
263
Joe Perchesf5492662009-09-21 17:04:13 -0700264if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700265 ($email_maintainer + $email_reviewer +
266 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700267 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700268 die "$P: Please select at least 1 email option\n";
269}
270
271if (!top_of_kernel_tree($lk_path)) {
272 die "$P: The current directory does not appear to be "
273 . "a linux kernel source tree.\n";
274}
275
276## Read MAINTAINERS for type/value pairs
277
278my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700279my %keyword_hash;
280
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800281open (my $maint, '<', "${lk_path}MAINTAINERS")
282 or die "$P: Can't open MAINTAINERS: $!\n";
283while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700284 my $line = $_;
285
286 if ($line =~ m/^(\C):\s*(.*)/) {
287 my $type = $1;
288 my $value = $2;
289
290 ##Filename pattern matching
291 if ($type eq "F" || $type eq "X") {
292 $value =~ s@\.@\\\.@g; ##Convert . to \.
293 $value =~ s/\*/\.\*/g; ##Convert * to .*
294 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700295 ##if pattern is a directory and it lacks a trailing slash, add one
296 if ((-d $value)) {
297 $value =~ s@([^/])$@$1/@;
298 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700299 } elsif ($type eq "K") {
300 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700301 }
302 push(@typevalue, "$type:$value");
303 } elsif (!/^(\s)*$/) {
304 $line =~ s/\n$//g;
305 push(@typevalue, $line);
306 }
307}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800308close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700309
Joe Perches8cbb3a72009-09-21 17:04:21 -0700310
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700311#
312# Read mail address map
313#
314
Joe Perchesb9e23312010-10-26 14:22:58 -0700315my $mailmap;
316
317read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700318
319sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700320 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700321 names => {},
322 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700323 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700324
Joe Perchesb9e23312010-10-26 14:22:58 -0700325 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700326
327 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800328 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700329
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700330 while (<$mailmap_file>) {
331 s/#.*$//; #strip comments
332 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700333
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700334 next if (/^\s*$/); #skip empty lines
335 #entries have one of the following formats:
336 # name1 <mail1>
337 # <mail1> <mail2>
338 # name1 <mail1> <mail2>
339 # name1 <mail1> name2 <mail2>
340 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700341
342 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700343 my $real_name = $1;
344 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700345
Joe Perches47abc722010-10-26 14:22:57 -0700346 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700347 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700348 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700349
Joe Perches0334b382011-07-25 17:13:13 -0700350 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700351 my $real_address = $1;
352 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700353
Joe Perches47abc722010-10-26 14:22:57 -0700354 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700355
Joe Perches0334b382011-07-25 17:13:13 -0700356 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700357 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700358 my $real_address = $2;
359 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700360
Joe Perches47abc722010-10-26 14:22:57 -0700361 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700362 ($real_name, $real_address) =
363 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700364 $mailmap->{names}->{$wrong_address} = $real_name;
365 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700366
Joe Perches0334b382011-07-25 17:13:13 -0700367 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700368 my $real_name = $1;
369 my $real_address = $2;
370 my $wrong_name = $3;
371 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700372
Joe Perches47abc722010-10-26 14:22:57 -0700373 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700374 ($real_name, $real_address) =
375 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700376
Joe Perchesb9e23312010-10-26 14:22:58 -0700377 $wrong_name =~ s/\s+$//;
378 ($wrong_name, $wrong_address) =
379 parse_email("$wrong_name <$wrong_address>");
380
381 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
382 $mailmap->{names}->{$wrong_email} = $real_name;
383 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700384 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700385 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700386 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700387}
388
Joe Perches4a7fdb52009-04-10 12:28:57 -0700389## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700390
391my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700392my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700393my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800394my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700395
Joe Perches64f77f32010-03-05 13:43:04 -0800396if (!@ARGV) {
397 push(@ARGV, "&STDIN");
398}
399
Joe Perches4a7fdb52009-04-10 12:28:57 -0700400foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800401 if ($file ne "&STDIN") {
402 ##if $file is a directory and it lacks a trailing slash, add one
403 if ((-d $file)) {
404 $file =~ s@([^/])$@$1/@;
405 } elsif (!(-f $file)) {
406 die "$P: file '${file}' not found\n";
407 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700408 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700409 if ($from_filename) {
410 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700411 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800412 open(my $f, '<', $file)
413 or die "$P: Can't open $file: $!\n";
414 my $text = do { local($/) ; <$f> };
415 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800416 if ($keywords) {
417 foreach my $line (keys %keyword_hash) {
418 if ($text =~ m/$keyword_hash{$line}/x) {
419 push(@keyword_tvi, $line);
420 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700421 }
422 }
Joe Perches03372db2010-03-05 13:43:00 -0800423 if ($file_emails) {
424 my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g;
425 push(@file_emails, clean_file_emails(@poss_addr));
426 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700427 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700428 } else {
429 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700430 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800431
Wolfram Sang3a4df132010-03-23 13:35:18 -0700432 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800433 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700434
435 # We can check arbitrary information before the patch
436 # like the commit message, mail headers, etc...
437 # This allows us to match arbitrary keywords against any part
438 # of a git format-patch generated file (subject tags, etc...)
439
440 my $patch_prefix = ""; #Parsing the intro
441
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800442 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700443 my $patch_line = $_;
Geert Uytterhoeven6be07102013-02-21 16:43:12 -0800444 if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700445 my $filename = $1;
446 $filename =~ s@^[^/]*/@@;
447 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700448 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700449 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700450 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700451 } elsif (m/^\@\@ -(\d+),(\d+)/) {
452 if ($email_git_blame) {
453 push(@range, "$lastfile:$1:$2");
454 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700455 } elsif ($keywords) {
456 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700457 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700458 push(@keyword_tvi, $line);
459 }
460 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700461 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700462 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800463 close($patch);
464
Joe Perches4a7fdb52009-04-10 12:28:57 -0700465 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700466 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700467 . "Add -f to options?\n";
468 }
469 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700470 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700471}
472
Joe Perches03372db2010-03-05 13:43:00 -0800473@file_emails = uniq(@file_emails);
474
Joe Perches683c6f82010-10-26 14:22:55 -0700475my %email_hash_name;
476my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700477my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700478my %hash_list_to;
Joe Perches290603c12009-06-16 15:33:58 -0700479my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700480my @scm = ();
481my @web = ();
482my @subsystem = ();
483my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700484my %deduplicate_name_hash = ();
485my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700486
Joe Perches6ef1c522010-10-26 14:22:56 -0700487my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700488
Joe Perches6ef1c522010-10-26 14:22:56 -0700489if (@maintainers) {
490 @maintainers = merge_email(@maintainers);
491 output(@maintainers);
492}
Joe Perchescb7301c2009-04-07 20:40:12 -0700493
494if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700495 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700496 output(@scm);
497}
Joe Perches683c6f82010-10-26 14:22:55 -0700498
Joe Perchescb7301c2009-04-07 20:40:12 -0700499if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700500 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700501 output(@status);
502}
503
504if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700505 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700506 output(@subsystem);
507}
508
509if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700510 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700511 output(@web);
512}
513
514exit($exit);
515
Joe Perchesab6c9372011-01-12 16:59:50 -0800516sub range_is_maintained {
517 my ($start, $end) = @_;
518
519 for (my $i = $start; $i < $end; $i++) {
520 my $line = $typevalue[$i];
521 if ($line =~ m/^(\C):\s*(.*)/) {
522 my $type = $1;
523 my $value = $2;
524 if ($type eq 'S') {
525 if ($value =~ /(maintain|support)/i) {
526 return 1;
527 }
528 }
529 }
530 }
531 return 0;
532}
533
534sub range_has_maintainer {
535 my ($start, $end) = @_;
536
537 for (my $i = $start; $i < $end; $i++) {
538 my $line = $typevalue[$i];
539 if ($line =~ m/^(\C):\s*(.*)/) {
540 my $type = $1;
541 my $value = $2;
542 if ($type eq 'M') {
543 return 1;
544 }
545 }
546 }
547 return 0;
548}
549
Joe Perches6ef1c522010-10-26 14:22:56 -0700550sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700551 %email_hash_name = ();
552 %email_hash_address = ();
553 %commit_author_hash = ();
554 %commit_signer_hash = ();
555 @email_to = ();
556 %hash_list_to = ();
557 @list_to = ();
558 @scm = ();
559 @web = ();
560 @subsystem = ();
561 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700562 %deduplicate_name_hash = ();
563 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700564 if ($email_git_all_signature_types) {
565 $signature_pattern = "(.+?)[Bb][Yy]:";
566 } else {
567 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
568 }
569
570 # Find responsible parties
571
Joe Perchesb9e23312010-10-26 14:22:58 -0700572 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700573
Joe Perches683c6f82010-10-26 14:22:55 -0700574 foreach my $file (@files) {
575
576 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700577 my $tvi = find_first_section();
578 while ($tvi < @typevalue) {
579 my $start = find_starting_index($tvi);
580 my $end = find_ending_index($tvi);
581 my $exclude = 0;
582 my $i;
583
584 #Do not match excluded file patterns
585
586 for ($i = $start; $i < $end; $i++) {
587 my $line = $typevalue[$i];
588 if ($line =~ m/^(\C):\s*(.*)/) {
589 my $type = $1;
590 my $value = $2;
591 if ($type eq 'X') {
592 if (file_match_pattern($file, $value)) {
593 $exclude = 1;
594 last;
595 }
596 }
597 }
598 }
599
600 if (!$exclude) {
601 for ($i = $start; $i < $end; $i++) {
602 my $line = $typevalue[$i];
603 if ($line =~ m/^(\C):\s*(.*)/) {
604 my $type = $1;
605 my $value = $2;
606 if ($type eq 'F') {
607 if (file_match_pattern($file, $value)) {
608 my $value_pd = ($value =~ tr@/@@);
609 my $file_pd = ($file =~ tr@/@@);
610 $value_pd++ if (substr($value,-1,1) ne "/");
611 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800612 if ($value_pd >= $file_pd &&
613 range_is_maintained($start, $end) &&
614 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700615 $exact_pattern_match_hash{$file} = 1;
616 }
Joe Perches683c6f82010-10-26 14:22:55 -0700617 if ($pattern_depth == 0 ||
618 (($file_pd - $value_pd) < $pattern_depth)) {
619 $hash{$tvi} = $value_pd;
620 }
621 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700622 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800623 if ($file =~ m/$value/x) {
624 $hash{$tvi} = 0;
625 }
Joe Perches683c6f82010-10-26 14:22:55 -0700626 }
627 }
628 }
629 }
630 $tvi = $end + 1;
631 }
632
633 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
634 add_categories($line);
635 if ($sections) {
636 my $i;
637 my $start = find_starting_index($line);
638 my $end = find_ending_index($line);
639 for ($i = $start; $i < $end; $i++) {
640 my $line = $typevalue[$i];
641 if ($line =~ /^[FX]:/) { ##Restore file patterns
642 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
643 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
644 $line =~ s/\\\./\./g; ##Convert \. to .
645 $line =~ s/\.\*/\*/g; ##Convert .* to *
646 }
647 $line =~ s/^([A-Z]):/$1:\t/g;
648 print("$line\n");
649 }
650 print("\n");
651 }
652 }
Joe Perches683c6f82010-10-26 14:22:55 -0700653 }
654
655 if ($keywords) {
656 @keyword_tvi = sort_and_uniq(@keyword_tvi);
657 foreach my $line (@keyword_tvi) {
658 add_categories($line);
659 }
660 }
661
Joe Perchesb9e23312010-10-26 14:22:58 -0700662 foreach my $email (@email_to, @list_to) {
663 $email->[0] = deduplicate_email($email->[0]);
664 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700665
666 foreach my $file (@files) {
667 if ($email &&
668 ($email_git || ($email_git_fallback &&
669 !$exact_pattern_match_hash{$file}))) {
670 vcs_file_signoffs($file);
671 }
672 if ($email && $email_git_blame) {
673 vcs_file_blame($file);
674 }
675 }
676
Joe Perches683c6f82010-10-26 14:22:55 -0700677 if ($email) {
678 foreach my $chief (@penguin_chief) {
679 if ($chief =~ m/^(.*):(.*)/) {
680 my $email_address;
681
682 $email_address = format_email($1, $2, $email_usename);
683 if ($email_git_penguin_chiefs) {
684 push(@email_to, [$email_address, 'chief penguin']);
685 } else {
686 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
687 }
688 }
689 }
690
691 foreach my $email (@file_emails) {
692 my ($name, $address) = parse_email($email);
693
694 my $tmp_email = format_email($name, $address, $email_usename);
695 push_email_address($tmp_email, '');
696 add_role($tmp_email, 'in file');
697 }
698 }
699
700 my @to = ();
701 if ($email || $email_list) {
702 if ($email) {
703 @to = (@to, @email_to);
704 }
705 if ($email_list) {
706 @to = (@to, @list_to);
707 }
708 }
709
Joe Perches6ef1c522010-10-26 14:22:56 -0700710 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700711 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700712 }
Joe Perches683c6f82010-10-26 14:22:55 -0700713
714 return @to;
715}
716
Joe Perchescb7301c2009-04-07 20:40:12 -0700717sub file_match_pattern {
718 my ($file, $pattern) = @_;
719 if (substr($pattern, -1) eq "/") {
720 if ($file =~ m@^$pattern@) {
721 return 1;
722 }
723 } else {
724 if ($file =~ m@^$pattern@) {
725 my $s1 = ($file =~ tr@/@@);
726 my $s2 = ($pattern =~ tr@/@@);
727 if ($s1 == $s2) {
728 return 1;
729 }
730 }
731 }
732 return 0;
733}
734
735sub usage {
736 print <<EOT;
737usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700738 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700739version: $V
740
741MAINTAINER field selection options:
742 --email => print email address(es) if any
743 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700744 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700745 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700746 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700747 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700748 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
749 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
750 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700751 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700752 --git-since => git history to use (default: $email_git_since)
753 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700754 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700755 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700756 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -0700757 --n => include name 'Full Name <addr\@domain.tld>'
758 --l => include list(s) if any
759 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700760 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800761 --roles => show roles (status:subsystem, git-signer, list, etc...)
762 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800763 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700764 --scm => print SCM tree(s) if any
765 --status => print status if any
766 --subsystem => print subsystem name if any
767 --web => print website(s) if any
768
769Output type options:
770 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700771 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700772 --multiline => print 1 entry per line
773
Joe Perchescb7301c2009-04-07 20:40:12 -0700774Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700775 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700776 --keywords => scan patch for keywords (default: $keywords)
777 --sections => print all of the subsystem sections with pattern matches
778 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700779 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700780 --help => show this help information
781
Joe Perches3fb55652009-09-21 17:04:17 -0700782Default options:
Joe Perches7e1863a2011-01-12 16:59:49 -0800783 [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
784 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700785
Joe Perches870020f2009-07-29 15:04:28 -0700786Notes:
787 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700788 Used with "--git", git signators for _all_ files in and below
789 directory are examined as git recurses directories.
790 Any specified X: (exclude) pattern matches are _not_ ignored.
791 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800792 no individual file within the directory or subdirectory
793 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700794 Used with "--git-blame", does not iterate all files in directory
795 Using "--git-blame" is slow and may add old committers and authors
796 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800797 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
798 other automated tools that expect only ["name"] <email address>
799 may not work because of additional output after <email address>.
800 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
801 not the percentage of the entire file authored. # of commits is
802 not a good measure of amount of code authored. 1 major commit may
803 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800804 If git is not installed, but mercurial (hg) is installed and an .hg
805 repository exists, the following options apply to mercurial:
806 --git,
807 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
808 --git-blame
809 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700810 File ".get_maintainer.conf", if it exists in the linux kernel source root
811 directory, can change whatever get_maintainer defaults are desired.
812 Entries in this file can be any command line argument.
813 This file is prepended to any additional command line arguments.
814 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700815EOT
816}
817
818sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700819 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700820
Joe Perches47abc722010-10-26 14:22:57 -0700821 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
822 $lk_path .= "/";
823 }
824 if ( (-f "${lk_path}COPYING")
825 && (-f "${lk_path}CREDITS")
826 && (-f "${lk_path}Kbuild")
827 && (-f "${lk_path}MAINTAINERS")
828 && (-f "${lk_path}Makefile")
829 && (-f "${lk_path}README")
830 && (-d "${lk_path}Documentation")
831 && (-d "${lk_path}arch")
832 && (-d "${lk_path}include")
833 && (-d "${lk_path}drivers")
834 && (-d "${lk_path}fs")
835 && (-d "${lk_path}init")
836 && (-d "${lk_path}ipc")
837 && (-d "${lk_path}kernel")
838 && (-d "${lk_path}lib")
839 && (-d "${lk_path}scripts")) {
840 return 1;
841 }
842 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700843}
844
Joe Perches0e70e832009-09-21 17:04:20 -0700845sub parse_email {
846 my ($formatted_email) = @_;
847
848 my $name = "";
849 my $address = "";
850
Joe Perches11ecf532009-09-21 17:04:22 -0700851 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700852 $name = $1;
853 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700854 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700855 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700856 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700857 $address = $1;
858 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700859
860 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700861 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700862 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700863
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800864 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700865 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700866 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700867 }
Joe Perches0e70e832009-09-21 17:04:20 -0700868
869 return ($name, $address);
870}
871
872sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800873 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700874
875 my $formatted_email;
876
877 $name =~ s/^\s+|\s+$//g;
878 $name =~ s/^\"|\"$//g;
879 $address =~ s/^\s+|\s+$//g;
880
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800881 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700882 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
883 $name = "\"$name\"";
884 }
885
Joe Perchesa8af2432009-12-14 18:00:49 -0800886 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700887 if ("$name" eq "") {
888 $formatted_email = "$address";
889 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800890 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700891 }
892 } else {
893 $formatted_email = $address;
894 }
895
Joe Perchescb7301c2009-04-07 20:40:12 -0700896 return $formatted_email;
897}
898
Joe Perches272a8972010-01-08 14:42:48 -0800899sub find_first_section {
900 my $index = 0;
901
902 while ($index < @typevalue) {
903 my $tv = $typevalue[$index];
904 if (($tv =~ m/^(\C):\s*(.*)/)) {
905 last;
906 }
907 $index++;
908 }
909
910 return $index;
911}
912
Joe Perchesb7816552009-09-21 17:04:24 -0700913sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700914 my ($index) = @_;
915
916 while ($index > 0) {
917 my $tv = $typevalue[$index];
918 if (!($tv =~ m/^(\C):\s*(.*)/)) {
919 last;
920 }
921 $index--;
922 }
923
924 return $index;
925}
926
927sub find_ending_index {
928 my ($index) = @_;
929
930 while ($index < @typevalue) {
931 my $tv = $typevalue[$index];
932 if (!($tv =~ m/^(\C):\s*(.*)/)) {
933 last;
934 }
935 $index++;
936 }
937
938 return $index;
939}
940
Joe Perches3c7385b2009-12-14 18:00:46 -0800941sub get_maintainer_role {
942 my ($index) = @_;
943
944 my $i;
945 my $start = find_starting_index($index);
946 my $end = find_ending_index($index);
947
Joe Perches0ede2742012-03-23 15:01:56 -0700948 my $role = "unknown";
Joe Perches3c7385b2009-12-14 18:00:46 -0800949 my $subsystem = $typevalue[$start];
950 if (length($subsystem) > 20) {
951 $subsystem = substr($subsystem, 0, 17);
952 $subsystem =~ s/\s*$//;
953 $subsystem = $subsystem . "...";
954 }
955
956 for ($i = $start + 1; $i < $end; $i++) {
957 my $tv = $typevalue[$i];
958 if ($tv =~ m/^(\C):\s*(.*)/) {
959 my $ptype = $1;
960 my $pvalue = $2;
961 if ($ptype eq "S") {
962 $role = $pvalue;
963 }
964 }
965 }
966
967 $role = lc($role);
968 if ($role eq "supported") {
969 $role = "supporter";
970 } elsif ($role eq "maintained") {
971 $role = "maintainer";
972 } elsif ($role eq "odd fixes") {
973 $role = "odd fixer";
974 } elsif ($role eq "orphan") {
975 $role = "orphan minder";
976 } elsif ($role eq "obsolete") {
977 $role = "obsolete minder";
978 } elsif ($role eq "buried alive in reporters") {
979 $role = "chief penguin";
980 }
981
982 return $role . ":" . $subsystem;
983}
984
985sub get_list_role {
986 my ($index) = @_;
987
988 my $i;
989 my $start = find_starting_index($index);
990 my $end = find_ending_index($index);
991
992 my $subsystem = $typevalue[$start];
993 if (length($subsystem) > 20) {
994 $subsystem = substr($subsystem, 0, 17);
995 $subsystem =~ s/\s*$//;
996 $subsystem = $subsystem . "...";
997 }
998
999 if ($subsystem eq "THE REST") {
1000 $subsystem = "";
1001 }
1002
1003 return $subsystem;
1004}
1005
Joe Perchescb7301c2009-04-07 20:40:12 -07001006sub add_categories {
1007 my ($index) = @_;
1008
Joe Perchesb7816552009-09-21 17:04:24 -07001009 my $i;
1010 my $start = find_starting_index($index);
1011 my $end = find_ending_index($index);
1012
1013 push(@subsystem, $typevalue[$start]);
1014
1015 for ($i = $start + 1; $i < $end; $i++) {
1016 my $tv = $typevalue[$i];
Joe Perches290603c12009-06-16 15:33:58 -07001017 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001018 my $ptype = $1;
1019 my $pvalue = $2;
1020 if ($ptype eq "L") {
Joe Perches290603c12009-06-16 15:33:58 -07001021 my $list_address = $pvalue;
1022 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001023 my $list_role = get_list_role($i);
1024
1025 if ($list_role ne "") {
1026 $list_role = ":" . $list_role;
1027 }
Joe Perches290603c12009-06-16 15:33:58 -07001028 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1029 $list_address = $1;
1030 $list_additional = $2;
1031 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001032 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001033 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001034 if (!$hash_list_to{lc($list_address)}) {
1035 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001036 push(@list_to, [$list_address,
1037 "subscriber list${list_role}"]);
1038 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001039 }
1040 } else {
1041 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001042 if (!$hash_list_to{lc($list_address)}) {
1043 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001044 if ($list_additional =~ m/moderated/) {
1045 push(@list_to, [$list_address,
1046 "moderated list${list_role}"]);
1047 } else {
1048 push(@list_to, [$list_address,
1049 "open list${list_role}"]);
1050 }
Joe Perches683c6f82010-10-26 14:22:55 -07001051 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001052 }
1053 }
1054 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001055 my ($name, $address) = parse_email($pvalue);
1056 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001057 if ($i > 0) {
1058 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -07001059 if ($tv =~ m/^(\C):\s*(.*)/) {
1060 if ($1 eq "P") {
1061 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001062 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001063 }
1064 }
1065 }
1066 }
Joe Perches0e70e832009-09-21 17:04:20 -07001067 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001068 my $role = get_maintainer_role($i);
1069 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001070 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001071 } elsif ($ptype eq "R") {
1072 my ($name, $address) = parse_email($pvalue);
1073 if ($name eq "") {
1074 if ($i > 0) {
1075 my $tv = $typevalue[$i - 1];
1076 if ($tv =~ m/^(\C):\s*(.*)/) {
1077 if ($1 eq "P") {
1078 $name = $2;
1079 $pvalue = format_email($name, $address, $email_usename);
1080 }
1081 }
1082 }
1083 }
1084 if ($email_reviewer) {
1085 push_email_addresses($pvalue, 'reviewer');
1086 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001087 } elsif ($ptype eq "T") {
1088 push(@scm, $pvalue);
1089 } elsif ($ptype eq "W") {
1090 push(@web, $pvalue);
1091 } elsif ($ptype eq "S") {
1092 push(@status, $pvalue);
1093 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001094 }
1095 }
1096}
1097
Joe Perches11ecf532009-09-21 17:04:22 -07001098sub email_inuse {
1099 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001100
Joe Perches11ecf532009-09-21 17:04:22 -07001101 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001102 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1103 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001104
Joe Perches0e70e832009-09-21 17:04:20 -07001105 return 0;
1106}
1107
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001108sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001109 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001110
Joe Perches0e70e832009-09-21 17:04:20 -07001111 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001112
Joe Perchesb7816552009-09-21 17:04:24 -07001113 if ($address eq "") {
1114 return 0;
1115 }
1116
Joe Perches11ecf532009-09-21 17:04:22 -07001117 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001118 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001119 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001120 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001121 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001122 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001123 }
Joe Perchesb7816552009-09-21 17:04:24 -07001124
1125 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001126}
1127
1128sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001129 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001130
1131 my @address_list = ();
1132
Joe Perches5f2441e2009-06-16 15:34:02 -07001133 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001134 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001135 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001136 my $array_count = shift(@address_list);
1137 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001138 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001139 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001140 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001141 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001142 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1143 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001144 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001145}
1146
Joe Perches3c7385b2009-12-14 18:00:46 -08001147sub add_role {
1148 my ($line, $role) = @_;
1149
1150 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001151 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001152
1153 foreach my $entry (@email_to) {
1154 if ($email_remove_duplicates) {
1155 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001156 if (($name eq $entry_name || $address eq $entry_address)
1157 && ($role eq "" || !($entry->[1] =~ m/$role/))
1158 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001159 if ($entry->[1] eq "") {
1160 $entry->[1] = "$role";
1161 } else {
1162 $entry->[1] = "$entry->[1],$role";
1163 }
1164 }
1165 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001166 if ($email eq $entry->[0]
1167 && ($role eq "" || !($entry->[1] =~ m/$role/))
1168 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001169 if ($entry->[1] eq "") {
1170 $entry->[1] = "$role";
1171 } else {
1172 $entry->[1] = "$entry->[1],$role";
1173 }
1174 }
1175 }
1176 }
1177}
1178
Joe Perchescb7301c2009-04-07 20:40:12 -07001179sub which {
1180 my ($bin) = @_;
1181
Joe Perchesf5f50782009-06-16 15:34:00 -07001182 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001183 if (-e "$path/$bin") {
1184 return "$path/$bin";
1185 }
1186 }
1187
1188 return "";
1189}
1190
Joe Perchesbcde44e2010-10-26 14:22:53 -07001191sub which_conf {
1192 my ($conf) = @_;
1193
1194 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1195 if (-e "$path/$conf") {
1196 return "$path/$conf";
1197 }
1198 }
1199
1200 return "";
1201}
1202
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001203sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001204 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001205
Joe Perches47abc722010-10-26 14:22:57 -07001206 my ($name, $address) = parse_email($line);
1207 my $email = format_email($name, $address, 1);
1208 my $real_name = $name;
1209 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001210
Joe Perches47abc722010-10-26 14:22:57 -07001211 if (exists $mailmap->{names}->{$email} ||
1212 exists $mailmap->{addresses}->{$email}) {
1213 if (exists $mailmap->{names}->{$email}) {
1214 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001215 }
Joe Perches47abc722010-10-26 14:22:57 -07001216 if (exists $mailmap->{addresses}->{$email}) {
1217 $real_address = $mailmap->{addresses}->{$email};
1218 }
1219 } else {
1220 if (exists $mailmap->{names}->{$address}) {
1221 $real_name = $mailmap->{names}->{$address};
1222 }
1223 if (exists $mailmap->{addresses}->{$address}) {
1224 $real_address = $mailmap->{addresses}->{$address};
1225 }
1226 }
1227 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001228}
1229
1230sub mailmap {
1231 my (@addresses) = @_;
1232
Joe Perchesb9e23312010-10-26 14:22:58 -07001233 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001234 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001235 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001236 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001237 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1238 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001239}
1240
1241sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001242 my %address_map;
1243 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001244
Joe Perches47abc722010-10-26 14:22:57 -07001245 foreach my $email (@emails) {
1246 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001247 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001248 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001249 $email = format_email($name, $address, 1);
1250 } else {
1251 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001252 }
Joe Perches47abc722010-10-26 14:22:57 -07001253 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001254}
1255
Joe Perches60db31a2009-12-14 18:00:50 -08001256sub git_execute_cmd {
1257 my ($cmd) = @_;
1258 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001259
Joe Perches60db31a2009-12-14 18:00:50 -08001260 my $output = `$cmd`;
1261 $output =~ s/^\s*//gm;
1262 @lines = split("\n", $output);
1263
1264 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001265}
1266
Joe Perches60db31a2009-12-14 18:00:50 -08001267sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001268 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001269 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001270
Joe Perches60db31a2009-12-14 18:00:50 -08001271 my $output = `$cmd`;
1272 @lines = split("\n", $output);
1273
1274 return @lines;
1275}
1276
Joe Perches683c6f82010-10-26 14:22:55 -07001277sub extract_formatted_signatures {
1278 my (@signature_lines) = @_;
1279
1280 my @type = @signature_lines;
1281
1282 s/\s*(.*):.*/$1/ for (@type);
1283
1284 # cut -f2- -d":"
1285 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1286
1287## Reformat email addresses (with names) to avoid badly written signatures
1288
1289 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001290 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001291 }
1292
1293 return (\@type, \@signature_lines);
1294}
1295
Joe Perches60db31a2009-12-14 18:00:50 -08001296sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001297 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001298 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001299 my @lines = ();
1300 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001301 my @authors = ();
1302 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001303
Joe Perches60db31a2009-12-14 18:00:50 -08001304 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001305
Joe Perches60db31a2009-12-14 18:00:50 -08001306 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001307 my $author_pattern = $VCS_cmds{"author_pattern"};
1308 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1309
1310 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001311
Joe Perches60db31a2009-12-14 18:00:50 -08001312 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001313
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001314 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001315 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001316 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001317
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001318# print("stats: <@stats>\n");
1319
1320 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001321
1322 save_commits_by_author(@lines) if ($interactive);
1323 save_commits_by_signer(@lines) if ($interactive);
1324
Joe Perches0e70e832009-09-21 17:04:20 -07001325 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001326 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001327 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001328
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001329 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001330 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001331
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001332 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001333}
1334
Joe Perches63ab52d2010-10-26 14:22:51 -07001335sub vcs_find_author {
1336 my ($cmd) = @_;
1337 my @lines = ();
1338
1339 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1340
1341 if (!$email_git_penguin_chiefs) {
1342 @lines = grep(!/${penguin_chiefs}/i, @lines);
1343 }
1344
1345 return @lines if !@lines;
1346
Joe Perches683c6f82010-10-26 14:22:55 -07001347 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001348 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001349 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1350 my $author = $1;
1351 my ($name, $address) = parse_email($author);
1352 $author = format_email($name, $address, 1);
1353 push(@authors, $author);
1354 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001355 }
1356
Joe Perches683c6f82010-10-26 14:22:55 -07001357 save_commits_by_author(@lines) if ($interactive);
1358 save_commits_by_signer(@lines) if ($interactive);
1359
1360 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001361}
1362
Joe Perches60db31a2009-12-14 18:00:50 -08001363sub vcs_save_commits {
1364 my ($cmd) = @_;
1365 my @lines = ();
1366 my @commits = ();
1367
1368 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1369
1370 foreach my $line (@lines) {
1371 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1372 push(@commits, $1);
1373 }
1374 }
1375
1376 return @commits;
1377}
1378
1379sub vcs_blame {
1380 my ($file) = @_;
1381 my $cmd;
1382 my @commits = ();
1383
1384 return @commits if (!(-f $file));
1385
1386 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1387 my @all_commits = ();
1388
1389 $cmd = $VCS_cmds{"blame_file_cmd"};
1390 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1391 @all_commits = vcs_save_commits($cmd);
1392
1393 foreach my $file_range_diff (@range) {
1394 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1395 my $diff_file = $1;
1396 my $diff_start = $2;
1397 my $diff_length = $3;
1398 next if ("$file" ne "$diff_file");
1399 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1400 push(@commits, $all_commits[$i]);
1401 }
1402 }
1403 } elsif (@range) {
1404 foreach my $file_range_diff (@range) {
1405 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1406 my $diff_file = $1;
1407 my $diff_start = $2;
1408 my $diff_length = $3;
1409 next if ("$file" ne "$diff_file");
1410 $cmd = $VCS_cmds{"blame_range_cmd"};
1411 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1412 push(@commits, vcs_save_commits($cmd));
1413 }
1414 } else {
1415 $cmd = $VCS_cmds{"blame_file_cmd"};
1416 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1417 @commits = vcs_save_commits($cmd);
1418 }
1419
Joe Perches63ab52d2010-10-26 14:22:51 -07001420 foreach my $commit (@commits) {
1421 $commit =~ s/^\^//g;
1422 }
1423
Joe Perches60db31a2009-12-14 18:00:50 -08001424 return @commits;
1425}
1426
1427my $printed_novcs = 0;
1428sub vcs_exists {
1429 %VCS_cmds = %VCS_cmds_git;
1430 return 1 if eval $VCS_cmds{"available"};
1431 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001432 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001433 %VCS_cmds = ();
1434 if (!$printed_novcs) {
1435 warn("$P: No supported VCS found. Add --nogit to options?\n");
1436 warn("Using a git repository produces better results.\n");
1437 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001438 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001439 $printed_novcs = 1;
1440 }
1441 return 0;
1442}
1443
Joe Perches683c6f82010-10-26 14:22:55 -07001444sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001445 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001446 return $vcs_used == 1;
1447}
1448
1449sub vcs_is_hg {
1450 return $vcs_used == 2;
1451}
1452
Joe Perches6ef1c522010-10-26 14:22:56 -07001453sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001454 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001455 my @list = @$list_ref;
1456
Joe Perches683c6f82010-10-26 14:22:55 -07001457 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001458
1459 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001460 my %authored;
1461 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001462 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001463 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001464 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001465 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1466 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001467 $authored{$count} = 0;
1468 $signed{$count} = 0;
1469 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001470 }
1471
1472 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001473 my $done = 0;
1474 my $print_options = 0;
1475 my $redraw = 1;
1476 while (!$done) {
1477 $count = 0;
1478 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001479 printf STDERR "\n%1s %2s %-65s",
1480 "*", "#", "email/list and role:stats";
1481 if ($email_git ||
1482 ($email_git_fallback && !$maintained) ||
1483 $email_git_blame) {
1484 print STDERR "auth sign";
1485 }
1486 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001487 foreach my $entry (@list) {
1488 my $email = $entry->[0];
1489 my $role = $entry->[1];
1490 my $sel = "";
1491 $sel = "*" if ($selected{$count});
1492 my $commit_author = $commit_author_hash{$email};
1493 my $commit_signer = $commit_signer_hash{$email};
1494 my $authored = 0;
1495 my $signed = 0;
1496 $authored++ for (@{$commit_author});
1497 $signed++ for (@{$commit_signer});
1498 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1499 printf STDERR "%4d %4d", $authored, $signed
1500 if ($authored > 0 || $signed > 0);
1501 printf STDERR "\n %s\n", $role;
1502 if ($authored{$count}) {
1503 my $commit_author = $commit_author_hash{$email};
1504 foreach my $ref (@{$commit_author}) {
1505 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001506 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001507 }
Joe Perches683c6f82010-10-26 14:22:55 -07001508 if ($signed{$count}) {
1509 my $commit_signer = $commit_signer_hash{$email};
1510 foreach my $ref (@{$commit_signer}) {
1511 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1512 }
1513 }
1514
1515 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001516 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001517 }
Joe Perches683c6f82010-10-26 14:22:55 -07001518 my $date_ref = \$email_git_since;
1519 $date_ref = \$email_hg_since if (vcs_is_hg());
1520 if ($print_options) {
1521 $print_options = 0;
1522 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001523 print STDERR <<EOT
1524
1525Version Control options:
1526g use git history [$email_git]
1527gf use git-fallback [$email_git_fallback]
1528b use git blame [$email_git_blame]
1529bs use blame signatures [$email_git_blame_signatures]
1530c# minimum commits [$email_git_min_signatures]
1531%# min percent [$email_git_min_percent]
1532d# history to use [$$date_ref]
1533x# max maintainers [$email_git_max_maintainers]
1534t all signature types [$email_git_all_signature_types]
1535m use .mailmap [$email_use_mailmap]
1536EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001537 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001538 print STDERR <<EOT
1539
1540Additional options:
15410 toggle all
1542tm toggle maintainers
1543tg toggle git entries
1544tl toggle open list entries
1545ts toggle subscriber list entries
1546f emails in file [$file_emails]
1547k keywords in file [$keywords]
1548r remove duplicates [$email_remove_duplicates]
1549p# pattern match depth [$pattern_depth]
1550EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001551 }
1552 print STDERR
1553"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1554
1555 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001556 chomp($input);
1557
Joe Perches683c6f82010-10-26 14:22:55 -07001558 $redraw = 1;
1559 my $rerun = 0;
1560 my @wish = split(/[, ]+/, $input);
1561 foreach my $nr (@wish) {
1562 $nr = lc($nr);
1563 my $sel = substr($nr, 0, 1);
1564 my $str = substr($nr, 1);
1565 my $val = 0;
1566 $val = $1 if $str =~ /^(\d+)$/;
1567
1568 if ($sel eq "y") {
1569 $interactive = 0;
1570 $done = 1;
1571 $output_rolestats = 0;
1572 $output_roles = 0;
1573 last;
1574 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1575 $selected{$nr - 1} = !$selected{$nr - 1};
1576 } elsif ($sel eq "*" || $sel eq '^') {
1577 my $toggle = 0;
1578 $toggle = 1 if ($sel eq '*');
1579 for (my $i = 0; $i < $count; $i++) {
1580 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001581 }
Joe Perches683c6f82010-10-26 14:22:55 -07001582 } elsif ($sel eq "0") {
1583 for (my $i = 0; $i < $count; $i++) {
1584 $selected{$i} = !$selected{$i};
1585 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001586 } elsif ($sel eq "t") {
1587 if (lc($str) eq "m") {
1588 for (my $i = 0; $i < $count; $i++) {
1589 $selected{$i} = !$selected{$i}
1590 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1591 }
1592 } elsif (lc($str) eq "g") {
1593 for (my $i = 0; $i < $count; $i++) {
1594 $selected{$i} = !$selected{$i}
1595 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1596 }
1597 } elsif (lc($str) eq "l") {
1598 for (my $i = 0; $i < $count; $i++) {
1599 $selected{$i} = !$selected{$i}
1600 if ($list[$i]->[1] =~ /^(open list)/i);
1601 }
1602 } elsif (lc($str) eq "s") {
1603 for (my $i = 0; $i < $count; $i++) {
1604 $selected{$i} = !$selected{$i}
1605 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1606 }
1607 }
Joe Perches683c6f82010-10-26 14:22:55 -07001608 } elsif ($sel eq "a") {
1609 if ($val > 0 && $val <= $count) {
1610 $authored{$val - 1} = !$authored{$val - 1};
1611 } elsif ($str eq '*' || $str eq '^') {
1612 my $toggle = 0;
1613 $toggle = 1 if ($str eq '*');
1614 for (my $i = 0; $i < $count; $i++) {
1615 $authored{$i} = $toggle;
1616 }
1617 }
1618 } elsif ($sel eq "s") {
1619 if ($val > 0 && $val <= $count) {
1620 $signed{$val - 1} = !$signed{$val - 1};
1621 } elsif ($str eq '*' || $str eq '^') {
1622 my $toggle = 0;
1623 $toggle = 1 if ($str eq '*');
1624 for (my $i = 0; $i < $count; $i++) {
1625 $signed{$i} = $toggle;
1626 }
1627 }
1628 } elsif ($sel eq "o") {
1629 $print_options = 1;
1630 $redraw = 1;
1631 } elsif ($sel eq "g") {
1632 if ($str eq "f") {
1633 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001634 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001635 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001636 }
Joe Perches683c6f82010-10-26 14:22:55 -07001637 $rerun = 1;
1638 } elsif ($sel eq "b") {
1639 if ($str eq "s") {
1640 bool_invert(\$email_git_blame_signatures);
1641 } else {
1642 bool_invert(\$email_git_blame);
1643 }
1644 $rerun = 1;
1645 } elsif ($sel eq "c") {
1646 if ($val > 0) {
1647 $email_git_min_signatures = $val;
1648 $rerun = 1;
1649 }
1650 } elsif ($sel eq "x") {
1651 if ($val > 0) {
1652 $email_git_max_maintainers = $val;
1653 $rerun = 1;
1654 }
1655 } elsif ($sel eq "%") {
1656 if ($str ne "" && $val >= 0) {
1657 $email_git_min_percent = $val;
1658 $rerun = 1;
1659 }
1660 } elsif ($sel eq "d") {
1661 if (vcs_is_git()) {
1662 $email_git_since = $str;
1663 } elsif (vcs_is_hg()) {
1664 $email_hg_since = $str;
1665 }
1666 $rerun = 1;
1667 } elsif ($sel eq "t") {
1668 bool_invert(\$email_git_all_signature_types);
1669 $rerun = 1;
1670 } elsif ($sel eq "f") {
1671 bool_invert(\$file_emails);
1672 $rerun = 1;
1673 } elsif ($sel eq "r") {
1674 bool_invert(\$email_remove_duplicates);
1675 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001676 } elsif ($sel eq "m") {
1677 bool_invert(\$email_use_mailmap);
1678 read_mailmap();
1679 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001680 } elsif ($sel eq "k") {
1681 bool_invert(\$keywords);
1682 $rerun = 1;
1683 } elsif ($sel eq "p") {
1684 if ($str ne "" && $val >= 0) {
1685 $pattern_depth = $val;
1686 $rerun = 1;
1687 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001688 } elsif ($sel eq "h" || $sel eq "?") {
1689 print STDERR <<EOT
1690
1691Interactive mode allows you to select the various maintainers, submitters,
1692commit signers and mailing lists that could be CC'd on a patch.
1693
1694Any *'d entry is selected.
1695
Joe Perches47abc722010-10-26 14:22:57 -07001696If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001697history of files in the patch. Also, each line of the current file can
1698be matched to its commit author and that commits signers with blame.
1699
1700Various knobs exist to control the length of time for active commit
1701tracking, the maximum number of commit authors and signers to add,
1702and such.
1703
1704Enter selections at the prompt until you are satisfied that the selected
1705maintainers are appropriate. You may enter multiple selections separated
1706by either commas or spaces.
1707
1708EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001709 } else {
1710 print STDERR "invalid option: '$nr'\n";
1711 $redraw = 0;
1712 }
1713 }
1714 if ($rerun) {
1715 print STDERR "git-blame can be very slow, please have patience..."
1716 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001717 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001718 }
1719 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001720
1721 #drop not selected entries
1722 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001723 my @new_emailto = ();
1724 foreach my $entry (@list) {
1725 if ($selected{$count}) {
1726 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001727 }
1728 $count++;
1729 }
Joe Perches683c6f82010-10-26 14:22:55 -07001730 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001731}
1732
Joe Perches683c6f82010-10-26 14:22:55 -07001733sub bool_invert {
1734 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001735
Joe Perches683c6f82010-10-26 14:22:55 -07001736 if ($$bool_ref) {
1737 $$bool_ref = 0;
1738 } else {
1739 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001740 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001741}
1742
Joe Perchesb9e23312010-10-26 14:22:58 -07001743sub deduplicate_email {
1744 my ($email) = @_;
1745
1746 my $matched = 0;
1747 my ($name, $address) = parse_email($email);
1748 $email = format_email($name, $address, 1);
1749 $email = mailmap_email($email);
1750
1751 return $email if (!$email_remove_duplicates);
1752
1753 ($name, $address) = parse_email($email);
1754
Joe Perchesfae99202010-10-26 14:22:58 -07001755 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001756 $name = $deduplicate_name_hash{lc($name)}->[0];
1757 $address = $deduplicate_name_hash{lc($name)}->[1];
1758 $matched = 1;
1759 } elsif ($deduplicate_address_hash{lc($address)}) {
1760 $name = $deduplicate_address_hash{lc($address)}->[0];
1761 $address = $deduplicate_address_hash{lc($address)}->[1];
1762 $matched = 1;
1763 }
1764 if (!$matched) {
1765 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1766 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1767 }
1768 $email = format_email($name, $address, 1);
1769 $email = mailmap_email($email);
1770 return $email;
1771}
1772
Joe Perches683c6f82010-10-26 14:22:55 -07001773sub save_commits_by_author {
1774 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001775
Joe Perches683c6f82010-10-26 14:22:55 -07001776 my @authors = ();
1777 my @commits = ();
1778 my @subjects = ();
1779
1780 foreach my $line (@lines) {
1781 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1782 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001783 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001784 push(@authors, $author);
1785 }
1786 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1787 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1788 }
1789
1790 for (my $i = 0; $i < @authors; $i++) {
1791 my $exists = 0;
1792 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1793 if (@{$ref}[0] eq $commits[$i] &&
1794 @{$ref}[1] eq $subjects[$i]) {
1795 $exists = 1;
1796 last;
1797 }
1798 }
1799 if (!$exists) {
1800 push(@{$commit_author_hash{$authors[$i]}},
1801 [ ($commits[$i], $subjects[$i]) ]);
1802 }
1803 }
1804}
1805
1806sub save_commits_by_signer {
1807 my (@lines) = @_;
1808
1809 my $commit = "";
1810 my $subject = "";
1811
1812 foreach my $line (@lines) {
1813 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1814 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1815 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1816 my @signatures = ($line);
1817 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1818 my @types = @$types_ref;
1819 my @signers = @$signers_ref;
1820
1821 my $type = $types[0];
1822 my $signer = $signers[0];
1823
Joe Perchesb9e23312010-10-26 14:22:58 -07001824 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001825
Joe Perches683c6f82010-10-26 14:22:55 -07001826 my $exists = 0;
1827 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1828 if (@{$ref}[0] eq $commit &&
1829 @{$ref}[1] eq $subject &&
1830 @{$ref}[2] eq $type) {
1831 $exists = 1;
1832 last;
1833 }
1834 }
1835 if (!$exists) {
1836 push(@{$commit_signer_hash{$signer}},
1837 [ ($commit, $subject, $type) ]);
1838 }
1839 }
1840 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001841}
1842
Joe Perches60db31a2009-12-14 18:00:50 -08001843sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001844 my ($role, $divisor, @lines) = @_;
1845
1846 my %hash;
1847 my $count = 0;
1848
Joe Perchesa8af2432009-12-14 18:00:49 -08001849 return if (@lines <= 0);
1850
1851 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001852 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001853 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001854 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001855
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001856 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001857
Joe Perches63ab52d2010-10-26 14:22:51 -07001858 return if (@lines <= 0);
1859
Joe Perches0e70e832009-09-21 17:04:20 -07001860 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001861
Joe Perches11ecf532009-09-21 17:04:22 -07001862 # uniq -c
1863 $hash{$_}++ for @lines;
1864
1865 # sort -rn
1866 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1867 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001868 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001869
Joe Perchesa8af2432009-12-14 18:00:49 -08001870 $percent = 100 if ($percent > 100);
Joe Perches11ecf532009-09-21 17:04:22 -07001871 $count++;
1872 last if ($sign_offs < $email_git_min_signatures ||
1873 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001874 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001875 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001876 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001877 my $fmt_percent = sprintf("%.0f", $percent);
1878 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1879 } else {
1880 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001881 }
Joe Perchesf5492662009-09-21 17:04:13 -07001882 }
1883}
1884
Joe Perches60db31a2009-12-14 18:00:50 -08001885sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001886 my ($file) = @_;
1887
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001888 my $authors_ref;
1889 my $signers_ref;
1890 my $stats_ref;
1891 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001892 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001893 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001894 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001895
Joe Perches683c6f82010-10-26 14:22:55 -07001896 $vcs_used = vcs_exists();
1897 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001898
Joe Perches60db31a2009-12-14 18:00:50 -08001899 my $cmd = $VCS_cmds{"find_signers_cmd"};
1900 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1901
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001902 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1903
1904 @signers = @{$signers_ref} if defined $signers_ref;
1905 @authors = @{$authors_ref} if defined $authors_ref;
1906 @stats = @{$stats_ref} if defined $stats_ref;
1907
1908# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07001909
1910 foreach my $signer (@signers) {
1911 $signer = deduplicate_email($signer);
1912 }
1913
Joe Perches60db31a2009-12-14 18:00:50 -08001914 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001915 vcs_assign("authored", $commits, @authors);
1916 if ($#authors == $#stats) {
1917 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1918 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
1919
1920 my $added = 0;
1921 my $deleted = 0;
1922 for (my $i = 0; $i <= $#stats; $i++) {
1923 if ($stats[$i] =~ /$stat_pattern/) {
1924 $added += $1;
1925 $deleted += $2;
1926 }
1927 }
1928 my @tmp_authors = uniq(@authors);
1929 foreach my $author (@tmp_authors) {
1930 $author = deduplicate_email($author);
1931 }
1932 @tmp_authors = uniq(@tmp_authors);
1933 my @list_added = ();
1934 my @list_deleted = ();
1935 foreach my $author (@tmp_authors) {
1936 my $auth_added = 0;
1937 my $auth_deleted = 0;
1938 for (my $i = 0; $i <= $#stats; $i++) {
1939 if ($author eq deduplicate_email($authors[$i]) &&
1940 $stats[$i] =~ /$stat_pattern/) {
1941 $auth_added += $1;
1942 $auth_deleted += $2;
1943 }
1944 }
1945 for (my $i = 0; $i < $auth_added; $i++) {
1946 push(@list_added, $author);
1947 }
1948 for (my $i = 0; $i < $auth_deleted; $i++) {
1949 push(@list_deleted, $author);
1950 }
1951 }
1952 vcs_assign("added_lines", $added, @list_added);
1953 vcs_assign("removed_lines", $deleted, @list_deleted);
1954 }
Joe Perchesa8af2432009-12-14 18:00:49 -08001955}
1956
Joe Perches60db31a2009-12-14 18:00:50 -08001957sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001958 my ($file) = @_;
1959
Joe Perches60db31a2009-12-14 18:00:50 -08001960 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001961 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001962 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001963 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001964 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001965
Joe Perches683c6f82010-10-26 14:22:55 -07001966 $vcs_used = vcs_exists();
1967 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07001968
Joe Perches63ab52d2010-10-26 14:22:51 -07001969 @all_commits = vcs_blame($file);
1970 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08001971 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001972 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001973
Joe Perches683c6f82010-10-26 14:22:55 -07001974 if ($email_git_blame_signatures) {
1975 if (vcs_is_hg()) {
1976 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001977 my $commit_authors_ref;
1978 my $commit_signers_ref;
1979 my $stats_ref;
1980 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07001981 my @commit_signers = ();
1982 my $commit = join(" -r ", @commits);
1983 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07001984
Joe Perches683c6f82010-10-26 14:22:55 -07001985 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1986 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08001987
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001988 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1989 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
1990 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07001991
Joe Perches683c6f82010-10-26 14:22:55 -07001992 push(@signers, @commit_signers);
1993 } else {
1994 foreach my $commit (@commits) {
1995 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001996 my $commit_authors_ref;
1997 my $commit_signers_ref;
1998 my $stats_ref;
1999 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002000 my @commit_signers = ();
2001 my $cmd;
2002
2003 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2004 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2005
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002006 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2007 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2008 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002009
2010 push(@signers, @commit_signers);
2011 }
2012 }
Joe Perchesf5492662009-09-21 17:04:13 -07002013 }
2014
Joe Perchesa8af2432009-12-14 18:00:49 -08002015 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002016 if ($output_rolestats) {
2017 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002018 if (vcs_is_hg()) {{ # Double brace for last exit
2019 my $commit_count;
2020 my @commit_signers = ();
2021 @commits = uniq(@commits);
2022 @commits = sort(@commits);
2023 my $commit = join(" -r ", @commits);
2024 my $cmd;
2025
2026 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2027 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2028
2029 my @lines = ();
2030
2031 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2032
2033 if (!$email_git_penguin_chiefs) {
2034 @lines = grep(!/${penguin_chiefs}/i, @lines);
2035 }
2036
2037 last if !@lines;
2038
2039 my @authors = ();
2040 foreach my $line (@lines) {
2041 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2042 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002043 $author = deduplicate_email($author);
2044 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002045 }
2046 }
2047
2048 save_commits_by_author(@lines) if ($interactive);
2049 save_commits_by_signer(@lines) if ($interactive);
2050
2051 push(@signers, @authors);
2052 }}
2053 else {
2054 foreach my $commit (@commits) {
2055 my $i;
2056 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2057 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2058 my @author = vcs_find_author($cmd);
2059 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002060
2061 my $formatted_author = deduplicate_email($author[0]);
2062
Joe Perches683c6f82010-10-26 14:22:55 -07002063 my $count = grep(/$commit/, @all_commits);
2064 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002065 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002066 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002067 }
2068 }
2069 if (@blame_signers) {
2070 vcs_assign("authored lines", $total_lines, @blame_signers);
2071 }
2072 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002073 foreach my $signer (@signers) {
2074 $signer = deduplicate_email($signer);
2075 }
Joe Perches60db31a2009-12-14 18:00:50 -08002076 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002077 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002078 foreach my $signer (@signers) {
2079 $signer = deduplicate_email($signer);
2080 }
Joe Perches60db31a2009-12-14 18:00:50 -08002081 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002082 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002083}
2084
2085sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002086 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002087
2088 my %saw;
2089 @parms = grep(!$saw{$_}++, @parms);
2090 return @parms;
2091}
2092
2093sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002094 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002095
2096 my %saw;
2097 @parms = sort @parms;
2098 @parms = grep(!$saw{$_}++, @parms);
2099 return @parms;
2100}
2101
Joe Perches03372db2010-03-05 13:43:00 -08002102sub clean_file_emails {
2103 my (@file_emails) = @_;
2104 my @fmt_emails = ();
2105
2106 foreach my $email (@file_emails) {
2107 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2108 my ($name, $address) = parse_email($email);
2109 if ($name eq '"[,\.]"') {
2110 $name = "";
2111 }
2112
2113 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2114 if (@nw > 2) {
2115 my $first = $nw[@nw - 3];
2116 my $middle = $nw[@nw - 2];
2117 my $last = $nw[@nw - 1];
2118
2119 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2120 (length($first) == 2 && substr($first, -1) eq ".")) ||
2121 (length($middle) == 1 ||
2122 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2123 $name = "$first $middle $last";
2124 } else {
2125 $name = "$middle $last";
2126 }
2127 }
2128
2129 if (substr($name, -1) =~ /[,\.]/) {
2130 $name = substr($name, 0, length($name) - 1);
2131 } elsif (substr($name, -2) =~ /[,\.]"/) {
2132 $name = substr($name, 0, length($name) - 2) . '"';
2133 }
2134
2135 if (substr($name, 0, 1) =~ /[,\.]/) {
2136 $name = substr($name, 1, length($name) - 1);
2137 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2138 $name = '"' . substr($name, 2, length($name) - 2);
2139 }
2140
2141 my $fmt_email = format_email($name, $address, $email_usename);
2142 push(@fmt_emails, $fmt_email);
2143 }
2144 return @fmt_emails;
2145}
2146
Joe Perches3c7385b2009-12-14 18:00:46 -08002147sub merge_email {
2148 my @lines;
2149 my %saw;
2150
2151 for (@_) {
2152 my ($address, $role) = @$_;
2153 if (!$saw{$address}) {
2154 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002155 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002156 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002157 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002158 }
2159 $saw{$address} = 1;
2160 }
2161 }
2162
2163 return @lines;
2164}
2165
Joe Perchescb7301c2009-04-07 20:40:12 -07002166sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002167 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002168
2169 if ($output_multiline) {
2170 foreach my $line (@parms) {
2171 print("${line}\n");
2172 }
2173 } else {
2174 print(join($output_separator, @parms));
2175 print("\n");
2176 }
2177}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002178
2179my $rfc822re;
2180
2181sub make_rfc822re {
2182# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2183# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2184# This regexp will only work on addresses which have had comments stripped
2185# and replaced with rfc822_lwsp.
2186
2187 my $specials = '()<>@,;:\\\\".\\[\\]';
2188 my $controls = '\\000-\\037\\177';
2189
2190 my $dtext = "[^\\[\\]\\r\\\\]";
2191 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2192
2193 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2194
2195# Use zero-width assertion to spot the limit of an atom. A simple
2196# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2197 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2198 my $word = "(?:$atom|$quoted_string)";
2199 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2200
2201 my $sub_domain = "(?:$atom|$domain_literal)";
2202 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2203
2204 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2205
2206 my $phrase = "$word*";
2207 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2208 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2209 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2210
2211 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2212 my $address = "(?:$mailbox|$group)";
2213
2214 return "$rfc822_lwsp*$address";
2215}
2216
2217sub rfc822_strip_comments {
2218 my $s = shift;
2219# Recursively remove comments, and replace with a single space. The simpler
2220# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2221# chars in atoms, for example.
2222
2223 while ($s =~ s/^((?:[^"\\]|\\.)*
2224 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2225 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2226 return $s;
2227}
2228
2229# valid: returns true if the parameter is an RFC822 valid address
2230#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002231sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002232 my $s = rfc822_strip_comments(shift);
2233
2234 if (!$rfc822re) {
2235 $rfc822re = make_rfc822re();
2236 }
2237
2238 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2239}
2240
2241# validlist: In scalar context, returns true if the parameter is an RFC822
2242# valid list of addresses.
2243#
2244# In list context, returns an empty list on failure (an invalid
2245# address was found); otherwise a list whose first element is the
2246# number of addresses found and whose remaining elements are the
2247# addresses. This is needed to disambiguate failure (invalid)
2248# from success with no addresses found, because an empty string is
2249# a valid list.
2250
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002251sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002252 my $s = rfc822_strip_comments(shift);
2253
2254 if (!$rfc822re) {
2255 $rfc822re = make_rfc822re();
2256 }
2257 # * null list items are valid according to the RFC
2258 # * the '1' business is to aid in distinguishing failure from no results
2259
2260 my @r;
2261 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2262 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002263 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002264 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002265 }
2266 return wantarray ? (scalar(@r), @r) : 1;
2267 }
Joe Perches60db31a2009-12-14 18:00:50 -08002268 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002269}