blob: 5e4fb144a04f657ebd43fb0c64145147d522d7d1 [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;
24my $email_list = 1;
25my $email_subscriber_list = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070026my $email_git_penguin_chiefs = 0;
Joe Perchese3e9d112010-10-26 14:22:53 -070027my $email_git = 0;
Florian Mickler0fa05592010-05-24 14:33:20 -070028my $email_git_all_signature_types = 0;
Joe Perches60db31a2009-12-14 18:00:50 -080029my $email_git_blame = 0;
Joe Perches683c6f82010-10-26 14:22:55 -070030my $email_git_blame_signatures = 1;
Joe Perchese3e9d112010-10-26 14:22:53 -070031my $email_git_fallback = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070032my $email_git_min_signatures = 1;
33my $email_git_max_maintainers = 5;
Joe Perchesafa81ee2009-07-29 15:04:28 -070034my $email_git_min_percent = 5;
Joe Perchescb7301c2009-04-07 20:40:12 -070035my $email_git_since = "1-year-ago";
Joe Perches60db31a2009-12-14 18:00:50 -080036my $email_hg_since = "-365";
Florian Micklerdace8e32010-10-26 14:22:54 -070037my $interactive = 0;
Joe Perches11ecf532009-09-21 17:04:22 -070038my $email_remove_duplicates = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -070039my $email_use_mailmap = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070040my $output_multiline = 1;
41my $output_separator = ", ";
Joe Perches3c7385b2009-12-14 18:00:46 -080042my $output_roles = 0;
Joe Perches7e1863a2011-01-12 16:59:49 -080043my $output_rolestats = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070044my $scm = 0;
45my $web = 0;
46my $subsystem = 0;
47my $status = 0;
Joe Perchesdcf36a92009-10-26 16:49:47 -070048my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080049my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080050my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070051my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070052my $pattern_depth = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070053my $version = 0;
54my $help = 0;
55
Joe Perches683c6f82010-10-26 14:22:55 -070056my $vcs_used = 0;
57
Joe Perchescb7301c2009-04-07 20:40:12 -070058my $exit = 0;
59
Joe Perches683c6f82010-10-26 14:22:55 -070060my %commit_author_hash;
61my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070062
Joe Perchescb7301c2009-04-07 20:40:12 -070063my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070064push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070065#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070066#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070067
68my @penguin_chief_names = ();
69foreach my $chief (@penguin_chief) {
70 if ($chief =~ m/^(.*):(.*)/) {
71 my $chief_name = $1;
72 my $chief_addr = $2;
73 push(@penguin_chief_names, $chief_name);
74 }
75}
Joe Perchese4d26b02010-05-24 14:33:17 -070076my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
77
78# Signature types of people who are either
79# a) responsible for the code in question, or
80# b) familiar enough with it to give relevant feedback
81my @signature_tags = ();
82push(@signature_tags, "Signed-off-by:");
83push(@signature_tags, "Reviewed-by:");
84push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070085
Joe Perches7dea2682012-06-20 12:53:02 -070086my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
87
Joe Perches5f2441e2009-06-16 15:34:02 -070088# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070089my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070090my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070091
Joe Perches60db31a2009-12-14 18:00:50 -080092# VCS command support: class-like functions and strings
93
94my %VCS_cmds;
95
96my %VCS_cmds_git = (
97 "execute_cmd" => \&git_execute_cmd,
98 "available" => '(which("git") ne "") && (-d ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -070099 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800100 "git log --no-color --follow --since=\$email_git_since " .
Joe Perches683c6f82010-10-26 14:22:55 -0700101 '--format="GitCommit: %H%n' .
102 'GitAuthor: %an <%ae>%n' .
103 'GitDate: %aD%n' .
104 'GitSubject: %s%n' .
105 '%b%n"' .
106 " -- \$file",
107 "find_commit_signers_cmd" =>
108 "git log --no-color " .
109 '--format="GitCommit: %H%n' .
110 'GitAuthor: %an <%ae>%n' .
111 'GitDate: %aD%n' .
112 'GitSubject: %s%n' .
113 '%b%n"' .
114 " -1 \$commit",
115 "find_commit_author_cmd" =>
116 "git log --no-color " .
117 '--format="GitCommit: %H%n' .
118 'GitAuthor: %an <%ae>%n' .
119 'GitDate: %aD%n' .
120 'GitSubject: %s%n"' .
121 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800122 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
123 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700124 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700125 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700126 "author_pattern" => "^GitAuthor: (.*)",
127 "subject_pattern" => "^GitSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800128);
129
130my %VCS_cmds_hg = (
131 "execute_cmd" => \&hg_execute_cmd,
132 "available" => '(which("hg") ne "") && (-d ".hg")',
133 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700134 "hg log --date=\$email_hg_since " .
135 "--template='HgCommit: {node}\\n" .
136 "HgAuthor: {author}\\n" .
137 "HgSubject: {desc}\\n'" .
138 " -- \$file",
139 "find_commit_signers_cmd" =>
140 "hg log " .
141 "--template='HgSubject: {desc}\\n'" .
142 " -r \$commit",
143 "find_commit_author_cmd" =>
144 "hg log " .
145 "--template='HgCommit: {node}\\n" .
146 "HgAuthor: {author}\\n" .
147 "HgSubject: {desc|firstline}\\n'" .
148 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800149 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700150 "blame_file_cmd" => "hg blame -n \$file",
151 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
152 "blame_commit_pattern" => "^([ 0-9a-f]+):",
153 "author_pattern" => "^HgAuthor: (.*)",
154 "subject_pattern" => "^HgSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800155);
156
Joe Perchesbcde44e2010-10-26 14:22:53 -0700157my $conf = which_conf(".get_maintainer.conf");
158if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700159 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700160 open(my $conffile, '<', "$conf")
161 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
162
Joe Perches368669d2010-05-24 14:33:19 -0700163 while (<$conffile>) {
164 my $line = $_;
165
166 $line =~ s/\s*\n?$//g;
167 $line =~ s/^\s*//g;
168 $line =~ s/\s+/ /g;
169
170 next if ($line =~ m/^\s*#/);
171 next if ($line =~ m/^\s*$/);
172
173 my @words = split(" ", $line);
174 foreach my $word (@words) {
175 last if ($word =~ m/^#/);
176 push (@conf_args, $word);
177 }
178 }
179 close($conffile);
180 unshift(@ARGV, @conf_args) if @conf_args;
181}
182
Joe Perchescb7301c2009-04-07 20:40:12 -0700183if (!GetOptions(
184 'email!' => \$email,
185 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700186 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800187 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700188 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700189 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700190 'git-chief-penguins!' => \$email_git_penguin_chiefs,
191 'git-min-signatures=i' => \$email_git_min_signatures,
192 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700193 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700194 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800195 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700196 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700197 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700198 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700199 'm!' => \$email_maintainer,
200 'n!' => \$email_usename,
201 'l!' => \$email_list,
202 's!' => \$email_subscriber_list,
203 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800204 'roles!' => \$output_roles,
205 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700206 'separator=s' => \$output_separator,
207 'subsystem!' => \$subsystem,
208 'status!' => \$status,
209 'scm!' => \$scm,
210 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700211 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700212 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800213 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800214 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700215 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700216 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800217 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700218 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800219 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700220}
221
222if ($help != 0) {
223 usage();
224 exit 0;
225}
226
227if ($version != 0) {
228 print("${P} ${V}\n");
229 exit 0;
230}
231
Joe Perches64f77f32010-03-05 13:43:04 -0800232if (-t STDIN && !@ARGV) {
233 # We're talking to a terminal, but have no command line arguments.
234 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700235}
236
Joe Perches683c6f82010-10-26 14:22:55 -0700237$output_multiline = 0 if ($output_separator ne ", ");
238$output_rolestats = 1 if ($interactive);
239$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800240
Joe Perches4b76c9d2010-03-05 13:43:03 -0800241if ($sections) {
242 $email = 0;
243 $email_list = 0;
244 $scm = 0;
245 $status = 0;
246 $subsystem = 0;
247 $web = 0;
248 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700249 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800250} else {
251 my $selections = $email + $scm + $status + $subsystem + $web;
252 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800253 die "$P: Missing required option: email, scm, status, subsystem or web\n";
254 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700255}
256
Joe Perchesf5492662009-09-21 17:04:13 -0700257if ($email &&
258 ($email_maintainer + $email_list + $email_subscriber_list +
259 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700260 die "$P: Please select at least 1 email option\n";
261}
262
263if (!top_of_kernel_tree($lk_path)) {
264 die "$P: The current directory does not appear to be "
265 . "a linux kernel source tree.\n";
266}
267
268## Read MAINTAINERS for type/value pairs
269
270my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700271my %keyword_hash;
272
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800273open (my $maint, '<', "${lk_path}MAINTAINERS")
274 or die "$P: Can't open MAINTAINERS: $!\n";
275while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700276 my $line = $_;
277
278 if ($line =~ m/^(\C):\s*(.*)/) {
279 my $type = $1;
280 my $value = $2;
281
282 ##Filename pattern matching
283 if ($type eq "F" || $type eq "X") {
284 $value =~ s@\.@\\\.@g; ##Convert . to \.
285 $value =~ s/\*/\.\*/g; ##Convert * to .*
286 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700287 ##if pattern is a directory and it lacks a trailing slash, add one
288 if ((-d $value)) {
289 $value =~ s@([^/])$@$1/@;
290 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700291 } elsif ($type eq "K") {
292 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700293 }
294 push(@typevalue, "$type:$value");
295 } elsif (!/^(\s)*$/) {
296 $line =~ s/\n$//g;
297 push(@typevalue, $line);
298 }
299}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800300close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700301
Joe Perches8cbb3a72009-09-21 17:04:21 -0700302
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700303#
304# Read mail address map
305#
306
Joe Perchesb9e23312010-10-26 14:22:58 -0700307my $mailmap;
308
309read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700310
311sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700312 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700313 names => {},
314 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700315 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700316
Joe Perchesb9e23312010-10-26 14:22:58 -0700317 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700318
319 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800320 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700321
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700322 while (<$mailmap_file>) {
323 s/#.*$//; #strip comments
324 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700325
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700326 next if (/^\s*$/); #skip empty lines
327 #entries have one of the following formats:
328 # name1 <mail1>
329 # <mail1> <mail2>
330 # name1 <mail1> <mail2>
331 # name1 <mail1> name2 <mail2>
332 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700333
334 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700335 my $real_name = $1;
336 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700337
Joe Perches47abc722010-10-26 14:22:57 -0700338 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700339 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700340 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700341
Joe Perches0334b382011-07-25 17:13:13 -0700342 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700343 my $real_address = $1;
344 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700345
Joe Perches47abc722010-10-26 14:22:57 -0700346 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700347
Joe Perches0334b382011-07-25 17:13:13 -0700348 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700349 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700350 my $real_address = $2;
351 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700352
Joe Perches47abc722010-10-26 14:22:57 -0700353 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700354 ($real_name, $real_address) =
355 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700356 $mailmap->{names}->{$wrong_address} = $real_name;
357 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700358
Joe Perches0334b382011-07-25 17:13:13 -0700359 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700360 my $real_name = $1;
361 my $real_address = $2;
362 my $wrong_name = $3;
363 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700364
Joe Perches47abc722010-10-26 14:22:57 -0700365 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700366 ($real_name, $real_address) =
367 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700368
Joe Perchesb9e23312010-10-26 14:22:58 -0700369 $wrong_name =~ s/\s+$//;
370 ($wrong_name, $wrong_address) =
371 parse_email("$wrong_name <$wrong_address>");
372
373 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
374 $mailmap->{names}->{$wrong_email} = $real_name;
375 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700376 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700377 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700378 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700379}
380
Joe Perches4a7fdb52009-04-10 12:28:57 -0700381## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700382
383my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700384my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700385my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800386my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700387
Joe Perches64f77f32010-03-05 13:43:04 -0800388if (!@ARGV) {
389 push(@ARGV, "&STDIN");
390}
391
Joe Perches4a7fdb52009-04-10 12:28:57 -0700392foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800393 if ($file ne "&STDIN") {
394 ##if $file is a directory and it lacks a trailing slash, add one
395 if ((-d $file)) {
396 $file =~ s@([^/])$@$1/@;
397 } elsif (!(-f $file)) {
398 die "$P: file '${file}' not found\n";
399 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700400 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700401 if ($from_filename) {
402 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700403 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800404 open(my $f, '<', $file)
405 or die "$P: Can't open $file: $!\n";
406 my $text = do { local($/) ; <$f> };
407 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800408 if ($keywords) {
409 foreach my $line (keys %keyword_hash) {
410 if ($text =~ m/$keyword_hash{$line}/x) {
411 push(@keyword_tvi, $line);
412 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700413 }
414 }
Joe Perches03372db2010-03-05 13:43:00 -0800415 if ($file_emails) {
416 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;
417 push(@file_emails, clean_file_emails(@poss_addr));
418 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700419 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700420 } else {
421 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700422 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800423
Wolfram Sang3a4df132010-03-23 13:35:18 -0700424 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800425 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700426
427 # We can check arbitrary information before the patch
428 # like the commit message, mail headers, etc...
429 # This allows us to match arbitrary keywords against any part
430 # of a git format-patch generated file (subject tags, etc...)
431
432 my $patch_prefix = ""; #Parsing the intro
433
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800434 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700435 my $patch_line = $_;
Geert Uytterhoeven6be07102013-02-21 16:43:12 -0800436 if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700437 my $filename = $1;
438 $filename =~ s@^[^/]*/@@;
439 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700440 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700441 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700442 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700443 } elsif (m/^\@\@ -(\d+),(\d+)/) {
444 if ($email_git_blame) {
445 push(@range, "$lastfile:$1:$2");
446 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700447 } elsif ($keywords) {
448 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700449 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700450 push(@keyword_tvi, $line);
451 }
452 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700453 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700454 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800455 close($patch);
456
Joe Perches4a7fdb52009-04-10 12:28:57 -0700457 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700458 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700459 . "Add -f to options?\n";
460 }
461 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700462 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700463}
464
Joe Perches03372db2010-03-05 13:43:00 -0800465@file_emails = uniq(@file_emails);
466
Joe Perches683c6f82010-10-26 14:22:55 -0700467my %email_hash_name;
468my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700469my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700470my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700471my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700472my @scm = ();
473my @web = ();
474my @subsystem = ();
475my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700476my %deduplicate_name_hash = ();
477my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700478
Joe Perches6ef1c522010-10-26 14:22:56 -0700479my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700480
Joe Perches6ef1c522010-10-26 14:22:56 -0700481if (@maintainers) {
482 @maintainers = merge_email(@maintainers);
483 output(@maintainers);
484}
Joe Perchescb7301c2009-04-07 20:40:12 -0700485
486if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700487 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700488 output(@scm);
489}
Joe Perches683c6f82010-10-26 14:22:55 -0700490
Joe Perchescb7301c2009-04-07 20:40:12 -0700491if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700492 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700493 output(@status);
494}
495
496if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700497 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700498 output(@subsystem);
499}
500
501if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700502 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700503 output(@web);
504}
505
506exit($exit);
507
Joe Perchesab6c9372011-01-12 16:59:50 -0800508sub range_is_maintained {
509 my ($start, $end) = @_;
510
511 for (my $i = $start; $i < $end; $i++) {
512 my $line = $typevalue[$i];
513 if ($line =~ m/^(\C):\s*(.*)/) {
514 my $type = $1;
515 my $value = $2;
516 if ($type eq 'S') {
517 if ($value =~ /(maintain|support)/i) {
518 return 1;
519 }
520 }
521 }
522 }
523 return 0;
524}
525
526sub range_has_maintainer {
527 my ($start, $end) = @_;
528
529 for (my $i = $start; $i < $end; $i++) {
530 my $line = $typevalue[$i];
531 if ($line =~ m/^(\C):\s*(.*)/) {
532 my $type = $1;
533 my $value = $2;
534 if ($type eq 'M') {
535 return 1;
536 }
537 }
538 }
539 return 0;
540}
541
Joe Perches6ef1c522010-10-26 14:22:56 -0700542sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700543 %email_hash_name = ();
544 %email_hash_address = ();
545 %commit_author_hash = ();
546 %commit_signer_hash = ();
547 @email_to = ();
548 %hash_list_to = ();
549 @list_to = ();
550 @scm = ();
551 @web = ();
552 @subsystem = ();
553 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700554 %deduplicate_name_hash = ();
555 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700556 if ($email_git_all_signature_types) {
557 $signature_pattern = "(.+?)[Bb][Yy]:";
558 } else {
559 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
560 }
561
562 # Find responsible parties
563
Joe Perchesb9e23312010-10-26 14:22:58 -0700564 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700565
Joe Perches683c6f82010-10-26 14:22:55 -0700566 foreach my $file (@files) {
567
568 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700569 my $tvi = find_first_section();
570 while ($tvi < @typevalue) {
571 my $start = find_starting_index($tvi);
572 my $end = find_ending_index($tvi);
573 my $exclude = 0;
574 my $i;
575
576 #Do not match excluded file patterns
577
578 for ($i = $start; $i < $end; $i++) {
579 my $line = $typevalue[$i];
580 if ($line =~ m/^(\C):\s*(.*)/) {
581 my $type = $1;
582 my $value = $2;
583 if ($type eq 'X') {
584 if (file_match_pattern($file, $value)) {
585 $exclude = 1;
586 last;
587 }
588 }
589 }
590 }
591
592 if (!$exclude) {
593 for ($i = $start; $i < $end; $i++) {
594 my $line = $typevalue[$i];
595 if ($line =~ m/^(\C):\s*(.*)/) {
596 my $type = $1;
597 my $value = $2;
598 if ($type eq 'F') {
599 if (file_match_pattern($file, $value)) {
600 my $value_pd = ($value =~ tr@/@@);
601 my $file_pd = ($file =~ tr@/@@);
602 $value_pd++ if (substr($value,-1,1) ne "/");
603 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800604 if ($value_pd >= $file_pd &&
605 range_is_maintained($start, $end) &&
606 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700607 $exact_pattern_match_hash{$file} = 1;
608 }
Joe Perches683c6f82010-10-26 14:22:55 -0700609 if ($pattern_depth == 0 ||
610 (($file_pd - $value_pd) < $pattern_depth)) {
611 $hash{$tvi} = $value_pd;
612 }
613 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700614 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800615 if ($file =~ m/$value/x) {
616 $hash{$tvi} = 0;
617 }
Joe Perches683c6f82010-10-26 14:22:55 -0700618 }
619 }
620 }
621 }
622 $tvi = $end + 1;
623 }
624
625 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
626 add_categories($line);
627 if ($sections) {
628 my $i;
629 my $start = find_starting_index($line);
630 my $end = find_ending_index($line);
631 for ($i = $start; $i < $end; $i++) {
632 my $line = $typevalue[$i];
633 if ($line =~ /^[FX]:/) { ##Restore file patterns
634 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
635 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
636 $line =~ s/\\\./\./g; ##Convert \. to .
637 $line =~ s/\.\*/\*/g; ##Convert .* to *
638 }
639 $line =~ s/^([A-Z]):/$1:\t/g;
640 print("$line\n");
641 }
642 print("\n");
643 }
644 }
Joe Perches683c6f82010-10-26 14:22:55 -0700645 }
646
647 if ($keywords) {
648 @keyword_tvi = sort_and_uniq(@keyword_tvi);
649 foreach my $line (@keyword_tvi) {
650 add_categories($line);
651 }
652 }
653
Joe Perchesb9e23312010-10-26 14:22:58 -0700654 foreach my $email (@email_to, @list_to) {
655 $email->[0] = deduplicate_email($email->[0]);
656 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700657
658 foreach my $file (@files) {
659 if ($email &&
660 ($email_git || ($email_git_fallback &&
661 !$exact_pattern_match_hash{$file}))) {
662 vcs_file_signoffs($file);
663 }
664 if ($email && $email_git_blame) {
665 vcs_file_blame($file);
666 }
667 }
668
Joe Perches683c6f82010-10-26 14:22:55 -0700669 if ($email) {
670 foreach my $chief (@penguin_chief) {
671 if ($chief =~ m/^(.*):(.*)/) {
672 my $email_address;
673
674 $email_address = format_email($1, $2, $email_usename);
675 if ($email_git_penguin_chiefs) {
676 push(@email_to, [$email_address, 'chief penguin']);
677 } else {
678 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
679 }
680 }
681 }
682
683 foreach my $email (@file_emails) {
684 my ($name, $address) = parse_email($email);
685
686 my $tmp_email = format_email($name, $address, $email_usename);
687 push_email_address($tmp_email, '');
688 add_role($tmp_email, 'in file');
689 }
690 }
691
692 my @to = ();
693 if ($email || $email_list) {
694 if ($email) {
695 @to = (@to, @email_to);
696 }
697 if ($email_list) {
698 @to = (@to, @list_to);
699 }
700 }
701
Joe Perches6ef1c522010-10-26 14:22:56 -0700702 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700703 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700704 }
Joe Perches683c6f82010-10-26 14:22:55 -0700705
706 return @to;
707}
708
Joe Perchescb7301c2009-04-07 20:40:12 -0700709sub file_match_pattern {
710 my ($file, $pattern) = @_;
711 if (substr($pattern, -1) eq "/") {
712 if ($file =~ m@^$pattern@) {
713 return 1;
714 }
715 } else {
716 if ($file =~ m@^$pattern@) {
717 my $s1 = ($file =~ tr@/@@);
718 my $s2 = ($pattern =~ tr@/@@);
719 if ($s1 == $s2) {
720 return 1;
721 }
722 }
723 }
724 return 0;
725}
726
727sub usage {
728 print <<EOT;
729usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700730 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700731version: $V
732
733MAINTAINER field selection options:
734 --email => print email address(es) if any
735 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700736 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700737 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700738 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700739 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700740 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
741 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
742 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700743 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700744 --git-since => git history to use (default: $email_git_since)
745 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700746 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700747 --m => include maintainer(s) if any
748 --n => include name 'Full Name <addr\@domain.tld>'
749 --l => include list(s) if any
750 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700751 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800752 --roles => show roles (status:subsystem, git-signer, list, etc...)
753 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800754 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700755 --scm => print SCM tree(s) if any
756 --status => print status if any
757 --subsystem => print subsystem name if any
758 --web => print website(s) if any
759
760Output type options:
761 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700762 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700763 --multiline => print 1 entry per line
764
Joe Perchescb7301c2009-04-07 20:40:12 -0700765Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700766 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700767 --keywords => scan patch for keywords (default: $keywords)
768 --sections => print all of the subsystem sections with pattern matches
769 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f5078d2009-06-16 15:34:00 -0700770 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700771 --help => show this help information
772
Joe Perches3fb55652009-09-21 17:04:17 -0700773Default options:
Joe Perches7e1863a2011-01-12 16:59:49 -0800774 [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
775 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700776
Joe Perches870020f2009-07-29 15:04:28 -0700777Notes:
778 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700779 Used with "--git", git signators for _all_ files in and below
780 directory are examined as git recurses directories.
781 Any specified X: (exclude) pattern matches are _not_ ignored.
782 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800783 no individual file within the directory or subdirectory
784 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700785 Used with "--git-blame", does not iterate all files in directory
786 Using "--git-blame" is slow and may add old committers and authors
787 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800788 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
789 other automated tools that expect only ["name"] <email address>
790 may not work because of additional output after <email address>.
791 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
792 not the percentage of the entire file authored. # of commits is
793 not a good measure of amount of code authored. 1 major commit may
794 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800795 If git is not installed, but mercurial (hg) is installed and an .hg
796 repository exists, the following options apply to mercurial:
797 --git,
798 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
799 --git-blame
800 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700801 File ".get_maintainer.conf", if it exists in the linux kernel source root
802 directory, can change whatever get_maintainer defaults are desired.
803 Entries in this file can be any command line argument.
804 This file is prepended to any additional command line arguments.
805 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700806EOT
807}
808
809sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700810 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700811
Joe Perches47abc722010-10-26 14:22:57 -0700812 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
813 $lk_path .= "/";
814 }
815 if ( (-f "${lk_path}COPYING")
816 && (-f "${lk_path}CREDITS")
817 && (-f "${lk_path}Kbuild")
818 && (-f "${lk_path}MAINTAINERS")
819 && (-f "${lk_path}Makefile")
820 && (-f "${lk_path}README")
821 && (-d "${lk_path}Documentation")
822 && (-d "${lk_path}arch")
823 && (-d "${lk_path}include")
824 && (-d "${lk_path}drivers")
825 && (-d "${lk_path}fs")
826 && (-d "${lk_path}init")
827 && (-d "${lk_path}ipc")
828 && (-d "${lk_path}kernel")
829 && (-d "${lk_path}lib")
830 && (-d "${lk_path}scripts")) {
831 return 1;
832 }
833 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700834}
835
Joe Perches0e70e832009-09-21 17:04:20 -0700836sub parse_email {
837 my ($formatted_email) = @_;
838
839 my $name = "";
840 my $address = "";
841
Joe Perches11ecf532009-09-21 17:04:22 -0700842 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700843 $name = $1;
844 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700845 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700846 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700847 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700848 $address = $1;
849 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700850
851 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700852 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700853 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700854
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800855 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700856 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700857 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700858 }
Joe Perches0e70e832009-09-21 17:04:20 -0700859
860 return ($name, $address);
861}
862
863sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800864 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700865
866 my $formatted_email;
867
868 $name =~ s/^\s+|\s+$//g;
869 $name =~ s/^\"|\"$//g;
870 $address =~ s/^\s+|\s+$//g;
871
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800872 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700873 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
874 $name = "\"$name\"";
875 }
876
Joe Perchesa8af2432009-12-14 18:00:49 -0800877 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700878 if ("$name" eq "") {
879 $formatted_email = "$address";
880 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800881 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700882 }
883 } else {
884 $formatted_email = $address;
885 }
886
Joe Perchescb7301c2009-04-07 20:40:12 -0700887 return $formatted_email;
888}
889
Joe Perches272a8972010-01-08 14:42:48 -0800890sub find_first_section {
891 my $index = 0;
892
893 while ($index < @typevalue) {
894 my $tv = $typevalue[$index];
895 if (($tv =~ m/^(\C):\s*(.*)/)) {
896 last;
897 }
898 $index++;
899 }
900
901 return $index;
902}
903
Joe Perchesb7816552009-09-21 17:04:24 -0700904sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700905 my ($index) = @_;
906
907 while ($index > 0) {
908 my $tv = $typevalue[$index];
909 if (!($tv =~ m/^(\C):\s*(.*)/)) {
910 last;
911 }
912 $index--;
913 }
914
915 return $index;
916}
917
918sub find_ending_index {
919 my ($index) = @_;
920
921 while ($index < @typevalue) {
922 my $tv = $typevalue[$index];
923 if (!($tv =~ m/^(\C):\s*(.*)/)) {
924 last;
925 }
926 $index++;
927 }
928
929 return $index;
930}
931
Joe Perches3c7385b2009-12-14 18:00:46 -0800932sub get_maintainer_role {
933 my ($index) = @_;
934
935 my $i;
936 my $start = find_starting_index($index);
937 my $end = find_ending_index($index);
938
Joe Perches0ede2742012-03-23 15:01:56 -0700939 my $role = "unknown";
Joe Perches3c7385b2009-12-14 18:00:46 -0800940 my $subsystem = $typevalue[$start];
941 if (length($subsystem) > 20) {
942 $subsystem = substr($subsystem, 0, 17);
943 $subsystem =~ s/\s*$//;
944 $subsystem = $subsystem . "...";
945 }
946
947 for ($i = $start + 1; $i < $end; $i++) {
948 my $tv = $typevalue[$i];
949 if ($tv =~ m/^(\C):\s*(.*)/) {
950 my $ptype = $1;
951 my $pvalue = $2;
952 if ($ptype eq "S") {
953 $role = $pvalue;
954 }
955 }
956 }
957
958 $role = lc($role);
959 if ($role eq "supported") {
960 $role = "supporter";
961 } elsif ($role eq "maintained") {
962 $role = "maintainer";
963 } elsif ($role eq "odd fixes") {
964 $role = "odd fixer";
965 } elsif ($role eq "orphan") {
966 $role = "orphan minder";
967 } elsif ($role eq "obsolete") {
968 $role = "obsolete minder";
969 } elsif ($role eq "buried alive in reporters") {
970 $role = "chief penguin";
971 }
972
973 return $role . ":" . $subsystem;
974}
975
976sub get_list_role {
977 my ($index) = @_;
978
979 my $i;
980 my $start = find_starting_index($index);
981 my $end = find_ending_index($index);
982
983 my $subsystem = $typevalue[$start];
984 if (length($subsystem) > 20) {
985 $subsystem = substr($subsystem, 0, 17);
986 $subsystem =~ s/\s*$//;
987 $subsystem = $subsystem . "...";
988 }
989
990 if ($subsystem eq "THE REST") {
991 $subsystem = "";
992 }
993
994 return $subsystem;
995}
996
Joe Perchescb7301c2009-04-07 20:40:12 -0700997sub add_categories {
998 my ($index) = @_;
999
Joe Perchesb7816552009-09-21 17:04:24 -07001000 my $i;
1001 my $start = find_starting_index($index);
1002 my $end = find_ending_index($index);
1003
1004 push(@subsystem, $typevalue[$start]);
1005
1006 for ($i = $start + 1; $i < $end; $i++) {
1007 my $tv = $typevalue[$i];
Joe Perches290603c2009-06-16 15:33:58 -07001008 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001009 my $ptype = $1;
1010 my $pvalue = $2;
1011 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001012 my $list_address = $pvalue;
1013 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001014 my $list_role = get_list_role($i);
1015
1016 if ($list_role ne "") {
1017 $list_role = ":" . $list_role;
1018 }
Joe Perches290603c2009-06-16 15:33:58 -07001019 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1020 $list_address = $1;
1021 $list_additional = $2;
1022 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001023 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001024 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001025 if (!$hash_list_to{lc($list_address)}) {
1026 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001027 push(@list_to, [$list_address,
1028 "subscriber list${list_role}"]);
1029 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001030 }
1031 } else {
1032 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001033 if (!$hash_list_to{lc($list_address)}) {
1034 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001035 if ($list_additional =~ m/moderated/) {
1036 push(@list_to, [$list_address,
1037 "moderated list${list_role}"]);
1038 } else {
1039 push(@list_to, [$list_address,
1040 "open list${list_role}"]);
1041 }
Joe Perches683c6f82010-10-26 14:22:55 -07001042 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001043 }
1044 }
1045 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001046 my ($name, $address) = parse_email($pvalue);
1047 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001048 if ($i > 0) {
1049 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -07001050 if ($tv =~ m/^(\C):\s*(.*)/) {
1051 if ($1 eq "P") {
1052 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001053 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001054 }
1055 }
1056 }
1057 }
Joe Perches0e70e832009-09-21 17:04:20 -07001058 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001059 my $role = get_maintainer_role($i);
1060 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001061 }
1062 } elsif ($ptype eq "T") {
1063 push(@scm, $pvalue);
1064 } elsif ($ptype eq "W") {
1065 push(@web, $pvalue);
1066 } elsif ($ptype eq "S") {
1067 push(@status, $pvalue);
1068 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001069 }
1070 }
1071}
1072
Joe Perches11ecf532009-09-21 17:04:22 -07001073sub email_inuse {
1074 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001075
Joe Perches11ecf532009-09-21 17:04:22 -07001076 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001077 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1078 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001079
Joe Perches0e70e832009-09-21 17:04:20 -07001080 return 0;
1081}
1082
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001083sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001084 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001085
Joe Perches0e70e832009-09-21 17:04:20 -07001086 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001087
Joe Perchesb7816552009-09-21 17:04:24 -07001088 if ($address eq "") {
1089 return 0;
1090 }
1091
Joe Perches11ecf532009-09-21 17:04:22 -07001092 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001093 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001094 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001095 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001096 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001097 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001098 }
Joe Perchesb7816552009-09-21 17:04:24 -07001099
1100 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001101}
1102
1103sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001104 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001105
1106 my @address_list = ();
1107
Joe Perches5f2441e2009-06-16 15:34:02 -07001108 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001109 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001110 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001111 my $array_count = shift(@address_list);
1112 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001113 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001114 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001115 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001116 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001117 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1118 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001119 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001120}
1121
Joe Perches3c7385b2009-12-14 18:00:46 -08001122sub add_role {
1123 my ($line, $role) = @_;
1124
1125 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001126 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001127
1128 foreach my $entry (@email_to) {
1129 if ($email_remove_duplicates) {
1130 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001131 if (($name eq $entry_name || $address eq $entry_address)
1132 && ($role eq "" || !($entry->[1] =~ m/$role/))
1133 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001134 if ($entry->[1] eq "") {
1135 $entry->[1] = "$role";
1136 } else {
1137 $entry->[1] = "$entry->[1],$role";
1138 }
1139 }
1140 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001141 if ($email eq $entry->[0]
1142 && ($role eq "" || !($entry->[1] =~ m/$role/))
1143 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001144 if ($entry->[1] eq "") {
1145 $entry->[1] = "$role";
1146 } else {
1147 $entry->[1] = "$entry->[1],$role";
1148 }
1149 }
1150 }
1151 }
1152}
1153
Joe Perchescb7301c2009-04-07 20:40:12 -07001154sub which {
1155 my ($bin) = @_;
1156
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001157 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001158 if (-e "$path/$bin") {
1159 return "$path/$bin";
1160 }
1161 }
1162
1163 return "";
1164}
1165
Joe Perchesbcde44e2010-10-26 14:22:53 -07001166sub which_conf {
1167 my ($conf) = @_;
1168
1169 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1170 if (-e "$path/$conf") {
1171 return "$path/$conf";
1172 }
1173 }
1174
1175 return "";
1176}
1177
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001178sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001179 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001180
Joe Perches47abc722010-10-26 14:22:57 -07001181 my ($name, $address) = parse_email($line);
1182 my $email = format_email($name, $address, 1);
1183 my $real_name = $name;
1184 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001185
Joe Perches47abc722010-10-26 14:22:57 -07001186 if (exists $mailmap->{names}->{$email} ||
1187 exists $mailmap->{addresses}->{$email}) {
1188 if (exists $mailmap->{names}->{$email}) {
1189 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001190 }
Joe Perches47abc722010-10-26 14:22:57 -07001191 if (exists $mailmap->{addresses}->{$email}) {
1192 $real_address = $mailmap->{addresses}->{$email};
1193 }
1194 } else {
1195 if (exists $mailmap->{names}->{$address}) {
1196 $real_name = $mailmap->{names}->{$address};
1197 }
1198 if (exists $mailmap->{addresses}->{$address}) {
1199 $real_address = $mailmap->{addresses}->{$address};
1200 }
1201 }
1202 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001203}
1204
1205sub mailmap {
1206 my (@addresses) = @_;
1207
Joe Perchesb9e23312010-10-26 14:22:58 -07001208 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001209 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001210 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001211 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001212 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1213 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001214}
1215
1216sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001217 my %address_map;
1218 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001219
Joe Perches47abc722010-10-26 14:22:57 -07001220 foreach my $email (@emails) {
1221 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001222 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001223 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001224 $email = format_email($name, $address, 1);
1225 } else {
1226 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001227 }
Joe Perches47abc722010-10-26 14:22:57 -07001228 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001229}
1230
Joe Perches60db31a2009-12-14 18:00:50 -08001231sub git_execute_cmd {
1232 my ($cmd) = @_;
1233 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001234
Joe Perches60db31a2009-12-14 18:00:50 -08001235 my $output = `$cmd`;
1236 $output =~ s/^\s*//gm;
1237 @lines = split("\n", $output);
1238
1239 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001240}
1241
Joe Perches60db31a2009-12-14 18:00:50 -08001242sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001243 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001244 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001245
Joe Perches60db31a2009-12-14 18:00:50 -08001246 my $output = `$cmd`;
1247 @lines = split("\n", $output);
1248
1249 return @lines;
1250}
1251
Joe Perches683c6f82010-10-26 14:22:55 -07001252sub extract_formatted_signatures {
1253 my (@signature_lines) = @_;
1254
1255 my @type = @signature_lines;
1256
1257 s/\s*(.*):.*/$1/ for (@type);
1258
1259 # cut -f2- -d":"
1260 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1261
1262## Reformat email addresses (with names) to avoid badly written signatures
1263
1264 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001265 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001266 }
1267
1268 return (\@type, \@signature_lines);
1269}
1270
Joe Perches60db31a2009-12-14 18:00:50 -08001271sub vcs_find_signers {
1272 my ($cmd) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001273 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001274 my @lines = ();
1275 my @signatures = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001276
Joe Perches60db31a2009-12-14 18:00:50 -08001277 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001278
Joe Perches60db31a2009-12-14 18:00:50 -08001279 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchescb7301c2009-04-07 20:40:12 -07001280
Joe Perches60db31a2009-12-14 18:00:50 -08001281 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001282
Joe Perches683c6f82010-10-26 14:22:55 -07001283 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
1284
1285 return (0, @signatures) if !@signatures;
1286
1287 save_commits_by_author(@lines) if ($interactive);
1288 save_commits_by_signer(@lines) if ($interactive);
1289
Joe Perches0e70e832009-09-21 17:04:20 -07001290 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001291 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001292 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001293
Joe Perches683c6f82010-10-26 14:22:55 -07001294 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001295
Joe Perches683c6f82010-10-26 14:22:55 -07001296 return ($commits, @$signers_ref);
Joe Perchesa8af2432009-12-14 18:00:49 -08001297}
1298
Joe Perches63ab52d2010-10-26 14:22:51 -07001299sub vcs_find_author {
1300 my ($cmd) = @_;
1301 my @lines = ();
1302
1303 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1304
1305 if (!$email_git_penguin_chiefs) {
1306 @lines = grep(!/${penguin_chiefs}/i, @lines);
1307 }
1308
1309 return @lines if !@lines;
1310
Joe Perches683c6f82010-10-26 14:22:55 -07001311 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001312 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001313 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1314 my $author = $1;
1315 my ($name, $address) = parse_email($author);
1316 $author = format_email($name, $address, 1);
1317 push(@authors, $author);
1318 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001319 }
1320
Joe Perches683c6f82010-10-26 14:22:55 -07001321 save_commits_by_author(@lines) if ($interactive);
1322 save_commits_by_signer(@lines) if ($interactive);
1323
1324 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001325}
1326
Joe Perches60db31a2009-12-14 18:00:50 -08001327sub vcs_save_commits {
1328 my ($cmd) = @_;
1329 my @lines = ();
1330 my @commits = ();
1331
1332 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1333
1334 foreach my $line (@lines) {
1335 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1336 push(@commits, $1);
1337 }
1338 }
1339
1340 return @commits;
1341}
1342
1343sub vcs_blame {
1344 my ($file) = @_;
1345 my $cmd;
1346 my @commits = ();
1347
1348 return @commits if (!(-f $file));
1349
1350 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1351 my @all_commits = ();
1352
1353 $cmd = $VCS_cmds{"blame_file_cmd"};
1354 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1355 @all_commits = vcs_save_commits($cmd);
1356
1357 foreach my $file_range_diff (@range) {
1358 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1359 my $diff_file = $1;
1360 my $diff_start = $2;
1361 my $diff_length = $3;
1362 next if ("$file" ne "$diff_file");
1363 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1364 push(@commits, $all_commits[$i]);
1365 }
1366 }
1367 } elsif (@range) {
1368 foreach my $file_range_diff (@range) {
1369 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1370 my $diff_file = $1;
1371 my $diff_start = $2;
1372 my $diff_length = $3;
1373 next if ("$file" ne "$diff_file");
1374 $cmd = $VCS_cmds{"blame_range_cmd"};
1375 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1376 push(@commits, vcs_save_commits($cmd));
1377 }
1378 } else {
1379 $cmd = $VCS_cmds{"blame_file_cmd"};
1380 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1381 @commits = vcs_save_commits($cmd);
1382 }
1383
Joe Perches63ab52d2010-10-26 14:22:51 -07001384 foreach my $commit (@commits) {
1385 $commit =~ s/^\^//g;
1386 }
1387
Joe Perches60db31a2009-12-14 18:00:50 -08001388 return @commits;
1389}
1390
1391my $printed_novcs = 0;
1392sub vcs_exists {
1393 %VCS_cmds = %VCS_cmds_git;
1394 return 1 if eval $VCS_cmds{"available"};
1395 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001396 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001397 %VCS_cmds = ();
1398 if (!$printed_novcs) {
1399 warn("$P: No supported VCS found. Add --nogit to options?\n");
1400 warn("Using a git repository produces better results.\n");
1401 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001402 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001403 $printed_novcs = 1;
1404 }
1405 return 0;
1406}
1407
Joe Perches683c6f82010-10-26 14:22:55 -07001408sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001409 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001410 return $vcs_used == 1;
1411}
1412
1413sub vcs_is_hg {
1414 return $vcs_used == 2;
1415}
1416
Joe Perches6ef1c522010-10-26 14:22:56 -07001417sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001418 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001419 my @list = @$list_ref;
1420
Joe Perches683c6f82010-10-26 14:22:55 -07001421 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001422
1423 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001424 my %authored;
1425 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001426 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001427 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001428 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001429 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1430 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001431 $authored{$count} = 0;
1432 $signed{$count} = 0;
1433 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001434 }
1435
1436 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001437 my $done = 0;
1438 my $print_options = 0;
1439 my $redraw = 1;
1440 while (!$done) {
1441 $count = 0;
1442 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001443 printf STDERR "\n%1s %2s %-65s",
1444 "*", "#", "email/list and role:stats";
1445 if ($email_git ||
1446 ($email_git_fallback && !$maintained) ||
1447 $email_git_blame) {
1448 print STDERR "auth sign";
1449 }
1450 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001451 foreach my $entry (@list) {
1452 my $email = $entry->[0];
1453 my $role = $entry->[1];
1454 my $sel = "";
1455 $sel = "*" if ($selected{$count});
1456 my $commit_author = $commit_author_hash{$email};
1457 my $commit_signer = $commit_signer_hash{$email};
1458 my $authored = 0;
1459 my $signed = 0;
1460 $authored++ for (@{$commit_author});
1461 $signed++ for (@{$commit_signer});
1462 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1463 printf STDERR "%4d %4d", $authored, $signed
1464 if ($authored > 0 || $signed > 0);
1465 printf STDERR "\n %s\n", $role;
1466 if ($authored{$count}) {
1467 my $commit_author = $commit_author_hash{$email};
1468 foreach my $ref (@{$commit_author}) {
1469 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001470 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001471 }
Joe Perches683c6f82010-10-26 14:22:55 -07001472 if ($signed{$count}) {
1473 my $commit_signer = $commit_signer_hash{$email};
1474 foreach my $ref (@{$commit_signer}) {
1475 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1476 }
1477 }
1478
1479 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001480 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001481 }
Joe Perches683c6f82010-10-26 14:22:55 -07001482 my $date_ref = \$email_git_since;
1483 $date_ref = \$email_hg_since if (vcs_is_hg());
1484 if ($print_options) {
1485 $print_options = 0;
1486 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001487 print STDERR <<EOT
1488
1489Version Control options:
1490g use git history [$email_git]
1491gf use git-fallback [$email_git_fallback]
1492b use git blame [$email_git_blame]
1493bs use blame signatures [$email_git_blame_signatures]
1494c# minimum commits [$email_git_min_signatures]
1495%# min percent [$email_git_min_percent]
1496d# history to use [$$date_ref]
1497x# max maintainers [$email_git_max_maintainers]
1498t all signature types [$email_git_all_signature_types]
1499m use .mailmap [$email_use_mailmap]
1500EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001501 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001502 print STDERR <<EOT
1503
1504Additional options:
15050 toggle all
1506tm toggle maintainers
1507tg toggle git entries
1508tl toggle open list entries
1509ts toggle subscriber list entries
1510f emails in file [$file_emails]
1511k keywords in file [$keywords]
1512r remove duplicates [$email_remove_duplicates]
1513p# pattern match depth [$pattern_depth]
1514EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001515 }
1516 print STDERR
1517"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1518
1519 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001520 chomp($input);
1521
Joe Perches683c6f82010-10-26 14:22:55 -07001522 $redraw = 1;
1523 my $rerun = 0;
1524 my @wish = split(/[, ]+/, $input);
1525 foreach my $nr (@wish) {
1526 $nr = lc($nr);
1527 my $sel = substr($nr, 0, 1);
1528 my $str = substr($nr, 1);
1529 my $val = 0;
1530 $val = $1 if $str =~ /^(\d+)$/;
1531
1532 if ($sel eq "y") {
1533 $interactive = 0;
1534 $done = 1;
1535 $output_rolestats = 0;
1536 $output_roles = 0;
1537 last;
1538 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1539 $selected{$nr - 1} = !$selected{$nr - 1};
1540 } elsif ($sel eq "*" || $sel eq '^') {
1541 my $toggle = 0;
1542 $toggle = 1 if ($sel eq '*');
1543 for (my $i = 0; $i < $count; $i++) {
1544 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001545 }
Joe Perches683c6f82010-10-26 14:22:55 -07001546 } elsif ($sel eq "0") {
1547 for (my $i = 0; $i < $count; $i++) {
1548 $selected{$i} = !$selected{$i};
1549 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001550 } elsif ($sel eq "t") {
1551 if (lc($str) eq "m") {
1552 for (my $i = 0; $i < $count; $i++) {
1553 $selected{$i} = !$selected{$i}
1554 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1555 }
1556 } elsif (lc($str) eq "g") {
1557 for (my $i = 0; $i < $count; $i++) {
1558 $selected{$i} = !$selected{$i}
1559 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1560 }
1561 } elsif (lc($str) eq "l") {
1562 for (my $i = 0; $i < $count; $i++) {
1563 $selected{$i} = !$selected{$i}
1564 if ($list[$i]->[1] =~ /^(open list)/i);
1565 }
1566 } elsif (lc($str) eq "s") {
1567 for (my $i = 0; $i < $count; $i++) {
1568 $selected{$i} = !$selected{$i}
1569 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1570 }
1571 }
Joe Perches683c6f82010-10-26 14:22:55 -07001572 } elsif ($sel eq "a") {
1573 if ($val > 0 && $val <= $count) {
1574 $authored{$val - 1} = !$authored{$val - 1};
1575 } elsif ($str eq '*' || $str eq '^') {
1576 my $toggle = 0;
1577 $toggle = 1 if ($str eq '*');
1578 for (my $i = 0; $i < $count; $i++) {
1579 $authored{$i} = $toggle;
1580 }
1581 }
1582 } elsif ($sel eq "s") {
1583 if ($val > 0 && $val <= $count) {
1584 $signed{$val - 1} = !$signed{$val - 1};
1585 } elsif ($str eq '*' || $str eq '^') {
1586 my $toggle = 0;
1587 $toggle = 1 if ($str eq '*');
1588 for (my $i = 0; $i < $count; $i++) {
1589 $signed{$i} = $toggle;
1590 }
1591 }
1592 } elsif ($sel eq "o") {
1593 $print_options = 1;
1594 $redraw = 1;
1595 } elsif ($sel eq "g") {
1596 if ($str eq "f") {
1597 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001598 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001599 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001600 }
Joe Perches683c6f82010-10-26 14:22:55 -07001601 $rerun = 1;
1602 } elsif ($sel eq "b") {
1603 if ($str eq "s") {
1604 bool_invert(\$email_git_blame_signatures);
1605 } else {
1606 bool_invert(\$email_git_blame);
1607 }
1608 $rerun = 1;
1609 } elsif ($sel eq "c") {
1610 if ($val > 0) {
1611 $email_git_min_signatures = $val;
1612 $rerun = 1;
1613 }
1614 } elsif ($sel eq "x") {
1615 if ($val > 0) {
1616 $email_git_max_maintainers = $val;
1617 $rerun = 1;
1618 }
1619 } elsif ($sel eq "%") {
1620 if ($str ne "" && $val >= 0) {
1621 $email_git_min_percent = $val;
1622 $rerun = 1;
1623 }
1624 } elsif ($sel eq "d") {
1625 if (vcs_is_git()) {
1626 $email_git_since = $str;
1627 } elsif (vcs_is_hg()) {
1628 $email_hg_since = $str;
1629 }
1630 $rerun = 1;
1631 } elsif ($sel eq "t") {
1632 bool_invert(\$email_git_all_signature_types);
1633 $rerun = 1;
1634 } elsif ($sel eq "f") {
1635 bool_invert(\$file_emails);
1636 $rerun = 1;
1637 } elsif ($sel eq "r") {
1638 bool_invert(\$email_remove_duplicates);
1639 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001640 } elsif ($sel eq "m") {
1641 bool_invert(\$email_use_mailmap);
1642 read_mailmap();
1643 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001644 } elsif ($sel eq "k") {
1645 bool_invert(\$keywords);
1646 $rerun = 1;
1647 } elsif ($sel eq "p") {
1648 if ($str ne "" && $val >= 0) {
1649 $pattern_depth = $val;
1650 $rerun = 1;
1651 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001652 } elsif ($sel eq "h" || $sel eq "?") {
1653 print STDERR <<EOT
1654
1655Interactive mode allows you to select the various maintainers, submitters,
1656commit signers and mailing lists that could be CC'd on a patch.
1657
1658Any *'d entry is selected.
1659
Joe Perches47abc722010-10-26 14:22:57 -07001660If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001661history of files in the patch. Also, each line of the current file can
1662be matched to its commit author and that commits signers with blame.
1663
1664Various knobs exist to control the length of time for active commit
1665tracking, the maximum number of commit authors and signers to add,
1666and such.
1667
1668Enter selections at the prompt until you are satisfied that the selected
1669maintainers are appropriate. You may enter multiple selections separated
1670by either commas or spaces.
1671
1672EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001673 } else {
1674 print STDERR "invalid option: '$nr'\n";
1675 $redraw = 0;
1676 }
1677 }
1678 if ($rerun) {
1679 print STDERR "git-blame can be very slow, please have patience..."
1680 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001681 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001682 }
1683 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001684
1685 #drop not selected entries
1686 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001687 my @new_emailto = ();
1688 foreach my $entry (@list) {
1689 if ($selected{$count}) {
1690 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001691 }
1692 $count++;
1693 }
Joe Perches683c6f82010-10-26 14:22:55 -07001694 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001695}
1696
Joe Perches683c6f82010-10-26 14:22:55 -07001697sub bool_invert {
1698 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001699
Joe Perches683c6f82010-10-26 14:22:55 -07001700 if ($$bool_ref) {
1701 $$bool_ref = 0;
1702 } else {
1703 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001704 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001705}
1706
Joe Perchesb9e23312010-10-26 14:22:58 -07001707sub deduplicate_email {
1708 my ($email) = @_;
1709
1710 my $matched = 0;
1711 my ($name, $address) = parse_email($email);
1712 $email = format_email($name, $address, 1);
1713 $email = mailmap_email($email);
1714
1715 return $email if (!$email_remove_duplicates);
1716
1717 ($name, $address) = parse_email($email);
1718
Joe Perchesfae99202010-10-26 14:22:58 -07001719 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001720 $name = $deduplicate_name_hash{lc($name)}->[0];
1721 $address = $deduplicate_name_hash{lc($name)}->[1];
1722 $matched = 1;
1723 } elsif ($deduplicate_address_hash{lc($address)}) {
1724 $name = $deduplicate_address_hash{lc($address)}->[0];
1725 $address = $deduplicate_address_hash{lc($address)}->[1];
1726 $matched = 1;
1727 }
1728 if (!$matched) {
1729 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1730 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1731 }
1732 $email = format_email($name, $address, 1);
1733 $email = mailmap_email($email);
1734 return $email;
1735}
1736
Joe Perches683c6f82010-10-26 14:22:55 -07001737sub save_commits_by_author {
1738 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001739
Joe Perches683c6f82010-10-26 14:22:55 -07001740 my @authors = ();
1741 my @commits = ();
1742 my @subjects = ();
1743
1744 foreach my $line (@lines) {
1745 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1746 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001747 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001748 push(@authors, $author);
1749 }
1750 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1751 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1752 }
1753
1754 for (my $i = 0; $i < @authors; $i++) {
1755 my $exists = 0;
1756 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1757 if (@{$ref}[0] eq $commits[$i] &&
1758 @{$ref}[1] eq $subjects[$i]) {
1759 $exists = 1;
1760 last;
1761 }
1762 }
1763 if (!$exists) {
1764 push(@{$commit_author_hash{$authors[$i]}},
1765 [ ($commits[$i], $subjects[$i]) ]);
1766 }
1767 }
1768}
1769
1770sub save_commits_by_signer {
1771 my (@lines) = @_;
1772
1773 my $commit = "";
1774 my $subject = "";
1775
1776 foreach my $line (@lines) {
1777 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1778 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1779 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1780 my @signatures = ($line);
1781 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1782 my @types = @$types_ref;
1783 my @signers = @$signers_ref;
1784
1785 my $type = $types[0];
1786 my $signer = $signers[0];
1787
Joe Perchesb9e23312010-10-26 14:22:58 -07001788 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001789
Joe Perches683c6f82010-10-26 14:22:55 -07001790 my $exists = 0;
1791 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1792 if (@{$ref}[0] eq $commit &&
1793 @{$ref}[1] eq $subject &&
1794 @{$ref}[2] eq $type) {
1795 $exists = 1;
1796 last;
1797 }
1798 }
1799 if (!$exists) {
1800 push(@{$commit_signer_hash{$signer}},
1801 [ ($commit, $subject, $type) ]);
1802 }
1803 }
1804 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001805}
1806
Joe Perches60db31a2009-12-14 18:00:50 -08001807sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001808 my ($role, $divisor, @lines) = @_;
1809
1810 my %hash;
1811 my $count = 0;
1812
Joe Perchesa8af2432009-12-14 18:00:49 -08001813 return if (@lines <= 0);
1814
1815 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001816 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001817 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001818 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001819
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001820 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001821
Joe Perches63ab52d2010-10-26 14:22:51 -07001822 return if (@lines <= 0);
1823
Joe Perches0e70e832009-09-21 17:04:20 -07001824 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001825
Joe Perches11ecf532009-09-21 17:04:22 -07001826 # uniq -c
1827 $hash{$_}++ for @lines;
1828
1829 # sort -rn
1830 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1831 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001832 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001833
Joe Perchesa8af2432009-12-14 18:00:49 -08001834 $percent = 100 if ($percent > 100);
Joe Perches11ecf532009-09-21 17:04:22 -07001835 $count++;
1836 last if ($sign_offs < $email_git_min_signatures ||
1837 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001838 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001839 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001840 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001841 my $fmt_percent = sprintf("%.0f", $percent);
1842 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1843 } else {
1844 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001845 }
Joe Perchesf5492662009-09-21 17:04:13 -07001846 }
1847}
1848
Joe Perches60db31a2009-12-14 18:00:50 -08001849sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001850 my ($file) = @_;
1851
1852 my @signers = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001853 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001854
Joe Perches683c6f82010-10-26 14:22:55 -07001855 $vcs_used = vcs_exists();
1856 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001857
Joe Perches60db31a2009-12-14 18:00:50 -08001858 my $cmd = $VCS_cmds{"find_signers_cmd"};
1859 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1860
1861 ($commits, @signers) = vcs_find_signers($cmd);
Joe Perchesb9e23312010-10-26 14:22:58 -07001862
1863 foreach my $signer (@signers) {
1864 $signer = deduplicate_email($signer);
1865 }
1866
Joe Perches60db31a2009-12-14 18:00:50 -08001867 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001868}
1869
Joe Perches60db31a2009-12-14 18:00:50 -08001870sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001871 my ($file) = @_;
1872
Joe Perches60db31a2009-12-14 18:00:50 -08001873 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001874 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001875 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001876 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001877 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001878
Joe Perches683c6f82010-10-26 14:22:55 -07001879 $vcs_used = vcs_exists();
1880 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07001881
Joe Perches63ab52d2010-10-26 14:22:51 -07001882 @all_commits = vcs_blame($file);
1883 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08001884 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001885 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001886
Joe Perches683c6f82010-10-26 14:22:55 -07001887 if ($email_git_blame_signatures) {
1888 if (vcs_is_hg()) {
1889 my $commit_count;
1890 my @commit_signers = ();
1891 my $commit = join(" -r ", @commits);
1892 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07001893
Joe Perches683c6f82010-10-26 14:22:55 -07001894 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1895 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08001896
Joe Perches683c6f82010-10-26 14:22:55 -07001897 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
Joe Perches63ab52d2010-10-26 14:22:51 -07001898
Joe Perches683c6f82010-10-26 14:22:55 -07001899 push(@signers, @commit_signers);
1900 } else {
1901 foreach my $commit (@commits) {
1902 my $commit_count;
1903 my @commit_signers = ();
1904 my $cmd;
1905
1906 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1907 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1908
1909 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
1910
1911 push(@signers, @commit_signers);
1912 }
1913 }
Joe Perchesf5492662009-09-21 17:04:13 -07001914 }
1915
Joe Perchesa8af2432009-12-14 18:00:49 -08001916 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07001917 if ($output_rolestats) {
1918 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07001919 if (vcs_is_hg()) {{ # Double brace for last exit
1920 my $commit_count;
1921 my @commit_signers = ();
1922 @commits = uniq(@commits);
1923 @commits = sort(@commits);
1924 my $commit = join(" -r ", @commits);
1925 my $cmd;
1926
1927 $cmd = $VCS_cmds{"find_commit_author_cmd"};
1928 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1929
1930 my @lines = ();
1931
1932 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1933
1934 if (!$email_git_penguin_chiefs) {
1935 @lines = grep(!/${penguin_chiefs}/i, @lines);
1936 }
1937
1938 last if !@lines;
1939
1940 my @authors = ();
1941 foreach my $line (@lines) {
1942 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1943 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001944 $author = deduplicate_email($author);
1945 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07001946 }
1947 }
1948
1949 save_commits_by_author(@lines) if ($interactive);
1950 save_commits_by_signer(@lines) if ($interactive);
1951
1952 push(@signers, @authors);
1953 }}
1954 else {
1955 foreach my $commit (@commits) {
1956 my $i;
1957 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
1958 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1959 my @author = vcs_find_author($cmd);
1960 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07001961
1962 my $formatted_author = deduplicate_email($author[0]);
1963
Joe Perches683c6f82010-10-26 14:22:55 -07001964 my $count = grep(/$commit/, @all_commits);
1965 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001966 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07001967 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001968 }
1969 }
1970 if (@blame_signers) {
1971 vcs_assign("authored lines", $total_lines, @blame_signers);
1972 }
1973 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001974 foreach my $signer (@signers) {
1975 $signer = deduplicate_email($signer);
1976 }
Joe Perches60db31a2009-12-14 18:00:50 -08001977 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001978 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07001979 foreach my $signer (@signers) {
1980 $signer = deduplicate_email($signer);
1981 }
Joe Perches60db31a2009-12-14 18:00:50 -08001982 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07001983 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001984}
1985
1986sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001987 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001988
1989 my %saw;
1990 @parms = grep(!$saw{$_}++, @parms);
1991 return @parms;
1992}
1993
1994sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001995 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001996
1997 my %saw;
1998 @parms = sort @parms;
1999 @parms = grep(!$saw{$_}++, @parms);
2000 return @parms;
2001}
2002
Joe Perches03372db2010-03-05 13:43:00 -08002003sub clean_file_emails {
2004 my (@file_emails) = @_;
2005 my @fmt_emails = ();
2006
2007 foreach my $email (@file_emails) {
2008 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2009 my ($name, $address) = parse_email($email);
2010 if ($name eq '"[,\.]"') {
2011 $name = "";
2012 }
2013
2014 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2015 if (@nw > 2) {
2016 my $first = $nw[@nw - 3];
2017 my $middle = $nw[@nw - 2];
2018 my $last = $nw[@nw - 1];
2019
2020 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2021 (length($first) == 2 && substr($first, -1) eq ".")) ||
2022 (length($middle) == 1 ||
2023 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2024 $name = "$first $middle $last";
2025 } else {
2026 $name = "$middle $last";
2027 }
2028 }
2029
2030 if (substr($name, -1) =~ /[,\.]/) {
2031 $name = substr($name, 0, length($name) - 1);
2032 } elsif (substr($name, -2) =~ /[,\.]"/) {
2033 $name = substr($name, 0, length($name) - 2) . '"';
2034 }
2035
2036 if (substr($name, 0, 1) =~ /[,\.]/) {
2037 $name = substr($name, 1, length($name) - 1);
2038 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2039 $name = '"' . substr($name, 2, length($name) - 2);
2040 }
2041
2042 my $fmt_email = format_email($name, $address, $email_usename);
2043 push(@fmt_emails, $fmt_email);
2044 }
2045 return @fmt_emails;
2046}
2047
Joe Perches3c7385b2009-12-14 18:00:46 -08002048sub merge_email {
2049 my @lines;
2050 my %saw;
2051
2052 for (@_) {
2053 my ($address, $role) = @$_;
2054 if (!$saw{$address}) {
2055 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002056 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002057 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002058 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002059 }
2060 $saw{$address} = 1;
2061 }
2062 }
2063
2064 return @lines;
2065}
2066
Joe Perchescb7301c2009-04-07 20:40:12 -07002067sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002068 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002069
2070 if ($output_multiline) {
2071 foreach my $line (@parms) {
2072 print("${line}\n");
2073 }
2074 } else {
2075 print(join($output_separator, @parms));
2076 print("\n");
2077 }
2078}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002079
2080my $rfc822re;
2081
2082sub make_rfc822re {
2083# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2084# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2085# This regexp will only work on addresses which have had comments stripped
2086# and replaced with rfc822_lwsp.
2087
2088 my $specials = '()<>@,;:\\\\".\\[\\]';
2089 my $controls = '\\000-\\037\\177';
2090
2091 my $dtext = "[^\\[\\]\\r\\\\]";
2092 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2093
2094 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2095
2096# Use zero-width assertion to spot the limit of an atom. A simple
2097# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2098 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2099 my $word = "(?:$atom|$quoted_string)";
2100 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2101
2102 my $sub_domain = "(?:$atom|$domain_literal)";
2103 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2104
2105 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2106
2107 my $phrase = "$word*";
2108 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2109 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2110 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2111
2112 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2113 my $address = "(?:$mailbox|$group)";
2114
2115 return "$rfc822_lwsp*$address";
2116}
2117
2118sub rfc822_strip_comments {
2119 my $s = shift;
2120# Recursively remove comments, and replace with a single space. The simpler
2121# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2122# chars in atoms, for example.
2123
2124 while ($s =~ s/^((?:[^"\\]|\\.)*
2125 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2126 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2127 return $s;
2128}
2129
2130# valid: returns true if the parameter is an RFC822 valid address
2131#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002132sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002133 my $s = rfc822_strip_comments(shift);
2134
2135 if (!$rfc822re) {
2136 $rfc822re = make_rfc822re();
2137 }
2138
2139 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2140}
2141
2142# validlist: In scalar context, returns true if the parameter is an RFC822
2143# valid list of addresses.
2144#
2145# In list context, returns an empty list on failure (an invalid
2146# address was found); otherwise a list whose first element is the
2147# number of addresses found and whose remaining elements are the
2148# addresses. This is needed to disambiguate failure (invalid)
2149# from success with no addresses found, because an empty string is
2150# a valid list.
2151
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002152sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002153 my $s = rfc822_strip_comments(shift);
2154
2155 if (!$rfc822re) {
2156 $rfc822re = make_rfc822re();
2157 }
2158 # * null list items are valid according to the RFC
2159 # * the '1' business is to aid in distinguishing failure from no results
2160
2161 my @r;
2162 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2163 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002164 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002165 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002166 }
2167 return wantarray ? (scalar(@r), @r) : 1;
2168 }
Joe Perches60db31a2009-12-14 18:00:50 -08002169 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002170}