blob: fc169fd2a3cc30816260d73e40093aaffbee4a68 [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 Perches435de072015-06-25 15:01:50 -0700189my @ignore_emails = ();
190my $ignore_file = which_conf(".get_maintainer.ignore");
191if (-f $ignore_file) {
192 open(my $ignore, '<', "$ignore_file")
193 or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
194 while (<$ignore>) {
195 my $line = $_;
196
197 $line =~ s/\s*\n?$//;
198 $line =~ s/^\s*//;
199 $line =~ s/\s+$//;
200 $line =~ s/#.*$//;
201
202 next if ($line =~ m/^\s*$/);
203 if (rfc822_valid($line)) {
204 push(@ignore_emails, $line);
205 }
206 }
207 close($ignore);
208}
209
Joe Perchescb7301c2009-04-07 20:40:12 -0700210if (!GetOptions(
211 'email!' => \$email,
212 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700213 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800214 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700215 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700216 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700217 'git-chief-penguins!' => \$email_git_penguin_chiefs,
218 'git-min-signatures=i' => \$email_git_min_signatures,
219 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700220 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700221 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800222 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700223 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700224 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700225 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700226 'm!' => \$email_maintainer,
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700227 'r!' => \$email_reviewer,
Joe Perchescb7301c2009-04-07 20:40:12 -0700228 'n!' => \$email_usename,
229 'l!' => \$email_list,
230 's!' => \$email_subscriber_list,
231 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800232 'roles!' => \$output_roles,
233 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700234 'separator=s' => \$output_separator,
235 'subsystem!' => \$subsystem,
236 'status!' => \$status,
237 'scm!' => \$scm,
238 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700239 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700240 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800241 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800242 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700243 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700244 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800245 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700246 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800247 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700248}
249
250if ($help != 0) {
251 usage();
252 exit 0;
253}
254
255if ($version != 0) {
256 print("${P} ${V}\n");
257 exit 0;
258}
259
Joe Perches64f77f32010-03-05 13:43:04 -0800260if (-t STDIN && !@ARGV) {
261 # We're talking to a terminal, but have no command line arguments.
262 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700263}
264
Joe Perches683c6f82010-10-26 14:22:55 -0700265$output_multiline = 0 if ($output_separator ne ", ");
266$output_rolestats = 1 if ($interactive);
267$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800268
Joe Perches4b76c9d2010-03-05 13:43:03 -0800269if ($sections) {
270 $email = 0;
271 $email_list = 0;
272 $scm = 0;
273 $status = 0;
274 $subsystem = 0;
275 $web = 0;
276 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700277 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800278} else {
279 my $selections = $email + $scm + $status + $subsystem + $web;
280 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800281 die "$P: Missing required option: email, scm, status, subsystem or web\n";
282 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700283}
284
Joe Perchesf5492662009-09-21 17:04:13 -0700285if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700286 ($email_maintainer + $email_reviewer +
287 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700288 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700289 die "$P: Please select at least 1 email option\n";
290}
291
292if (!top_of_kernel_tree($lk_path)) {
293 die "$P: The current directory does not appear to be "
294 . "a linux kernel source tree.\n";
295}
296
297## Read MAINTAINERS for type/value pairs
298
299my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700300my %keyword_hash;
301
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800302open (my $maint, '<', "${lk_path}MAINTAINERS")
303 or die "$P: Can't open MAINTAINERS: $!\n";
304while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700305 my $line = $_;
306
307 if ($line =~ m/^(\C):\s*(.*)/) {
308 my $type = $1;
309 my $value = $2;
310
311 ##Filename pattern matching
312 if ($type eq "F" || $type eq "X") {
313 $value =~ s@\.@\\\.@g; ##Convert . to \.
314 $value =~ s/\*/\.\*/g; ##Convert * to .*
315 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700316 ##if pattern is a directory and it lacks a trailing slash, add one
317 if ((-d $value)) {
318 $value =~ s@([^/])$@$1/@;
319 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700320 } elsif ($type eq "K") {
321 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700322 }
323 push(@typevalue, "$type:$value");
324 } elsif (!/^(\s)*$/) {
325 $line =~ s/\n$//g;
326 push(@typevalue, $line);
327 }
328}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800329close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700330
Joe Perches8cbb3a72009-09-21 17:04:21 -0700331
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700332#
333# Read mail address map
334#
335
Joe Perchesb9e23312010-10-26 14:22:58 -0700336my $mailmap;
337
338read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700339
340sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700341 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700342 names => {},
343 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700344 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700345
Joe Perchesb9e23312010-10-26 14:22:58 -0700346 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700347
348 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800349 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700350
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700351 while (<$mailmap_file>) {
352 s/#.*$//; #strip comments
353 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700354
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700355 next if (/^\s*$/); #skip empty lines
356 #entries have one of the following formats:
357 # name1 <mail1>
358 # <mail1> <mail2>
359 # name1 <mail1> <mail2>
360 # name1 <mail1> name2 <mail2>
361 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700362
363 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700364 my $real_name = $1;
365 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700366
Joe Perches47abc722010-10-26 14:22:57 -0700367 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700368 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700369 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700370
Joe Perches0334b382011-07-25 17:13:13 -0700371 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700372 my $real_address = $1;
373 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700374
Joe Perches47abc722010-10-26 14:22:57 -0700375 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700376
Joe Perches0334b382011-07-25 17:13:13 -0700377 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700378 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700379 my $real_address = $2;
380 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700381
Joe Perches47abc722010-10-26 14:22:57 -0700382 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700383 ($real_name, $real_address) =
384 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700385 $mailmap->{names}->{$wrong_address} = $real_name;
386 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700387
Joe Perches0334b382011-07-25 17:13:13 -0700388 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700389 my $real_name = $1;
390 my $real_address = $2;
391 my $wrong_name = $3;
392 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700393
Joe Perches47abc722010-10-26 14:22:57 -0700394 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700395 ($real_name, $real_address) =
396 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700397
Joe Perchesb9e23312010-10-26 14:22:58 -0700398 $wrong_name =~ s/\s+$//;
399 ($wrong_name, $wrong_address) =
400 parse_email("$wrong_name <$wrong_address>");
401
402 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
403 $mailmap->{names}->{$wrong_email} = $real_name;
404 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700405 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700406 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700407 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700408}
409
Joe Perches4a7fdb52009-04-10 12:28:57 -0700410## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700411
412my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700413my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700414my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800415my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700416
Joe Perches64f77f32010-03-05 13:43:04 -0800417if (!@ARGV) {
418 push(@ARGV, "&STDIN");
419}
420
Joe Perches4a7fdb52009-04-10 12:28:57 -0700421foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800422 if ($file ne "&STDIN") {
423 ##if $file is a directory and it lacks a trailing slash, add one
424 if ((-d $file)) {
425 $file =~ s@([^/])$@$1/@;
426 } elsif (!(-f $file)) {
427 die "$P: file '${file}' not found\n";
428 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700429 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700430 if ($from_filename) {
431 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700432 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800433 open(my $f, '<', $file)
434 or die "$P: Can't open $file: $!\n";
435 my $text = do { local($/) ; <$f> };
436 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800437 if ($keywords) {
438 foreach my $line (keys %keyword_hash) {
439 if ($text =~ m/$keyword_hash{$line}/x) {
440 push(@keyword_tvi, $line);
441 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700442 }
443 }
Joe Perches03372db2010-03-05 13:43:00 -0800444 if ($file_emails) {
445 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;
446 push(@file_emails, clean_file_emails(@poss_addr));
447 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700448 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700449 } else {
450 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700451 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800452
Wolfram Sang3a4df132010-03-23 13:35:18 -0700453 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800454 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700455
456 # We can check arbitrary information before the patch
457 # like the commit message, mail headers, etc...
458 # This allows us to match arbitrary keywords against any part
459 # of a git format-patch generated file (subject tags, etc...)
460
461 my $patch_prefix = ""; #Parsing the intro
462
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800463 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700464 my $patch_line = $_;
Geert Uytterhoeven6be07102013-02-21 16:43:12 -0800465 if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700466 my $filename = $1;
467 $filename =~ s@^[^/]*/@@;
468 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700469 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700470 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700471 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700472 } elsif (m/^\@\@ -(\d+),(\d+)/) {
473 if ($email_git_blame) {
474 push(@range, "$lastfile:$1:$2");
475 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700476 } elsif ($keywords) {
477 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700478 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700479 push(@keyword_tvi, $line);
480 }
481 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700482 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700483 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800484 close($patch);
485
Joe Perches4a7fdb52009-04-10 12:28:57 -0700486 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700487 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700488 . "Add -f to options?\n";
489 }
490 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700491 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700492}
493
Joe Perches03372db2010-03-05 13:43:00 -0800494@file_emails = uniq(@file_emails);
495
Joe Perches683c6f82010-10-26 14:22:55 -0700496my %email_hash_name;
497my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700498my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700499my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700500my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700501my @scm = ();
502my @web = ();
503my @subsystem = ();
504my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700505my %deduplicate_name_hash = ();
506my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700507
Joe Perches6ef1c522010-10-26 14:22:56 -0700508my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700509
Joe Perches6ef1c522010-10-26 14:22:56 -0700510if (@maintainers) {
511 @maintainers = merge_email(@maintainers);
512 output(@maintainers);
513}
Joe Perchescb7301c2009-04-07 20:40:12 -0700514
515if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700516 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700517 output(@scm);
518}
Joe Perches683c6f82010-10-26 14:22:55 -0700519
Joe Perchescb7301c2009-04-07 20:40:12 -0700520if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700521 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700522 output(@status);
523}
524
525if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700526 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700527 output(@subsystem);
528}
529
530if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700531 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700532 output(@web);
533}
534
535exit($exit);
536
Joe Perches435de072015-06-25 15:01:50 -0700537sub ignore_email_address {
538 my ($address) = @_;
539
540 foreach my $ignore (@ignore_emails) {
541 return 1 if ($ignore eq $address);
542 }
543
544 return 0;
545}
546
Joe Perchesab6c9372011-01-12 16:59:50 -0800547sub range_is_maintained {
548 my ($start, $end) = @_;
549
550 for (my $i = $start; $i < $end; $i++) {
551 my $line = $typevalue[$i];
552 if ($line =~ m/^(\C):\s*(.*)/) {
553 my $type = $1;
554 my $value = $2;
555 if ($type eq 'S') {
556 if ($value =~ /(maintain|support)/i) {
557 return 1;
558 }
559 }
560 }
561 }
562 return 0;
563}
564
565sub range_has_maintainer {
566 my ($start, $end) = @_;
567
568 for (my $i = $start; $i < $end; $i++) {
569 my $line = $typevalue[$i];
570 if ($line =~ m/^(\C):\s*(.*)/) {
571 my $type = $1;
572 my $value = $2;
573 if ($type eq 'M') {
574 return 1;
575 }
576 }
577 }
578 return 0;
579}
580
Joe Perches6ef1c522010-10-26 14:22:56 -0700581sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700582 %email_hash_name = ();
583 %email_hash_address = ();
584 %commit_author_hash = ();
585 %commit_signer_hash = ();
586 @email_to = ();
587 %hash_list_to = ();
588 @list_to = ();
589 @scm = ();
590 @web = ();
591 @subsystem = ();
592 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700593 %deduplicate_name_hash = ();
594 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700595 if ($email_git_all_signature_types) {
596 $signature_pattern = "(.+?)[Bb][Yy]:";
597 } else {
598 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
599 }
600
601 # Find responsible parties
602
Joe Perchesb9e23312010-10-26 14:22:58 -0700603 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700604
Joe Perches683c6f82010-10-26 14:22:55 -0700605 foreach my $file (@files) {
606
607 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700608 my $tvi = find_first_section();
609 while ($tvi < @typevalue) {
610 my $start = find_starting_index($tvi);
611 my $end = find_ending_index($tvi);
612 my $exclude = 0;
613 my $i;
614
615 #Do not match excluded file patterns
616
617 for ($i = $start; $i < $end; $i++) {
618 my $line = $typevalue[$i];
619 if ($line =~ m/^(\C):\s*(.*)/) {
620 my $type = $1;
621 my $value = $2;
622 if ($type eq 'X') {
623 if (file_match_pattern($file, $value)) {
624 $exclude = 1;
625 last;
626 }
627 }
628 }
629 }
630
631 if (!$exclude) {
632 for ($i = $start; $i < $end; $i++) {
633 my $line = $typevalue[$i];
634 if ($line =~ m/^(\C):\s*(.*)/) {
635 my $type = $1;
636 my $value = $2;
637 if ($type eq 'F') {
638 if (file_match_pattern($file, $value)) {
639 my $value_pd = ($value =~ tr@/@@);
640 my $file_pd = ($file =~ tr@/@@);
641 $value_pd++ if (substr($value,-1,1) ne "/");
642 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800643 if ($value_pd >= $file_pd &&
644 range_is_maintained($start, $end) &&
645 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700646 $exact_pattern_match_hash{$file} = 1;
647 }
Joe Perches683c6f82010-10-26 14:22:55 -0700648 if ($pattern_depth == 0 ||
649 (($file_pd - $value_pd) < $pattern_depth)) {
650 $hash{$tvi} = $value_pd;
651 }
652 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700653 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800654 if ($file =~ m/$value/x) {
655 $hash{$tvi} = 0;
656 }
Joe Perches683c6f82010-10-26 14:22:55 -0700657 }
658 }
659 }
660 }
661 $tvi = $end + 1;
662 }
663
664 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
665 add_categories($line);
666 if ($sections) {
667 my $i;
668 my $start = find_starting_index($line);
669 my $end = find_ending_index($line);
670 for ($i = $start; $i < $end; $i++) {
671 my $line = $typevalue[$i];
672 if ($line =~ /^[FX]:/) { ##Restore file patterns
673 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
674 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
675 $line =~ s/\\\./\./g; ##Convert \. to .
676 $line =~ s/\.\*/\*/g; ##Convert .* to *
677 }
678 $line =~ s/^([A-Z]):/$1:\t/g;
679 print("$line\n");
680 }
681 print("\n");
682 }
683 }
Joe Perches683c6f82010-10-26 14:22:55 -0700684 }
685
686 if ($keywords) {
687 @keyword_tvi = sort_and_uniq(@keyword_tvi);
688 foreach my $line (@keyword_tvi) {
689 add_categories($line);
690 }
691 }
692
Joe Perchesb9e23312010-10-26 14:22:58 -0700693 foreach my $email (@email_to, @list_to) {
694 $email->[0] = deduplicate_email($email->[0]);
695 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700696
697 foreach my $file (@files) {
698 if ($email &&
699 ($email_git || ($email_git_fallback &&
700 !$exact_pattern_match_hash{$file}))) {
701 vcs_file_signoffs($file);
702 }
703 if ($email && $email_git_blame) {
704 vcs_file_blame($file);
705 }
706 }
707
Joe Perches683c6f82010-10-26 14:22:55 -0700708 if ($email) {
709 foreach my $chief (@penguin_chief) {
710 if ($chief =~ m/^(.*):(.*)/) {
711 my $email_address;
712
713 $email_address = format_email($1, $2, $email_usename);
714 if ($email_git_penguin_chiefs) {
715 push(@email_to, [$email_address, 'chief penguin']);
716 } else {
717 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
718 }
719 }
720 }
721
722 foreach my $email (@file_emails) {
723 my ($name, $address) = parse_email($email);
724
725 my $tmp_email = format_email($name, $address, $email_usename);
726 push_email_address($tmp_email, '');
727 add_role($tmp_email, 'in file');
728 }
729 }
730
731 my @to = ();
732 if ($email || $email_list) {
733 if ($email) {
734 @to = (@to, @email_to);
735 }
736 if ($email_list) {
737 @to = (@to, @list_to);
738 }
739 }
740
Joe Perches6ef1c522010-10-26 14:22:56 -0700741 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700742 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700743 }
Joe Perches683c6f82010-10-26 14:22:55 -0700744
745 return @to;
746}
747
Joe Perchescb7301c2009-04-07 20:40:12 -0700748sub file_match_pattern {
749 my ($file, $pattern) = @_;
750 if (substr($pattern, -1) eq "/") {
751 if ($file =~ m@^$pattern@) {
752 return 1;
753 }
754 } else {
755 if ($file =~ m@^$pattern@) {
756 my $s1 = ($file =~ tr@/@@);
757 my $s2 = ($pattern =~ tr@/@@);
758 if ($s1 == $s2) {
759 return 1;
760 }
761 }
762 }
763 return 0;
764}
765
766sub usage {
767 print <<EOT;
768usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700769 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700770version: $V
771
772MAINTAINER field selection options:
773 --email => print email address(es) if any
774 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700775 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700776 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700777 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700778 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700779 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
780 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
781 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700782 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700783 --git-since => git history to use (default: $email_git_since)
784 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700785 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700786 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700787 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -0700788 --n => include name 'Full Name <addr\@domain.tld>'
789 --l => include list(s) if any
790 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700791 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800792 --roles => show roles (status:subsystem, git-signer, list, etc...)
793 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800794 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700795 --scm => print SCM tree(s) if any
796 --status => print status if any
797 --subsystem => print subsystem name if any
798 --web => print website(s) if any
799
800Output type options:
801 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700802 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700803 --multiline => print 1 entry per line
804
Joe Perchescb7301c2009-04-07 20:40:12 -0700805Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700806 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700807 --keywords => scan patch for keywords (default: $keywords)
808 --sections => print all of the subsystem sections with pattern matches
809 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700810 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700811 --help => show this help information
812
Joe Perches3fb55652009-09-21 17:04:17 -0700813Default options:
Joe Perches7e1863a2011-01-12 16:59:49 -0800814 [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
815 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700816
Joe Perches870020f2009-07-29 15:04:28 -0700817Notes:
818 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700819 Used with "--git", git signators for _all_ files in and below
820 directory are examined as git recurses directories.
821 Any specified X: (exclude) pattern matches are _not_ ignored.
822 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800823 no individual file within the directory or subdirectory
824 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700825 Used with "--git-blame", does not iterate all files in directory
826 Using "--git-blame" is slow and may add old committers and authors
827 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800828 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
829 other automated tools that expect only ["name"] <email address>
830 may not work because of additional output after <email address>.
831 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
832 not the percentage of the entire file authored. # of commits is
833 not a good measure of amount of code authored. 1 major commit may
834 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800835 If git is not installed, but mercurial (hg) is installed and an .hg
836 repository exists, the following options apply to mercurial:
837 --git,
838 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
839 --git-blame
840 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700841 File ".get_maintainer.conf", if it exists in the linux kernel source root
842 directory, can change whatever get_maintainer defaults are desired.
843 Entries in this file can be any command line argument.
844 This file is prepended to any additional command line arguments.
845 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700846EOT
847}
848
849sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700850 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700851
Joe Perches47abc722010-10-26 14:22:57 -0700852 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
853 $lk_path .= "/";
854 }
855 if ( (-f "${lk_path}COPYING")
856 && (-f "${lk_path}CREDITS")
857 && (-f "${lk_path}Kbuild")
858 && (-f "${lk_path}MAINTAINERS")
859 && (-f "${lk_path}Makefile")
860 && (-f "${lk_path}README")
861 && (-d "${lk_path}Documentation")
862 && (-d "${lk_path}arch")
863 && (-d "${lk_path}include")
864 && (-d "${lk_path}drivers")
865 && (-d "${lk_path}fs")
866 && (-d "${lk_path}init")
867 && (-d "${lk_path}ipc")
868 && (-d "${lk_path}kernel")
869 && (-d "${lk_path}lib")
870 && (-d "${lk_path}scripts")) {
871 return 1;
872 }
873 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700874}
875
Joe Perches0e70e832009-09-21 17:04:20 -0700876sub parse_email {
877 my ($formatted_email) = @_;
878
879 my $name = "";
880 my $address = "";
881
Joe Perches11ecf532009-09-21 17:04:22 -0700882 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700883 $name = $1;
884 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700885 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700886 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700887 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700888 $address = $1;
889 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700890
891 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700892 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700893 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700894
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800895 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700896 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700897 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700898 }
Joe Perches0e70e832009-09-21 17:04:20 -0700899
900 return ($name, $address);
901}
902
903sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800904 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700905
906 my $formatted_email;
907
908 $name =~ s/^\s+|\s+$//g;
909 $name =~ s/^\"|\"$//g;
910 $address =~ s/^\s+|\s+$//g;
911
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800912 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700913 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
914 $name = "\"$name\"";
915 }
916
Joe Perchesa8af2432009-12-14 18:00:49 -0800917 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700918 if ("$name" eq "") {
919 $formatted_email = "$address";
920 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800921 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700922 }
923 } else {
924 $formatted_email = $address;
925 }
926
Joe Perchescb7301c2009-04-07 20:40:12 -0700927 return $formatted_email;
928}
929
Joe Perches272a8972010-01-08 14:42:48 -0800930sub find_first_section {
931 my $index = 0;
932
933 while ($index < @typevalue) {
934 my $tv = $typevalue[$index];
935 if (($tv =~ m/^(\C):\s*(.*)/)) {
936 last;
937 }
938 $index++;
939 }
940
941 return $index;
942}
943
Joe Perchesb7816552009-09-21 17:04:24 -0700944sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700945 my ($index) = @_;
946
947 while ($index > 0) {
948 my $tv = $typevalue[$index];
949 if (!($tv =~ m/^(\C):\s*(.*)/)) {
950 last;
951 }
952 $index--;
953 }
954
955 return $index;
956}
957
958sub find_ending_index {
959 my ($index) = @_;
960
961 while ($index < @typevalue) {
962 my $tv = $typevalue[$index];
963 if (!($tv =~ m/^(\C):\s*(.*)/)) {
964 last;
965 }
966 $index++;
967 }
968
969 return $index;
970}
971
Joe Perches3c7385b2009-12-14 18:00:46 -0800972sub get_maintainer_role {
973 my ($index) = @_;
974
975 my $i;
976 my $start = find_starting_index($index);
977 my $end = find_ending_index($index);
978
Joe Perches0ede2742012-03-23 15:01:56 -0700979 my $role = "unknown";
Joe Perches3c7385b2009-12-14 18:00:46 -0800980 my $subsystem = $typevalue[$start];
981 if (length($subsystem) > 20) {
982 $subsystem = substr($subsystem, 0, 17);
983 $subsystem =~ s/\s*$//;
984 $subsystem = $subsystem . "...";
985 }
986
987 for ($i = $start + 1; $i < $end; $i++) {
988 my $tv = $typevalue[$i];
989 if ($tv =~ m/^(\C):\s*(.*)/) {
990 my $ptype = $1;
991 my $pvalue = $2;
992 if ($ptype eq "S") {
993 $role = $pvalue;
994 }
995 }
996 }
997
998 $role = lc($role);
999 if ($role eq "supported") {
1000 $role = "supporter";
1001 } elsif ($role eq "maintained") {
1002 $role = "maintainer";
1003 } elsif ($role eq "odd fixes") {
1004 $role = "odd fixer";
1005 } elsif ($role eq "orphan") {
1006 $role = "orphan minder";
1007 } elsif ($role eq "obsolete") {
1008 $role = "obsolete minder";
1009 } elsif ($role eq "buried alive in reporters") {
1010 $role = "chief penguin";
1011 }
1012
1013 return $role . ":" . $subsystem;
1014}
1015
1016sub get_list_role {
1017 my ($index) = @_;
1018
1019 my $i;
1020 my $start = find_starting_index($index);
1021 my $end = find_ending_index($index);
1022
1023 my $subsystem = $typevalue[$start];
1024 if (length($subsystem) > 20) {
1025 $subsystem = substr($subsystem, 0, 17);
1026 $subsystem =~ s/\s*$//;
1027 $subsystem = $subsystem . "...";
1028 }
1029
1030 if ($subsystem eq "THE REST") {
1031 $subsystem = "";
1032 }
1033
1034 return $subsystem;
1035}
1036
Joe Perchescb7301c2009-04-07 20:40:12 -07001037sub add_categories {
1038 my ($index) = @_;
1039
Joe Perchesb7816552009-09-21 17:04:24 -07001040 my $i;
1041 my $start = find_starting_index($index);
1042 my $end = find_ending_index($index);
1043
1044 push(@subsystem, $typevalue[$start]);
1045
1046 for ($i = $start + 1; $i < $end; $i++) {
1047 my $tv = $typevalue[$i];
Joe Perches290603c2009-06-16 15:33:58 -07001048 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001049 my $ptype = $1;
1050 my $pvalue = $2;
1051 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001052 my $list_address = $pvalue;
1053 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001054 my $list_role = get_list_role($i);
1055
1056 if ($list_role ne "") {
1057 $list_role = ":" . $list_role;
1058 }
Joe Perches290603c2009-06-16 15:33:58 -07001059 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1060 $list_address = $1;
1061 $list_additional = $2;
1062 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001063 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001064 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001065 if (!$hash_list_to{lc($list_address)}) {
1066 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001067 push(@list_to, [$list_address,
1068 "subscriber list${list_role}"]);
1069 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001070 }
1071 } else {
1072 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001073 if (!$hash_list_to{lc($list_address)}) {
1074 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001075 if ($list_additional =~ m/moderated/) {
1076 push(@list_to, [$list_address,
1077 "moderated list${list_role}"]);
1078 } else {
1079 push(@list_to, [$list_address,
1080 "open list${list_role}"]);
1081 }
Joe Perches683c6f82010-10-26 14:22:55 -07001082 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001083 }
1084 }
1085 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001086 my ($name, $address) = parse_email($pvalue);
1087 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001088 if ($i > 0) {
1089 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -07001090 if ($tv =~ m/^(\C):\s*(.*)/) {
1091 if ($1 eq "P") {
1092 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001093 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001094 }
1095 }
1096 }
1097 }
Joe Perches0e70e832009-09-21 17:04:20 -07001098 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001099 my $role = get_maintainer_role($i);
1100 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001101 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001102 } elsif ($ptype eq "R") {
1103 my ($name, $address) = parse_email($pvalue);
1104 if ($name eq "") {
1105 if ($i > 0) {
1106 my $tv = $typevalue[$i - 1];
1107 if ($tv =~ m/^(\C):\s*(.*)/) {
1108 if ($1 eq "P") {
1109 $name = $2;
1110 $pvalue = format_email($name, $address, $email_usename);
1111 }
1112 }
1113 }
1114 }
1115 if ($email_reviewer) {
1116 push_email_addresses($pvalue, 'reviewer');
1117 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001118 } elsif ($ptype eq "T") {
1119 push(@scm, $pvalue);
1120 } elsif ($ptype eq "W") {
1121 push(@web, $pvalue);
1122 } elsif ($ptype eq "S") {
1123 push(@status, $pvalue);
1124 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001125 }
1126 }
1127}
1128
Joe Perches11ecf532009-09-21 17:04:22 -07001129sub email_inuse {
1130 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001131
Joe Perches11ecf532009-09-21 17:04:22 -07001132 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001133 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1134 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001135
Joe Perches0e70e832009-09-21 17:04:20 -07001136 return 0;
1137}
1138
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001139sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001140 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001141
Joe Perches0e70e832009-09-21 17:04:20 -07001142 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001143
Joe Perchesb7816552009-09-21 17:04:24 -07001144 if ($address eq "") {
1145 return 0;
1146 }
1147
Joe Perches11ecf532009-09-21 17:04:22 -07001148 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001149 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001150 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001151 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001152 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001153 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001154 }
Joe Perchesb7816552009-09-21 17:04:24 -07001155
1156 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001157}
1158
1159sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001160 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001161
1162 my @address_list = ();
1163
Joe Perches5f2441e2009-06-16 15:34:02 -07001164 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001165 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001166 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001167 my $array_count = shift(@address_list);
1168 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001169 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001170 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001171 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001172 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001173 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1174 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001175 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001176}
1177
Joe Perches3c7385b2009-12-14 18:00:46 -08001178sub add_role {
1179 my ($line, $role) = @_;
1180
1181 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001182 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001183
1184 foreach my $entry (@email_to) {
1185 if ($email_remove_duplicates) {
1186 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001187 if (($name eq $entry_name || $address eq $entry_address)
1188 && ($role eq "" || !($entry->[1] =~ m/$role/))
1189 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001190 if ($entry->[1] eq "") {
1191 $entry->[1] = "$role";
1192 } else {
1193 $entry->[1] = "$entry->[1],$role";
1194 }
1195 }
1196 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001197 if ($email eq $entry->[0]
1198 && ($role eq "" || !($entry->[1] =~ m/$role/))
1199 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001200 if ($entry->[1] eq "") {
1201 $entry->[1] = "$role";
1202 } else {
1203 $entry->[1] = "$entry->[1],$role";
1204 }
1205 }
1206 }
1207 }
1208}
1209
Joe Perchescb7301c2009-04-07 20:40:12 -07001210sub which {
1211 my ($bin) = @_;
1212
Joe Perchesf5f50782009-06-16 15:34:00 -07001213 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001214 if (-e "$path/$bin") {
1215 return "$path/$bin";
1216 }
1217 }
1218
1219 return "";
1220}
1221
Joe Perchesbcde44e2010-10-26 14:22:53 -07001222sub which_conf {
1223 my ($conf) = @_;
1224
1225 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1226 if (-e "$path/$conf") {
1227 return "$path/$conf";
1228 }
1229 }
1230
1231 return "";
1232}
1233
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001234sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001235 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001236
Joe Perches47abc722010-10-26 14:22:57 -07001237 my ($name, $address) = parse_email($line);
1238 my $email = format_email($name, $address, 1);
1239 my $real_name = $name;
1240 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001241
Joe Perches47abc722010-10-26 14:22:57 -07001242 if (exists $mailmap->{names}->{$email} ||
1243 exists $mailmap->{addresses}->{$email}) {
1244 if (exists $mailmap->{names}->{$email}) {
1245 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001246 }
Joe Perches47abc722010-10-26 14:22:57 -07001247 if (exists $mailmap->{addresses}->{$email}) {
1248 $real_address = $mailmap->{addresses}->{$email};
1249 }
1250 } else {
1251 if (exists $mailmap->{names}->{$address}) {
1252 $real_name = $mailmap->{names}->{$address};
1253 }
1254 if (exists $mailmap->{addresses}->{$address}) {
1255 $real_address = $mailmap->{addresses}->{$address};
1256 }
1257 }
1258 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001259}
1260
1261sub mailmap {
1262 my (@addresses) = @_;
1263
Joe Perchesb9e23312010-10-26 14:22:58 -07001264 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001265 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001266 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001267 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001268 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1269 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001270}
1271
1272sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001273 my %address_map;
1274 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001275
Joe Perches47abc722010-10-26 14:22:57 -07001276 foreach my $email (@emails) {
1277 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001278 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001279 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001280 $email = format_email($name, $address, 1);
1281 } else {
1282 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001283 }
Joe Perches47abc722010-10-26 14:22:57 -07001284 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001285}
1286
Joe Perches60db31a2009-12-14 18:00:50 -08001287sub git_execute_cmd {
1288 my ($cmd) = @_;
1289 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001290
Joe Perches60db31a2009-12-14 18:00:50 -08001291 my $output = `$cmd`;
1292 $output =~ s/^\s*//gm;
1293 @lines = split("\n", $output);
1294
1295 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001296}
1297
Joe Perches60db31a2009-12-14 18:00:50 -08001298sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001299 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001300 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001301
Joe Perches60db31a2009-12-14 18:00:50 -08001302 my $output = `$cmd`;
1303 @lines = split("\n", $output);
1304
1305 return @lines;
1306}
1307
Joe Perches683c6f82010-10-26 14:22:55 -07001308sub extract_formatted_signatures {
1309 my (@signature_lines) = @_;
1310
1311 my @type = @signature_lines;
1312
1313 s/\s*(.*):.*/$1/ for (@type);
1314
1315 # cut -f2- -d":"
1316 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1317
1318## Reformat email addresses (with names) to avoid badly written signatures
1319
1320 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001321 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001322 }
1323
1324 return (\@type, \@signature_lines);
1325}
1326
Joe Perches60db31a2009-12-14 18:00:50 -08001327sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001328 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001329 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001330 my @lines = ();
1331 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001332 my @authors = ();
1333 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001334
Joe Perches60db31a2009-12-14 18:00:50 -08001335 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001336
Joe Perches60db31a2009-12-14 18:00:50 -08001337 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001338 my $author_pattern = $VCS_cmds{"author_pattern"};
1339 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1340
1341 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001342
Joe Perches60db31a2009-12-14 18:00:50 -08001343 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001344
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001345 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001346 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001347 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001348
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001349# print("stats: <@stats>\n");
1350
1351 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001352
1353 save_commits_by_author(@lines) if ($interactive);
1354 save_commits_by_signer(@lines) if ($interactive);
1355
Joe Perches0e70e832009-09-21 17:04:20 -07001356 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001357 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001358 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001359
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001360 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001361 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001362
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001363 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001364}
1365
Joe Perches63ab52d2010-10-26 14:22:51 -07001366sub vcs_find_author {
1367 my ($cmd) = @_;
1368 my @lines = ();
1369
1370 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1371
1372 if (!$email_git_penguin_chiefs) {
1373 @lines = grep(!/${penguin_chiefs}/i, @lines);
1374 }
1375
1376 return @lines if !@lines;
1377
Joe Perches683c6f82010-10-26 14:22:55 -07001378 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001379 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001380 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1381 my $author = $1;
1382 my ($name, $address) = parse_email($author);
1383 $author = format_email($name, $address, 1);
1384 push(@authors, $author);
1385 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001386 }
1387
Joe Perches683c6f82010-10-26 14:22:55 -07001388 save_commits_by_author(@lines) if ($interactive);
1389 save_commits_by_signer(@lines) if ($interactive);
1390
1391 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001392}
1393
Joe Perches60db31a2009-12-14 18:00:50 -08001394sub vcs_save_commits {
1395 my ($cmd) = @_;
1396 my @lines = ();
1397 my @commits = ();
1398
1399 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1400
1401 foreach my $line (@lines) {
1402 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1403 push(@commits, $1);
1404 }
1405 }
1406
1407 return @commits;
1408}
1409
1410sub vcs_blame {
1411 my ($file) = @_;
1412 my $cmd;
1413 my @commits = ();
1414
1415 return @commits if (!(-f $file));
1416
1417 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1418 my @all_commits = ();
1419
1420 $cmd = $VCS_cmds{"blame_file_cmd"};
1421 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1422 @all_commits = vcs_save_commits($cmd);
1423
1424 foreach my $file_range_diff (@range) {
1425 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1426 my $diff_file = $1;
1427 my $diff_start = $2;
1428 my $diff_length = $3;
1429 next if ("$file" ne "$diff_file");
1430 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1431 push(@commits, $all_commits[$i]);
1432 }
1433 }
1434 } elsif (@range) {
1435 foreach my $file_range_diff (@range) {
1436 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1437 my $diff_file = $1;
1438 my $diff_start = $2;
1439 my $diff_length = $3;
1440 next if ("$file" ne "$diff_file");
1441 $cmd = $VCS_cmds{"blame_range_cmd"};
1442 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1443 push(@commits, vcs_save_commits($cmd));
1444 }
1445 } else {
1446 $cmd = $VCS_cmds{"blame_file_cmd"};
1447 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1448 @commits = vcs_save_commits($cmd);
1449 }
1450
Joe Perches63ab52d2010-10-26 14:22:51 -07001451 foreach my $commit (@commits) {
1452 $commit =~ s/^\^//g;
1453 }
1454
Joe Perches60db31a2009-12-14 18:00:50 -08001455 return @commits;
1456}
1457
1458my $printed_novcs = 0;
1459sub vcs_exists {
1460 %VCS_cmds = %VCS_cmds_git;
1461 return 1 if eval $VCS_cmds{"available"};
1462 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001463 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001464 %VCS_cmds = ();
1465 if (!$printed_novcs) {
1466 warn("$P: No supported VCS found. Add --nogit to options?\n");
1467 warn("Using a git repository produces better results.\n");
1468 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001469 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001470 $printed_novcs = 1;
1471 }
1472 return 0;
1473}
1474
Joe Perches683c6f82010-10-26 14:22:55 -07001475sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001476 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001477 return $vcs_used == 1;
1478}
1479
1480sub vcs_is_hg {
1481 return $vcs_used == 2;
1482}
1483
Joe Perches6ef1c522010-10-26 14:22:56 -07001484sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001485 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001486 my @list = @$list_ref;
1487
Joe Perches683c6f82010-10-26 14:22:55 -07001488 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001489
1490 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001491 my %authored;
1492 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001493 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001494 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001495 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001496 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1497 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001498 $authored{$count} = 0;
1499 $signed{$count} = 0;
1500 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001501 }
1502
1503 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001504 my $done = 0;
1505 my $print_options = 0;
1506 my $redraw = 1;
1507 while (!$done) {
1508 $count = 0;
1509 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001510 printf STDERR "\n%1s %2s %-65s",
1511 "*", "#", "email/list and role:stats";
1512 if ($email_git ||
1513 ($email_git_fallback && !$maintained) ||
1514 $email_git_blame) {
1515 print STDERR "auth sign";
1516 }
1517 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001518 foreach my $entry (@list) {
1519 my $email = $entry->[0];
1520 my $role = $entry->[1];
1521 my $sel = "";
1522 $sel = "*" if ($selected{$count});
1523 my $commit_author = $commit_author_hash{$email};
1524 my $commit_signer = $commit_signer_hash{$email};
1525 my $authored = 0;
1526 my $signed = 0;
1527 $authored++ for (@{$commit_author});
1528 $signed++ for (@{$commit_signer});
1529 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1530 printf STDERR "%4d %4d", $authored, $signed
1531 if ($authored > 0 || $signed > 0);
1532 printf STDERR "\n %s\n", $role;
1533 if ($authored{$count}) {
1534 my $commit_author = $commit_author_hash{$email};
1535 foreach my $ref (@{$commit_author}) {
1536 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001537 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001538 }
Joe Perches683c6f82010-10-26 14:22:55 -07001539 if ($signed{$count}) {
1540 my $commit_signer = $commit_signer_hash{$email};
1541 foreach my $ref (@{$commit_signer}) {
1542 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1543 }
1544 }
1545
1546 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001547 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001548 }
Joe Perches683c6f82010-10-26 14:22:55 -07001549 my $date_ref = \$email_git_since;
1550 $date_ref = \$email_hg_since if (vcs_is_hg());
1551 if ($print_options) {
1552 $print_options = 0;
1553 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001554 print STDERR <<EOT
1555
1556Version Control options:
1557g use git history [$email_git]
1558gf use git-fallback [$email_git_fallback]
1559b use git blame [$email_git_blame]
1560bs use blame signatures [$email_git_blame_signatures]
1561c# minimum commits [$email_git_min_signatures]
1562%# min percent [$email_git_min_percent]
1563d# history to use [$$date_ref]
1564x# max maintainers [$email_git_max_maintainers]
1565t all signature types [$email_git_all_signature_types]
1566m use .mailmap [$email_use_mailmap]
1567EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001568 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001569 print STDERR <<EOT
1570
1571Additional options:
15720 toggle all
1573tm toggle maintainers
1574tg toggle git entries
1575tl toggle open list entries
1576ts toggle subscriber list entries
1577f emails in file [$file_emails]
1578k keywords in file [$keywords]
1579r remove duplicates [$email_remove_duplicates]
1580p# pattern match depth [$pattern_depth]
1581EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001582 }
1583 print STDERR
1584"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1585
1586 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001587 chomp($input);
1588
Joe Perches683c6f82010-10-26 14:22:55 -07001589 $redraw = 1;
1590 my $rerun = 0;
1591 my @wish = split(/[, ]+/, $input);
1592 foreach my $nr (@wish) {
1593 $nr = lc($nr);
1594 my $sel = substr($nr, 0, 1);
1595 my $str = substr($nr, 1);
1596 my $val = 0;
1597 $val = $1 if $str =~ /^(\d+)$/;
1598
1599 if ($sel eq "y") {
1600 $interactive = 0;
1601 $done = 1;
1602 $output_rolestats = 0;
1603 $output_roles = 0;
1604 last;
1605 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1606 $selected{$nr - 1} = !$selected{$nr - 1};
1607 } elsif ($sel eq "*" || $sel eq '^') {
1608 my $toggle = 0;
1609 $toggle = 1 if ($sel eq '*');
1610 for (my $i = 0; $i < $count; $i++) {
1611 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001612 }
Joe Perches683c6f82010-10-26 14:22:55 -07001613 } elsif ($sel eq "0") {
1614 for (my $i = 0; $i < $count; $i++) {
1615 $selected{$i} = !$selected{$i};
1616 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001617 } elsif ($sel eq "t") {
1618 if (lc($str) eq "m") {
1619 for (my $i = 0; $i < $count; $i++) {
1620 $selected{$i} = !$selected{$i}
1621 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1622 }
1623 } elsif (lc($str) eq "g") {
1624 for (my $i = 0; $i < $count; $i++) {
1625 $selected{$i} = !$selected{$i}
1626 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1627 }
1628 } elsif (lc($str) eq "l") {
1629 for (my $i = 0; $i < $count; $i++) {
1630 $selected{$i} = !$selected{$i}
1631 if ($list[$i]->[1] =~ /^(open list)/i);
1632 }
1633 } elsif (lc($str) eq "s") {
1634 for (my $i = 0; $i < $count; $i++) {
1635 $selected{$i} = !$selected{$i}
1636 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1637 }
1638 }
Joe Perches683c6f82010-10-26 14:22:55 -07001639 } elsif ($sel eq "a") {
1640 if ($val > 0 && $val <= $count) {
1641 $authored{$val - 1} = !$authored{$val - 1};
1642 } elsif ($str eq '*' || $str eq '^') {
1643 my $toggle = 0;
1644 $toggle = 1 if ($str eq '*');
1645 for (my $i = 0; $i < $count; $i++) {
1646 $authored{$i} = $toggle;
1647 }
1648 }
1649 } elsif ($sel eq "s") {
1650 if ($val > 0 && $val <= $count) {
1651 $signed{$val - 1} = !$signed{$val - 1};
1652 } elsif ($str eq '*' || $str eq '^') {
1653 my $toggle = 0;
1654 $toggle = 1 if ($str eq '*');
1655 for (my $i = 0; $i < $count; $i++) {
1656 $signed{$i} = $toggle;
1657 }
1658 }
1659 } elsif ($sel eq "o") {
1660 $print_options = 1;
1661 $redraw = 1;
1662 } elsif ($sel eq "g") {
1663 if ($str eq "f") {
1664 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001665 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001666 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001667 }
Joe Perches683c6f82010-10-26 14:22:55 -07001668 $rerun = 1;
1669 } elsif ($sel eq "b") {
1670 if ($str eq "s") {
1671 bool_invert(\$email_git_blame_signatures);
1672 } else {
1673 bool_invert(\$email_git_blame);
1674 }
1675 $rerun = 1;
1676 } elsif ($sel eq "c") {
1677 if ($val > 0) {
1678 $email_git_min_signatures = $val;
1679 $rerun = 1;
1680 }
1681 } elsif ($sel eq "x") {
1682 if ($val > 0) {
1683 $email_git_max_maintainers = $val;
1684 $rerun = 1;
1685 }
1686 } elsif ($sel eq "%") {
1687 if ($str ne "" && $val >= 0) {
1688 $email_git_min_percent = $val;
1689 $rerun = 1;
1690 }
1691 } elsif ($sel eq "d") {
1692 if (vcs_is_git()) {
1693 $email_git_since = $str;
1694 } elsif (vcs_is_hg()) {
1695 $email_hg_since = $str;
1696 }
1697 $rerun = 1;
1698 } elsif ($sel eq "t") {
1699 bool_invert(\$email_git_all_signature_types);
1700 $rerun = 1;
1701 } elsif ($sel eq "f") {
1702 bool_invert(\$file_emails);
1703 $rerun = 1;
1704 } elsif ($sel eq "r") {
1705 bool_invert(\$email_remove_duplicates);
1706 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001707 } elsif ($sel eq "m") {
1708 bool_invert(\$email_use_mailmap);
1709 read_mailmap();
1710 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001711 } elsif ($sel eq "k") {
1712 bool_invert(\$keywords);
1713 $rerun = 1;
1714 } elsif ($sel eq "p") {
1715 if ($str ne "" && $val >= 0) {
1716 $pattern_depth = $val;
1717 $rerun = 1;
1718 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001719 } elsif ($sel eq "h" || $sel eq "?") {
1720 print STDERR <<EOT
1721
1722Interactive mode allows you to select the various maintainers, submitters,
1723commit signers and mailing lists that could be CC'd on a patch.
1724
1725Any *'d entry is selected.
1726
Joe Perches47abc722010-10-26 14:22:57 -07001727If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001728history of files in the patch. Also, each line of the current file can
1729be matched to its commit author and that commits signers with blame.
1730
1731Various knobs exist to control the length of time for active commit
1732tracking, the maximum number of commit authors and signers to add,
1733and such.
1734
1735Enter selections at the prompt until you are satisfied that the selected
1736maintainers are appropriate. You may enter multiple selections separated
1737by either commas or spaces.
1738
1739EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001740 } else {
1741 print STDERR "invalid option: '$nr'\n";
1742 $redraw = 0;
1743 }
1744 }
1745 if ($rerun) {
1746 print STDERR "git-blame can be very slow, please have patience..."
1747 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001748 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001749 }
1750 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001751
1752 #drop not selected entries
1753 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001754 my @new_emailto = ();
1755 foreach my $entry (@list) {
1756 if ($selected{$count}) {
1757 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001758 }
1759 $count++;
1760 }
Joe Perches683c6f82010-10-26 14:22:55 -07001761 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001762}
1763
Joe Perches683c6f82010-10-26 14:22:55 -07001764sub bool_invert {
1765 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001766
Joe Perches683c6f82010-10-26 14:22:55 -07001767 if ($$bool_ref) {
1768 $$bool_ref = 0;
1769 } else {
1770 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001771 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001772}
1773
Joe Perchesb9e23312010-10-26 14:22:58 -07001774sub deduplicate_email {
1775 my ($email) = @_;
1776
1777 my $matched = 0;
1778 my ($name, $address) = parse_email($email);
1779 $email = format_email($name, $address, 1);
1780 $email = mailmap_email($email);
1781
1782 return $email if (!$email_remove_duplicates);
1783
1784 ($name, $address) = parse_email($email);
1785
Joe Perchesfae99202010-10-26 14:22:58 -07001786 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001787 $name = $deduplicate_name_hash{lc($name)}->[0];
1788 $address = $deduplicate_name_hash{lc($name)}->[1];
1789 $matched = 1;
1790 } elsif ($deduplicate_address_hash{lc($address)}) {
1791 $name = $deduplicate_address_hash{lc($address)}->[0];
1792 $address = $deduplicate_address_hash{lc($address)}->[1];
1793 $matched = 1;
1794 }
1795 if (!$matched) {
1796 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1797 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1798 }
1799 $email = format_email($name, $address, 1);
1800 $email = mailmap_email($email);
1801 return $email;
1802}
1803
Joe Perches683c6f82010-10-26 14:22:55 -07001804sub save_commits_by_author {
1805 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001806
Joe Perches683c6f82010-10-26 14:22:55 -07001807 my @authors = ();
1808 my @commits = ();
1809 my @subjects = ();
1810
1811 foreach my $line (@lines) {
1812 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1813 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001814 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001815 push(@authors, $author);
1816 }
1817 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1818 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1819 }
1820
1821 for (my $i = 0; $i < @authors; $i++) {
1822 my $exists = 0;
1823 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1824 if (@{$ref}[0] eq $commits[$i] &&
1825 @{$ref}[1] eq $subjects[$i]) {
1826 $exists = 1;
1827 last;
1828 }
1829 }
1830 if (!$exists) {
1831 push(@{$commit_author_hash{$authors[$i]}},
1832 [ ($commits[$i], $subjects[$i]) ]);
1833 }
1834 }
1835}
1836
1837sub save_commits_by_signer {
1838 my (@lines) = @_;
1839
1840 my $commit = "";
1841 my $subject = "";
1842
1843 foreach my $line (@lines) {
1844 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1845 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1846 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1847 my @signatures = ($line);
1848 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1849 my @types = @$types_ref;
1850 my @signers = @$signers_ref;
1851
1852 my $type = $types[0];
1853 my $signer = $signers[0];
1854
Joe Perchesb9e23312010-10-26 14:22:58 -07001855 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001856
Joe Perches683c6f82010-10-26 14:22:55 -07001857 my $exists = 0;
1858 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1859 if (@{$ref}[0] eq $commit &&
1860 @{$ref}[1] eq $subject &&
1861 @{$ref}[2] eq $type) {
1862 $exists = 1;
1863 last;
1864 }
1865 }
1866 if (!$exists) {
1867 push(@{$commit_signer_hash{$signer}},
1868 [ ($commit, $subject, $type) ]);
1869 }
1870 }
1871 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001872}
1873
Joe Perches60db31a2009-12-14 18:00:50 -08001874sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001875 my ($role, $divisor, @lines) = @_;
1876
1877 my %hash;
1878 my $count = 0;
1879
Joe Perchesa8af2432009-12-14 18:00:49 -08001880 return if (@lines <= 0);
1881
1882 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001883 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001884 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001885 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001886
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001887 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001888
Joe Perches63ab52d2010-10-26 14:22:51 -07001889 return if (@lines <= 0);
1890
Joe Perches0e70e832009-09-21 17:04:20 -07001891 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001892
Joe Perches11ecf532009-09-21 17:04:22 -07001893 # uniq -c
1894 $hash{$_}++ for @lines;
1895
1896 # sort -rn
1897 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1898 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001899 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001900
Joe Perchesa8af2432009-12-14 18:00:49 -08001901 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07001902 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07001903 $count++;
1904 last if ($sign_offs < $email_git_min_signatures ||
1905 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001906 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001907 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001908 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001909 my $fmt_percent = sprintf("%.0f", $percent);
1910 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1911 } else {
1912 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001913 }
Joe Perchesf5492662009-09-21 17:04:13 -07001914 }
1915}
1916
Joe Perches60db31a2009-12-14 18:00:50 -08001917sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001918 my ($file) = @_;
1919
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001920 my $authors_ref;
1921 my $signers_ref;
1922 my $stats_ref;
1923 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001924 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001925 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001926 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001927
Joe Perches683c6f82010-10-26 14:22:55 -07001928 $vcs_used = vcs_exists();
1929 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001930
Joe Perches60db31a2009-12-14 18:00:50 -08001931 my $cmd = $VCS_cmds{"find_signers_cmd"};
1932 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1933
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001934 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1935
1936 @signers = @{$signers_ref} if defined $signers_ref;
1937 @authors = @{$authors_ref} if defined $authors_ref;
1938 @stats = @{$stats_ref} if defined $stats_ref;
1939
1940# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07001941
1942 foreach my $signer (@signers) {
1943 $signer = deduplicate_email($signer);
1944 }
1945
Joe Perches60db31a2009-12-14 18:00:50 -08001946 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001947 vcs_assign("authored", $commits, @authors);
1948 if ($#authors == $#stats) {
1949 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1950 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
1951
1952 my $added = 0;
1953 my $deleted = 0;
1954 for (my $i = 0; $i <= $#stats; $i++) {
1955 if ($stats[$i] =~ /$stat_pattern/) {
1956 $added += $1;
1957 $deleted += $2;
1958 }
1959 }
1960 my @tmp_authors = uniq(@authors);
1961 foreach my $author (@tmp_authors) {
1962 $author = deduplicate_email($author);
1963 }
1964 @tmp_authors = uniq(@tmp_authors);
1965 my @list_added = ();
1966 my @list_deleted = ();
1967 foreach my $author (@tmp_authors) {
1968 my $auth_added = 0;
1969 my $auth_deleted = 0;
1970 for (my $i = 0; $i <= $#stats; $i++) {
1971 if ($author eq deduplicate_email($authors[$i]) &&
1972 $stats[$i] =~ /$stat_pattern/) {
1973 $auth_added += $1;
1974 $auth_deleted += $2;
1975 }
1976 }
1977 for (my $i = 0; $i < $auth_added; $i++) {
1978 push(@list_added, $author);
1979 }
1980 for (my $i = 0; $i < $auth_deleted; $i++) {
1981 push(@list_deleted, $author);
1982 }
1983 }
1984 vcs_assign("added_lines", $added, @list_added);
1985 vcs_assign("removed_lines", $deleted, @list_deleted);
1986 }
Joe Perchesa8af2432009-12-14 18:00:49 -08001987}
1988
Joe Perches60db31a2009-12-14 18:00:50 -08001989sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001990 my ($file) = @_;
1991
Joe Perches60db31a2009-12-14 18:00:50 -08001992 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001993 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001994 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001995 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001996 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001997
Joe Perches683c6f82010-10-26 14:22:55 -07001998 $vcs_used = vcs_exists();
1999 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002000
Joe Perches63ab52d2010-10-26 14:22:51 -07002001 @all_commits = vcs_blame($file);
2002 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002003 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002004 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002005
Joe Perches683c6f82010-10-26 14:22:55 -07002006 if ($email_git_blame_signatures) {
2007 if (vcs_is_hg()) {
2008 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002009 my $commit_authors_ref;
2010 my $commit_signers_ref;
2011 my $stats_ref;
2012 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002013 my @commit_signers = ();
2014 my $commit = join(" -r ", @commits);
2015 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002016
Joe Perches683c6f82010-10-26 14:22:55 -07002017 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2018 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002019
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002020 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2021 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2022 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002023
Joe Perches683c6f82010-10-26 14:22:55 -07002024 push(@signers, @commit_signers);
2025 } else {
2026 foreach my $commit (@commits) {
2027 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002028 my $commit_authors_ref;
2029 my $commit_signers_ref;
2030 my $stats_ref;
2031 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002032 my @commit_signers = ();
2033 my $cmd;
2034
2035 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2036 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2037
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002038 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2039 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2040 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002041
2042 push(@signers, @commit_signers);
2043 }
2044 }
Joe Perchesf5492662009-09-21 17:04:13 -07002045 }
2046
Joe Perchesa8af2432009-12-14 18:00:49 -08002047 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002048 if ($output_rolestats) {
2049 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002050 if (vcs_is_hg()) {{ # Double brace for last exit
2051 my $commit_count;
2052 my @commit_signers = ();
2053 @commits = uniq(@commits);
2054 @commits = sort(@commits);
2055 my $commit = join(" -r ", @commits);
2056 my $cmd;
2057
2058 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2059 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2060
2061 my @lines = ();
2062
2063 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2064
2065 if (!$email_git_penguin_chiefs) {
2066 @lines = grep(!/${penguin_chiefs}/i, @lines);
2067 }
2068
2069 last if !@lines;
2070
2071 my @authors = ();
2072 foreach my $line (@lines) {
2073 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2074 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002075 $author = deduplicate_email($author);
2076 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002077 }
2078 }
2079
2080 save_commits_by_author(@lines) if ($interactive);
2081 save_commits_by_signer(@lines) if ($interactive);
2082
2083 push(@signers, @authors);
2084 }}
2085 else {
2086 foreach my $commit (@commits) {
2087 my $i;
2088 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2089 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2090 my @author = vcs_find_author($cmd);
2091 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002092
2093 my $formatted_author = deduplicate_email($author[0]);
2094
Joe Perches683c6f82010-10-26 14:22:55 -07002095 my $count = grep(/$commit/, @all_commits);
2096 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002097 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002098 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002099 }
2100 }
2101 if (@blame_signers) {
2102 vcs_assign("authored lines", $total_lines, @blame_signers);
2103 }
2104 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002105 foreach my $signer (@signers) {
2106 $signer = deduplicate_email($signer);
2107 }
Joe Perches60db31a2009-12-14 18:00:50 -08002108 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002109 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002110 foreach my $signer (@signers) {
2111 $signer = deduplicate_email($signer);
2112 }
Joe Perches60db31a2009-12-14 18:00:50 -08002113 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002114 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002115}
2116
2117sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002118 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002119
2120 my %saw;
2121 @parms = grep(!$saw{$_}++, @parms);
2122 return @parms;
2123}
2124
2125sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002126 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002127
2128 my %saw;
2129 @parms = sort @parms;
2130 @parms = grep(!$saw{$_}++, @parms);
2131 return @parms;
2132}
2133
Joe Perches03372db2010-03-05 13:43:00 -08002134sub clean_file_emails {
2135 my (@file_emails) = @_;
2136 my @fmt_emails = ();
2137
2138 foreach my $email (@file_emails) {
2139 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2140 my ($name, $address) = parse_email($email);
2141 if ($name eq '"[,\.]"') {
2142 $name = "";
2143 }
2144
2145 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2146 if (@nw > 2) {
2147 my $first = $nw[@nw - 3];
2148 my $middle = $nw[@nw - 2];
2149 my $last = $nw[@nw - 1];
2150
2151 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2152 (length($first) == 2 && substr($first, -1) eq ".")) ||
2153 (length($middle) == 1 ||
2154 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2155 $name = "$first $middle $last";
2156 } else {
2157 $name = "$middle $last";
2158 }
2159 }
2160
2161 if (substr($name, -1) =~ /[,\.]/) {
2162 $name = substr($name, 0, length($name) - 1);
2163 } elsif (substr($name, -2) =~ /[,\.]"/) {
2164 $name = substr($name, 0, length($name) - 2) . '"';
2165 }
2166
2167 if (substr($name, 0, 1) =~ /[,\.]/) {
2168 $name = substr($name, 1, length($name) - 1);
2169 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2170 $name = '"' . substr($name, 2, length($name) - 2);
2171 }
2172
2173 my $fmt_email = format_email($name, $address, $email_usename);
2174 push(@fmt_emails, $fmt_email);
2175 }
2176 return @fmt_emails;
2177}
2178
Joe Perches3c7385b2009-12-14 18:00:46 -08002179sub merge_email {
2180 my @lines;
2181 my %saw;
2182
2183 for (@_) {
2184 my ($address, $role) = @$_;
2185 if (!$saw{$address}) {
2186 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002187 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002188 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002189 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002190 }
2191 $saw{$address} = 1;
2192 }
2193 }
2194
2195 return @lines;
2196}
2197
Joe Perchescb7301c2009-04-07 20:40:12 -07002198sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002199 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002200
2201 if ($output_multiline) {
2202 foreach my $line (@parms) {
2203 print("${line}\n");
2204 }
2205 } else {
2206 print(join($output_separator, @parms));
2207 print("\n");
2208 }
2209}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002210
2211my $rfc822re;
2212
2213sub make_rfc822re {
2214# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2215# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2216# This regexp will only work on addresses which have had comments stripped
2217# and replaced with rfc822_lwsp.
2218
2219 my $specials = '()<>@,;:\\\\".\\[\\]';
2220 my $controls = '\\000-\\037\\177';
2221
2222 my $dtext = "[^\\[\\]\\r\\\\]";
2223 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2224
2225 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2226
2227# Use zero-width assertion to spot the limit of an atom. A simple
2228# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2229 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2230 my $word = "(?:$atom|$quoted_string)";
2231 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2232
2233 my $sub_domain = "(?:$atom|$domain_literal)";
2234 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2235
2236 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2237
2238 my $phrase = "$word*";
2239 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2240 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2241 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2242
2243 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2244 my $address = "(?:$mailbox|$group)";
2245
2246 return "$rfc822_lwsp*$address";
2247}
2248
2249sub rfc822_strip_comments {
2250 my $s = shift;
2251# Recursively remove comments, and replace with a single space. The simpler
2252# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2253# chars in atoms, for example.
2254
2255 while ($s =~ s/^((?:[^"\\]|\\.)*
2256 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2257 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2258 return $s;
2259}
2260
2261# valid: returns true if the parameter is an RFC822 valid address
2262#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002263sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002264 my $s = rfc822_strip_comments(shift);
2265
2266 if (!$rfc822re) {
2267 $rfc822re = make_rfc822re();
2268 }
2269
2270 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2271}
2272
2273# validlist: In scalar context, returns true if the parameter is an RFC822
2274# valid list of addresses.
2275#
2276# In list context, returns an empty list on failure (an invalid
2277# address was found); otherwise a list whose first element is the
2278# number of addresses found and whose remaining elements are the
2279# addresses. This is needed to disambiguate failure (invalid)
2280# from success with no addresses found, because an empty string is
2281# a valid list.
2282
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002283sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002284 my $s = rfc822_strip_comments(shift);
2285
2286 if (!$rfc822re) {
2287 $rfc822re = make_rfc822re();
2288 }
2289 # * null list items are valid according to the RFC
2290 # * the '1' business is to aid in distinguishing failure from no results
2291
2292 my @r;
2293 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2294 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002295 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002296 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002297 }
2298 return wantarray ? (scalar(@r), @r) : 1;
2299 }
Joe Perches60db31a2009-12-14 18:00:50 -08002300 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002301}