blob: 139e0fff8e3131159d8d16046d2cf9bc72503aaa [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 Perches5f2441e2009-06-16 15:34:02 -070086# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070087my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070088my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070089
Joe Perches60db31a2009-12-14 18:00:50 -080090# VCS command support: class-like functions and strings
91
92my %VCS_cmds;
93
94my %VCS_cmds_git = (
95 "execute_cmd" => \&git_execute_cmd,
96 "available" => '(which("git") ne "") && (-d ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -070097 "find_signers_cmd" =>
98 "git log --no-color --since=\$email_git_since " .
99 '--format="GitCommit: %H%n' .
100 'GitAuthor: %an <%ae>%n' .
101 'GitDate: %aD%n' .
102 'GitSubject: %s%n' .
103 '%b%n"' .
104 " -- \$file",
105 "find_commit_signers_cmd" =>
106 "git log --no-color " .
107 '--format="GitCommit: %H%n' .
108 'GitAuthor: %an <%ae>%n' .
109 'GitDate: %aD%n' .
110 'GitSubject: %s%n' .
111 '%b%n"' .
112 " -1 \$commit",
113 "find_commit_author_cmd" =>
114 "git log --no-color " .
115 '--format="GitCommit: %H%n' .
116 'GitAuthor: %an <%ae>%n' .
117 'GitDate: %aD%n' .
118 'GitSubject: %s%n"' .
119 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800120 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
121 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700122 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700123 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700124 "author_pattern" => "^GitAuthor: (.*)",
125 "subject_pattern" => "^GitSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800126);
127
128my %VCS_cmds_hg = (
129 "execute_cmd" => \&hg_execute_cmd,
130 "available" => '(which("hg") ne "") && (-d ".hg")',
131 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700132 "hg log --date=\$email_hg_since " .
133 "--template='HgCommit: {node}\\n" .
134 "HgAuthor: {author}\\n" .
135 "HgSubject: {desc}\\n'" .
136 " -- \$file",
137 "find_commit_signers_cmd" =>
138 "hg log " .
139 "--template='HgSubject: {desc}\\n'" .
140 " -r \$commit",
141 "find_commit_author_cmd" =>
142 "hg log " .
143 "--template='HgCommit: {node}\\n" .
144 "HgAuthor: {author}\\n" .
145 "HgSubject: {desc|firstline}\\n'" .
146 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800147 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700148 "blame_file_cmd" => "hg blame -n \$file",
149 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
150 "blame_commit_pattern" => "^([ 0-9a-f]+):",
151 "author_pattern" => "^HgAuthor: (.*)",
152 "subject_pattern" => "^HgSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800153);
154
Joe Perchesbcde44e2010-10-26 14:22:53 -0700155my $conf = which_conf(".get_maintainer.conf");
156if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700157 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700158 open(my $conffile, '<', "$conf")
159 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
160
Joe Perches368669d2010-05-24 14:33:19 -0700161 while (<$conffile>) {
162 my $line = $_;
163
164 $line =~ s/\s*\n?$//g;
165 $line =~ s/^\s*//g;
166 $line =~ s/\s+/ /g;
167
168 next if ($line =~ m/^\s*#/);
169 next if ($line =~ m/^\s*$/);
170
171 my @words = split(" ", $line);
172 foreach my $word (@words) {
173 last if ($word =~ m/^#/);
174 push (@conf_args, $word);
175 }
176 }
177 close($conffile);
178 unshift(@ARGV, @conf_args) if @conf_args;
179}
180
Joe Perchescb7301c2009-04-07 20:40:12 -0700181if (!GetOptions(
182 'email!' => \$email,
183 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700184 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800185 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700186 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700187 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700188 'git-chief-penguins!' => \$email_git_penguin_chiefs,
189 'git-min-signatures=i' => \$email_git_min_signatures,
190 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700191 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700192 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800193 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700194 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700195 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700196 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700197 'm!' => \$email_maintainer,
198 'n!' => \$email_usename,
199 'l!' => \$email_list,
200 's!' => \$email_subscriber_list,
201 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800202 'roles!' => \$output_roles,
203 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700204 'separator=s' => \$output_separator,
205 'subsystem!' => \$subsystem,
206 'status!' => \$status,
207 'scm!' => \$scm,
208 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700209 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700210 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800211 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800212 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700213 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700214 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800215 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700216 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800217 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700218}
219
220if ($help != 0) {
221 usage();
222 exit 0;
223}
224
225if ($version != 0) {
226 print("${P} ${V}\n");
227 exit 0;
228}
229
Joe Perches64f77f32010-03-05 13:43:04 -0800230if (-t STDIN && !@ARGV) {
231 # We're talking to a terminal, but have no command line arguments.
232 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700233}
234
Joe Perches683c6f82010-10-26 14:22:55 -0700235$output_multiline = 0 if ($output_separator ne ", ");
236$output_rolestats = 1 if ($interactive);
237$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800238
Joe Perches4b76c9d2010-03-05 13:43:03 -0800239if ($sections) {
240 $email = 0;
241 $email_list = 0;
242 $scm = 0;
243 $status = 0;
244 $subsystem = 0;
245 $web = 0;
246 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700247 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800248} else {
249 my $selections = $email + $scm + $status + $subsystem + $web;
250 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800251 die "$P: Missing required option: email, scm, status, subsystem or web\n";
252 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700253}
254
Joe Perchesf5492662009-09-21 17:04:13 -0700255if ($email &&
256 ($email_maintainer + $email_list + $email_subscriber_list +
257 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700258 die "$P: Please select at least 1 email option\n";
259}
260
261if (!top_of_kernel_tree($lk_path)) {
262 die "$P: The current directory does not appear to be "
263 . "a linux kernel source tree.\n";
264}
265
266## Read MAINTAINERS for type/value pairs
267
268my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700269my %keyword_hash;
270
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800271open (my $maint, '<', "${lk_path}MAINTAINERS")
272 or die "$P: Can't open MAINTAINERS: $!\n";
273while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700274 my $line = $_;
275
276 if ($line =~ m/^(\C):\s*(.*)/) {
277 my $type = $1;
278 my $value = $2;
279
280 ##Filename pattern matching
281 if ($type eq "F" || $type eq "X") {
282 $value =~ s@\.@\\\.@g; ##Convert . to \.
283 $value =~ s/\*/\.\*/g; ##Convert * to .*
284 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700285 ##if pattern is a directory and it lacks a trailing slash, add one
286 if ((-d $value)) {
287 $value =~ s@([^/])$@$1/@;
288 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700289 } elsif ($type eq "K") {
290 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700291 }
292 push(@typevalue, "$type:$value");
293 } elsif (!/^(\s)*$/) {
294 $line =~ s/\n$//g;
295 push(@typevalue, $line);
296 }
297}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800298close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700299
Joe Perches8cbb3a72009-09-21 17:04:21 -0700300
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700301#
302# Read mail address map
303#
304
Joe Perchesb9e23312010-10-26 14:22:58 -0700305my $mailmap;
306
307read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700308
309sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700310 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700311 names => {},
312 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700313 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700314
Joe Perchesb9e23312010-10-26 14:22:58 -0700315 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700316
317 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800318 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700319
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700320 while (<$mailmap_file>) {
321 s/#.*$//; #strip comments
322 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700323
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700324 next if (/^\s*$/); #skip empty lines
325 #entries have one of the following formats:
326 # name1 <mail1>
327 # <mail1> <mail2>
328 # name1 <mail1> <mail2>
329 # name1 <mail1> name2 <mail2>
330 # (see man git-shortlog)
331 if (/^(.+)<(.+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700332 my $real_name = $1;
333 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700334
Joe Perches47abc722010-10-26 14:22:57 -0700335 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700336 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700337 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700338
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700339 } elsif (/^<([^\s]+)>\s*<([^\s]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700340 my $real_address = $1;
341 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700342
Joe Perches47abc722010-10-26 14:22:57 -0700343 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700344
345 } elsif (/^(.+)<([^\s]+)>\s*<([^\s]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700346 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700347 my $real_address = $2;
348 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700349
Joe Perches47abc722010-10-26 14:22:57 -0700350 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700351 ($real_name, $real_address) =
352 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700353 $mailmap->{names}->{$wrong_address} = $real_name;
354 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700355
356 } elsif (/^(.+)<([^\s]+)>\s*([^\s].*)<([^\s]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700357 my $real_name = $1;
358 my $real_address = $2;
359 my $wrong_name = $3;
360 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700361
Joe Perches47abc722010-10-26 14:22:57 -0700362 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700363 ($real_name, $real_address) =
364 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700365
Joe Perchesb9e23312010-10-26 14:22:58 -0700366 $wrong_name =~ s/\s+$//;
367 ($wrong_name, $wrong_address) =
368 parse_email("$wrong_name <$wrong_address>");
369
370 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
371 $mailmap->{names}->{$wrong_email} = $real_name;
372 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700373 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700374 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700375 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700376}
377
Joe Perches4a7fdb52009-04-10 12:28:57 -0700378## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700379
380my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700381my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700382my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800383my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700384
Joe Perches64f77f32010-03-05 13:43:04 -0800385if (!@ARGV) {
386 push(@ARGV, "&STDIN");
387}
388
Joe Perches4a7fdb52009-04-10 12:28:57 -0700389foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800390 if ($file ne "&STDIN") {
391 ##if $file is a directory and it lacks a trailing slash, add one
392 if ((-d $file)) {
393 $file =~ s@([^/])$@$1/@;
394 } elsif (!(-f $file)) {
395 die "$P: file '${file}' not found\n";
396 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700397 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700398 if ($from_filename) {
399 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700400 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800401 open(my $f, '<', $file)
402 or die "$P: Can't open $file: $!\n";
403 my $text = do { local($/) ; <$f> };
404 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800405 if ($keywords) {
406 foreach my $line (keys %keyword_hash) {
407 if ($text =~ m/$keyword_hash{$line}/x) {
408 push(@keyword_tvi, $line);
409 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700410 }
411 }
Joe Perches03372db2010-03-05 13:43:00 -0800412 if ($file_emails) {
413 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;
414 push(@file_emails, clean_file_emails(@poss_addr));
415 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700416 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700417 } else {
418 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700419 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800420
Wolfram Sang3a4df132010-03-23 13:35:18 -0700421 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800422 or die "$P: Can't open $file: $!\n";
423 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700424 my $patch_line = $_;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700425 if (m/^\+\+\+\s+(\S+)/) {
426 my $filename = $1;
427 $filename =~ s@^[^/]*/@@;
428 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700429 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700430 push(@files, $filename);
Joe Perchesf5492662009-09-21 17:04:13 -0700431 } elsif (m/^\@\@ -(\d+),(\d+)/) {
432 if ($email_git_blame) {
433 push(@range, "$lastfile:$1:$2");
434 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700435 } elsif ($keywords) {
436 foreach my $line (keys %keyword_hash) {
437 if ($patch_line =~ m/^[+-].*$keyword_hash{$line}/x) {
438 push(@keyword_tvi, $line);
439 }
440 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700441 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700442 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800443 close($patch);
444
Joe Perches4a7fdb52009-04-10 12:28:57 -0700445 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700446 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700447 . "Add -f to options?\n";
448 }
449 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700450 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700451}
452
Joe Perches03372db2010-03-05 13:43:00 -0800453@file_emails = uniq(@file_emails);
454
Joe Perches683c6f82010-10-26 14:22:55 -0700455my %email_hash_name;
456my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700457my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700458my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700459my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700460my @scm = ();
461my @web = ();
462my @subsystem = ();
463my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700464my %deduplicate_name_hash = ();
465my %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700466my $signature_pattern;
Joe Perchescb7301c2009-04-07 20:40:12 -0700467
Joe Perches6ef1c522010-10-26 14:22:56 -0700468my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700469
Joe Perches6ef1c522010-10-26 14:22:56 -0700470if (@maintainers) {
471 @maintainers = merge_email(@maintainers);
472 output(@maintainers);
473}
Joe Perchescb7301c2009-04-07 20:40:12 -0700474
475if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700476 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700477 output(@scm);
478}
Joe Perches683c6f82010-10-26 14:22:55 -0700479
Joe Perchescb7301c2009-04-07 20:40:12 -0700480if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700481 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700482 output(@status);
483}
484
485if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700486 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700487 output(@subsystem);
488}
489
490if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700491 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700492 output(@web);
493}
494
495exit($exit);
496
Joe Perchesab6c9372011-01-12 16:59:50 -0800497sub range_is_maintained {
498 my ($start, $end) = @_;
499
500 for (my $i = $start; $i < $end; $i++) {
501 my $line = $typevalue[$i];
502 if ($line =~ m/^(\C):\s*(.*)/) {
503 my $type = $1;
504 my $value = $2;
505 if ($type eq 'S') {
506 if ($value =~ /(maintain|support)/i) {
507 return 1;
508 }
509 }
510 }
511 }
512 return 0;
513}
514
515sub range_has_maintainer {
516 my ($start, $end) = @_;
517
518 for (my $i = $start; $i < $end; $i++) {
519 my $line = $typevalue[$i];
520 if ($line =~ m/^(\C):\s*(.*)/) {
521 my $type = $1;
522 my $value = $2;
523 if ($type eq 'M') {
524 return 1;
525 }
526 }
527 }
528 return 0;
529}
530
Joe Perches6ef1c522010-10-26 14:22:56 -0700531sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700532 %email_hash_name = ();
533 %email_hash_address = ();
534 %commit_author_hash = ();
535 %commit_signer_hash = ();
536 @email_to = ();
537 %hash_list_to = ();
538 @list_to = ();
539 @scm = ();
540 @web = ();
541 @subsystem = ();
542 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700543 %deduplicate_name_hash = ();
544 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700545 if ($email_git_all_signature_types) {
546 $signature_pattern = "(.+?)[Bb][Yy]:";
547 } else {
548 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
549 }
550
551 # Find responsible parties
552
Joe Perchesb9e23312010-10-26 14:22:58 -0700553 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700554
Joe Perches683c6f82010-10-26 14:22:55 -0700555 foreach my $file (@files) {
556
557 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700558 my $tvi = find_first_section();
559 while ($tvi < @typevalue) {
560 my $start = find_starting_index($tvi);
561 my $end = find_ending_index($tvi);
562 my $exclude = 0;
563 my $i;
564
565 #Do not match excluded file patterns
566
567 for ($i = $start; $i < $end; $i++) {
568 my $line = $typevalue[$i];
569 if ($line =~ m/^(\C):\s*(.*)/) {
570 my $type = $1;
571 my $value = $2;
572 if ($type eq 'X') {
573 if (file_match_pattern($file, $value)) {
574 $exclude = 1;
575 last;
576 }
577 }
578 }
579 }
580
581 if (!$exclude) {
582 for ($i = $start; $i < $end; $i++) {
583 my $line = $typevalue[$i];
584 if ($line =~ m/^(\C):\s*(.*)/) {
585 my $type = $1;
586 my $value = $2;
587 if ($type eq 'F') {
588 if (file_match_pattern($file, $value)) {
589 my $value_pd = ($value =~ tr@/@@);
590 my $file_pd = ($file =~ tr@/@@);
591 $value_pd++ if (substr($value,-1,1) ne "/");
592 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800593 if ($value_pd >= $file_pd &&
594 range_is_maintained($start, $end) &&
595 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700596 $exact_pattern_match_hash{$file} = 1;
597 }
Joe Perches683c6f82010-10-26 14:22:55 -0700598 if ($pattern_depth == 0 ||
599 (($file_pd - $value_pd) < $pattern_depth)) {
600 $hash{$tvi} = $value_pd;
601 }
602 }
603 }
604 }
605 }
606 }
607 $tvi = $end + 1;
608 }
609
610 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
611 add_categories($line);
612 if ($sections) {
613 my $i;
614 my $start = find_starting_index($line);
615 my $end = find_ending_index($line);
616 for ($i = $start; $i < $end; $i++) {
617 my $line = $typevalue[$i];
618 if ($line =~ /^[FX]:/) { ##Restore file patterns
619 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
620 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
621 $line =~ s/\\\./\./g; ##Convert \. to .
622 $line =~ s/\.\*/\*/g; ##Convert .* to *
623 }
624 $line =~ s/^([A-Z]):/$1:\t/g;
625 print("$line\n");
626 }
627 print("\n");
628 }
629 }
Joe Perches683c6f82010-10-26 14:22:55 -0700630 }
631
632 if ($keywords) {
633 @keyword_tvi = sort_and_uniq(@keyword_tvi);
634 foreach my $line (@keyword_tvi) {
635 add_categories($line);
636 }
637 }
638
Joe Perchesb9e23312010-10-26 14:22:58 -0700639 foreach my $email (@email_to, @list_to) {
640 $email->[0] = deduplicate_email($email->[0]);
641 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700642
643 foreach my $file (@files) {
644 if ($email &&
645 ($email_git || ($email_git_fallback &&
646 !$exact_pattern_match_hash{$file}))) {
647 vcs_file_signoffs($file);
648 }
649 if ($email && $email_git_blame) {
650 vcs_file_blame($file);
651 }
652 }
653
Joe Perches683c6f82010-10-26 14:22:55 -0700654 if ($email) {
655 foreach my $chief (@penguin_chief) {
656 if ($chief =~ m/^(.*):(.*)/) {
657 my $email_address;
658
659 $email_address = format_email($1, $2, $email_usename);
660 if ($email_git_penguin_chiefs) {
661 push(@email_to, [$email_address, 'chief penguin']);
662 } else {
663 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
664 }
665 }
666 }
667
668 foreach my $email (@file_emails) {
669 my ($name, $address) = parse_email($email);
670
671 my $tmp_email = format_email($name, $address, $email_usename);
672 push_email_address($tmp_email, '');
673 add_role($tmp_email, 'in file');
674 }
675 }
676
677 my @to = ();
678 if ($email || $email_list) {
679 if ($email) {
680 @to = (@to, @email_to);
681 }
682 if ($email_list) {
683 @to = (@to, @list_to);
684 }
685 }
686
Joe Perches6ef1c522010-10-26 14:22:56 -0700687 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700688 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700689 }
Joe Perches683c6f82010-10-26 14:22:55 -0700690
691 return @to;
692}
693
Joe Perchescb7301c2009-04-07 20:40:12 -0700694sub file_match_pattern {
695 my ($file, $pattern) = @_;
696 if (substr($pattern, -1) eq "/") {
697 if ($file =~ m@^$pattern@) {
698 return 1;
699 }
700 } else {
701 if ($file =~ m@^$pattern@) {
702 my $s1 = ($file =~ tr@/@@);
703 my $s2 = ($pattern =~ tr@/@@);
704 if ($s1 == $s2) {
705 return 1;
706 }
707 }
708 }
709 return 0;
710}
711
712sub usage {
713 print <<EOT;
714usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700715 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700716version: $V
717
718MAINTAINER field selection options:
719 --email => print email address(es) if any
720 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700721 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700722 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700723 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700724 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700725 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
726 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
727 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700728 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700729 --git-since => git history to use (default: $email_git_since)
730 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700731 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700732 --m => include maintainer(s) if any
733 --n => include name 'Full Name <addr\@domain.tld>'
734 --l => include list(s) if any
735 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700736 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800737 --roles => show roles (status:subsystem, git-signer, list, etc...)
738 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800739 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700740 --scm => print SCM tree(s) if any
741 --status => print status if any
742 --subsystem => print subsystem name if any
743 --web => print website(s) if any
744
745Output type options:
746 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700747 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700748 --multiline => print 1 entry per line
749
Joe Perchescb7301c2009-04-07 20:40:12 -0700750Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700751 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700752 --keywords => scan patch for keywords (default: $keywords)
753 --sections => print all of the subsystem sections with pattern matches
754 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700755 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700756 --help => show this help information
757
Joe Perches3fb55652009-09-21 17:04:17 -0700758Default options:
Joe Perches7e1863a2011-01-12 16:59:49 -0800759 [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
760 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700761
Joe Perches870020f2009-07-29 15:04:28 -0700762Notes:
763 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700764 Used with "--git", git signators for _all_ files in and below
765 directory are examined as git recurses directories.
766 Any specified X: (exclude) pattern matches are _not_ ignored.
767 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800768 no individual file within the directory or subdirectory
769 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700770 Used with "--git-blame", does not iterate all files in directory
771 Using "--git-blame" is slow and may add old committers and authors
772 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800773 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
774 other automated tools that expect only ["name"] <email address>
775 may not work because of additional output after <email address>.
776 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
777 not the percentage of the entire file authored. # of commits is
778 not a good measure of amount of code authored. 1 major commit may
779 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800780 If git is not installed, but mercurial (hg) is installed and an .hg
781 repository exists, the following options apply to mercurial:
782 --git,
783 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
784 --git-blame
785 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700786 File ".get_maintainer.conf", if it exists in the linux kernel source root
787 directory, can change whatever get_maintainer defaults are desired.
788 Entries in this file can be any command line argument.
789 This file is prepended to any additional command line arguments.
790 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700791EOT
792}
793
794sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700795 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700796
Joe Perches47abc722010-10-26 14:22:57 -0700797 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
798 $lk_path .= "/";
799 }
800 if ( (-f "${lk_path}COPYING")
801 && (-f "${lk_path}CREDITS")
802 && (-f "${lk_path}Kbuild")
803 && (-f "${lk_path}MAINTAINERS")
804 && (-f "${lk_path}Makefile")
805 && (-f "${lk_path}README")
806 && (-d "${lk_path}Documentation")
807 && (-d "${lk_path}arch")
808 && (-d "${lk_path}include")
809 && (-d "${lk_path}drivers")
810 && (-d "${lk_path}fs")
811 && (-d "${lk_path}init")
812 && (-d "${lk_path}ipc")
813 && (-d "${lk_path}kernel")
814 && (-d "${lk_path}lib")
815 && (-d "${lk_path}scripts")) {
816 return 1;
817 }
818 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700819}
820
Joe Perches0e70e832009-09-21 17:04:20 -0700821sub parse_email {
822 my ($formatted_email) = @_;
823
824 my $name = "";
825 my $address = "";
826
Joe Perches11ecf532009-09-21 17:04:22 -0700827 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700828 $name = $1;
829 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700830 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700831 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700832 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700833 $address = $1;
834 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700835
836 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700837 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700838 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700839
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800840 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700841 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700842 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700843 }
Joe Perches0e70e832009-09-21 17:04:20 -0700844
845 return ($name, $address);
846}
847
848sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800849 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700850
851 my $formatted_email;
852
853 $name =~ s/^\s+|\s+$//g;
854 $name =~ s/^\"|\"$//g;
855 $address =~ s/^\s+|\s+$//g;
856
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800857 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700858 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
859 $name = "\"$name\"";
860 }
861
Joe Perchesa8af2432009-12-14 18:00:49 -0800862 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700863 if ("$name" eq "") {
864 $formatted_email = "$address";
865 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800866 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700867 }
868 } else {
869 $formatted_email = $address;
870 }
871
Joe Perchescb7301c2009-04-07 20:40:12 -0700872 return $formatted_email;
873}
874
Joe Perches272a8972010-01-08 14:42:48 -0800875sub find_first_section {
876 my $index = 0;
877
878 while ($index < @typevalue) {
879 my $tv = $typevalue[$index];
880 if (($tv =~ m/^(\C):\s*(.*)/)) {
881 last;
882 }
883 $index++;
884 }
885
886 return $index;
887}
888
Joe Perchesb7816552009-09-21 17:04:24 -0700889sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700890 my ($index) = @_;
891
892 while ($index > 0) {
893 my $tv = $typevalue[$index];
894 if (!($tv =~ m/^(\C):\s*(.*)/)) {
895 last;
896 }
897 $index--;
898 }
899
900 return $index;
901}
902
903sub find_ending_index {
904 my ($index) = @_;
905
906 while ($index < @typevalue) {
907 my $tv = $typevalue[$index];
908 if (!($tv =~ m/^(\C):\s*(.*)/)) {
909 last;
910 }
911 $index++;
912 }
913
914 return $index;
915}
916
Joe Perches3c7385b2009-12-14 18:00:46 -0800917sub get_maintainer_role {
918 my ($index) = @_;
919
920 my $i;
921 my $start = find_starting_index($index);
922 my $end = find_ending_index($index);
923
924 my $role;
925 my $subsystem = $typevalue[$start];
926 if (length($subsystem) > 20) {
927 $subsystem = substr($subsystem, 0, 17);
928 $subsystem =~ s/\s*$//;
929 $subsystem = $subsystem . "...";
930 }
931
932 for ($i = $start + 1; $i < $end; $i++) {
933 my $tv = $typevalue[$i];
934 if ($tv =~ m/^(\C):\s*(.*)/) {
935 my $ptype = $1;
936 my $pvalue = $2;
937 if ($ptype eq "S") {
938 $role = $pvalue;
939 }
940 }
941 }
942
943 $role = lc($role);
944 if ($role eq "supported") {
945 $role = "supporter";
946 } elsif ($role eq "maintained") {
947 $role = "maintainer";
948 } elsif ($role eq "odd fixes") {
949 $role = "odd fixer";
950 } elsif ($role eq "orphan") {
951 $role = "orphan minder";
952 } elsif ($role eq "obsolete") {
953 $role = "obsolete minder";
954 } elsif ($role eq "buried alive in reporters") {
955 $role = "chief penguin";
956 }
957
958 return $role . ":" . $subsystem;
959}
960
961sub get_list_role {
962 my ($index) = @_;
963
964 my $i;
965 my $start = find_starting_index($index);
966 my $end = find_ending_index($index);
967
968 my $subsystem = $typevalue[$start];
969 if (length($subsystem) > 20) {
970 $subsystem = substr($subsystem, 0, 17);
971 $subsystem =~ s/\s*$//;
972 $subsystem = $subsystem . "...";
973 }
974
975 if ($subsystem eq "THE REST") {
976 $subsystem = "";
977 }
978
979 return $subsystem;
980}
981
Joe Perchescb7301c2009-04-07 20:40:12 -0700982sub add_categories {
983 my ($index) = @_;
984
Joe Perchesb7816552009-09-21 17:04:24 -0700985 my $i;
986 my $start = find_starting_index($index);
987 my $end = find_ending_index($index);
988
989 push(@subsystem, $typevalue[$start]);
990
991 for ($i = $start + 1; $i < $end; $i++) {
992 my $tv = $typevalue[$i];
Joe Perches290603c2009-06-16 15:33:58 -0700993 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700994 my $ptype = $1;
995 my $pvalue = $2;
996 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -0700997 my $list_address = $pvalue;
998 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -0800999 my $list_role = get_list_role($i);
1000
1001 if ($list_role ne "") {
1002 $list_role = ":" . $list_role;
1003 }
Joe Perches290603c2009-06-16 15:33:58 -07001004 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1005 $list_address = $1;
1006 $list_additional = $2;
1007 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001008 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001009 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001010 if (!$hash_list_to{lc($list_address)}) {
1011 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001012 push(@list_to, [$list_address,
1013 "subscriber list${list_role}"]);
1014 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001015 }
1016 } else {
1017 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001018 if (!$hash_list_to{lc($list_address)}) {
1019 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001020 push(@list_to, [$list_address,
1021 "open list${list_role}"]);
1022 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001023 }
1024 }
1025 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001026 my ($name, $address) = parse_email($pvalue);
1027 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001028 if ($i > 0) {
1029 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -07001030 if ($tv =~ m/^(\C):\s*(.*)/) {
1031 if ($1 eq "P") {
1032 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001033 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001034 }
1035 }
1036 }
1037 }
Joe Perches0e70e832009-09-21 17:04:20 -07001038 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001039 my $role = get_maintainer_role($i);
1040 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001041 }
1042 } elsif ($ptype eq "T") {
1043 push(@scm, $pvalue);
1044 } elsif ($ptype eq "W") {
1045 push(@web, $pvalue);
1046 } elsif ($ptype eq "S") {
1047 push(@status, $pvalue);
1048 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001049 }
1050 }
1051}
1052
Joe Perches11ecf532009-09-21 17:04:22 -07001053sub email_inuse {
1054 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001055
Joe Perches11ecf532009-09-21 17:04:22 -07001056 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001057 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1058 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001059
Joe Perches0e70e832009-09-21 17:04:20 -07001060 return 0;
1061}
1062
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001063sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001064 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001065
Joe Perches0e70e832009-09-21 17:04:20 -07001066 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001067
Joe Perchesb7816552009-09-21 17:04:24 -07001068 if ($address eq "") {
1069 return 0;
1070 }
1071
Joe Perches11ecf532009-09-21 17:04:22 -07001072 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001073 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001074 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001075 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001076 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001077 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001078 }
Joe Perchesb7816552009-09-21 17:04:24 -07001079
1080 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001081}
1082
1083sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001084 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001085
1086 my @address_list = ();
1087
Joe Perches5f2441e2009-06-16 15:34:02 -07001088 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001089 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001090 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001091 my $array_count = shift(@address_list);
1092 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001093 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001094 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001095 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001096 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001097 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1098 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001099 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001100}
1101
Joe Perches3c7385b2009-12-14 18:00:46 -08001102sub add_role {
1103 my ($line, $role) = @_;
1104
1105 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001106 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001107
1108 foreach my $entry (@email_to) {
1109 if ($email_remove_duplicates) {
1110 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001111 if (($name eq $entry_name || $address eq $entry_address)
1112 && ($role eq "" || !($entry->[1] =~ m/$role/))
1113 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001114 if ($entry->[1] eq "") {
1115 $entry->[1] = "$role";
1116 } else {
1117 $entry->[1] = "$entry->[1],$role";
1118 }
1119 }
1120 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001121 if ($email eq $entry->[0]
1122 && ($role eq "" || !($entry->[1] =~ m/$role/))
1123 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001124 if ($entry->[1] eq "") {
1125 $entry->[1] = "$role";
1126 } else {
1127 $entry->[1] = "$entry->[1],$role";
1128 }
1129 }
1130 }
1131 }
1132}
1133
Joe Perchescb7301c2009-04-07 20:40:12 -07001134sub which {
1135 my ($bin) = @_;
1136
Joe Perchesf5f50782009-06-16 15:34:00 -07001137 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001138 if (-e "$path/$bin") {
1139 return "$path/$bin";
1140 }
1141 }
1142
1143 return "";
1144}
1145
Joe Perchesbcde44e2010-10-26 14:22:53 -07001146sub which_conf {
1147 my ($conf) = @_;
1148
1149 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1150 if (-e "$path/$conf") {
1151 return "$path/$conf";
1152 }
1153 }
1154
1155 return "";
1156}
1157
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001158sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001159 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001160
Joe Perches47abc722010-10-26 14:22:57 -07001161 my ($name, $address) = parse_email($line);
1162 my $email = format_email($name, $address, 1);
1163 my $real_name = $name;
1164 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001165
Joe Perches47abc722010-10-26 14:22:57 -07001166 if (exists $mailmap->{names}->{$email} ||
1167 exists $mailmap->{addresses}->{$email}) {
1168 if (exists $mailmap->{names}->{$email}) {
1169 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001170 }
Joe Perches47abc722010-10-26 14:22:57 -07001171 if (exists $mailmap->{addresses}->{$email}) {
1172 $real_address = $mailmap->{addresses}->{$email};
1173 }
1174 } else {
1175 if (exists $mailmap->{names}->{$address}) {
1176 $real_name = $mailmap->{names}->{$address};
1177 }
1178 if (exists $mailmap->{addresses}->{$address}) {
1179 $real_address = $mailmap->{addresses}->{$address};
1180 }
1181 }
1182 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001183}
1184
1185sub mailmap {
1186 my (@addresses) = @_;
1187
Joe Perchesb9e23312010-10-26 14:22:58 -07001188 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001189 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001190 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001191 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001192 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1193 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001194}
1195
1196sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001197 my %address_map;
1198 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001199
Joe Perches47abc722010-10-26 14:22:57 -07001200 foreach my $email (@emails) {
1201 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001202 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001203 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001204 $email = format_email($name, $address, 1);
1205 } else {
1206 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001207 }
Joe Perches47abc722010-10-26 14:22:57 -07001208 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001209}
1210
Joe Perches60db31a2009-12-14 18:00:50 -08001211sub git_execute_cmd {
1212 my ($cmd) = @_;
1213 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001214
Joe Perches60db31a2009-12-14 18:00:50 -08001215 my $output = `$cmd`;
1216 $output =~ s/^\s*//gm;
1217 @lines = split("\n", $output);
1218
1219 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001220}
1221
Joe Perches60db31a2009-12-14 18:00:50 -08001222sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001223 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001224 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001225
Joe Perches60db31a2009-12-14 18:00:50 -08001226 my $output = `$cmd`;
1227 @lines = split("\n", $output);
1228
1229 return @lines;
1230}
1231
Joe Perches683c6f82010-10-26 14:22:55 -07001232sub extract_formatted_signatures {
1233 my (@signature_lines) = @_;
1234
1235 my @type = @signature_lines;
1236
1237 s/\s*(.*):.*/$1/ for (@type);
1238
1239 # cut -f2- -d":"
1240 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1241
1242## Reformat email addresses (with names) to avoid badly written signatures
1243
1244 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001245 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001246 }
1247
1248 return (\@type, \@signature_lines);
1249}
1250
Joe Perches60db31a2009-12-14 18:00:50 -08001251sub vcs_find_signers {
1252 my ($cmd) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001253 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001254 my @lines = ();
1255 my @signatures = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001256
Joe Perches60db31a2009-12-14 18:00:50 -08001257 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001258
Joe Perches60db31a2009-12-14 18:00:50 -08001259 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchescb7301c2009-04-07 20:40:12 -07001260
Joe Perches60db31a2009-12-14 18:00:50 -08001261 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001262
Joe Perches683c6f82010-10-26 14:22:55 -07001263 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
1264
1265 return (0, @signatures) if !@signatures;
1266
1267 save_commits_by_author(@lines) if ($interactive);
1268 save_commits_by_signer(@lines) if ($interactive);
1269
Joe Perches0e70e832009-09-21 17:04:20 -07001270 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001271 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001272 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001273
Joe Perches683c6f82010-10-26 14:22:55 -07001274 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001275
Joe Perches683c6f82010-10-26 14:22:55 -07001276 return ($commits, @$signers_ref);
Joe Perchesa8af2432009-12-14 18:00:49 -08001277}
1278
Joe Perches63ab52d2010-10-26 14:22:51 -07001279sub vcs_find_author {
1280 my ($cmd) = @_;
1281 my @lines = ();
1282
1283 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1284
1285 if (!$email_git_penguin_chiefs) {
1286 @lines = grep(!/${penguin_chiefs}/i, @lines);
1287 }
1288
1289 return @lines if !@lines;
1290
Joe Perches683c6f82010-10-26 14:22:55 -07001291 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001292 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001293 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1294 my $author = $1;
1295 my ($name, $address) = parse_email($author);
1296 $author = format_email($name, $address, 1);
1297 push(@authors, $author);
1298 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001299 }
1300
Joe Perches683c6f82010-10-26 14:22:55 -07001301 save_commits_by_author(@lines) if ($interactive);
1302 save_commits_by_signer(@lines) if ($interactive);
1303
1304 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001305}
1306
Joe Perches60db31a2009-12-14 18:00:50 -08001307sub vcs_save_commits {
1308 my ($cmd) = @_;
1309 my @lines = ();
1310 my @commits = ();
1311
1312 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1313
1314 foreach my $line (@lines) {
1315 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1316 push(@commits, $1);
1317 }
1318 }
1319
1320 return @commits;
1321}
1322
1323sub vcs_blame {
1324 my ($file) = @_;
1325 my $cmd;
1326 my @commits = ();
1327
1328 return @commits if (!(-f $file));
1329
1330 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1331 my @all_commits = ();
1332
1333 $cmd = $VCS_cmds{"blame_file_cmd"};
1334 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1335 @all_commits = vcs_save_commits($cmd);
1336
1337 foreach my $file_range_diff (@range) {
1338 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1339 my $diff_file = $1;
1340 my $diff_start = $2;
1341 my $diff_length = $3;
1342 next if ("$file" ne "$diff_file");
1343 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1344 push(@commits, $all_commits[$i]);
1345 }
1346 }
1347 } elsif (@range) {
1348 foreach my $file_range_diff (@range) {
1349 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1350 my $diff_file = $1;
1351 my $diff_start = $2;
1352 my $diff_length = $3;
1353 next if ("$file" ne "$diff_file");
1354 $cmd = $VCS_cmds{"blame_range_cmd"};
1355 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1356 push(@commits, vcs_save_commits($cmd));
1357 }
1358 } else {
1359 $cmd = $VCS_cmds{"blame_file_cmd"};
1360 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1361 @commits = vcs_save_commits($cmd);
1362 }
1363
Joe Perches63ab52d2010-10-26 14:22:51 -07001364 foreach my $commit (@commits) {
1365 $commit =~ s/^\^//g;
1366 }
1367
Joe Perches60db31a2009-12-14 18:00:50 -08001368 return @commits;
1369}
1370
1371my $printed_novcs = 0;
1372sub vcs_exists {
1373 %VCS_cmds = %VCS_cmds_git;
1374 return 1 if eval $VCS_cmds{"available"};
1375 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001376 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001377 %VCS_cmds = ();
1378 if (!$printed_novcs) {
1379 warn("$P: No supported VCS found. Add --nogit to options?\n");
1380 warn("Using a git repository produces better results.\n");
1381 warn("Try Linus Torvalds' latest git repository using:\n");
1382 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git\n");
1383 $printed_novcs = 1;
1384 }
1385 return 0;
1386}
1387
Joe Perches683c6f82010-10-26 14:22:55 -07001388sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001389 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001390 return $vcs_used == 1;
1391}
1392
1393sub vcs_is_hg {
1394 return $vcs_used == 2;
1395}
1396
Joe Perches6ef1c522010-10-26 14:22:56 -07001397sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001398 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001399 my @list = @$list_ref;
1400
Joe Perches683c6f82010-10-26 14:22:55 -07001401 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001402
1403 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001404 my %authored;
1405 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001406 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001407 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001408 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001409 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1410 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001411 $authored{$count} = 0;
1412 $signed{$count} = 0;
1413 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001414 }
1415
1416 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001417 my $done = 0;
1418 my $print_options = 0;
1419 my $redraw = 1;
1420 while (!$done) {
1421 $count = 0;
1422 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001423 printf STDERR "\n%1s %2s %-65s",
1424 "*", "#", "email/list and role:stats";
1425 if ($email_git ||
1426 ($email_git_fallback && !$maintained) ||
1427 $email_git_blame) {
1428 print STDERR "auth sign";
1429 }
1430 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001431 foreach my $entry (@list) {
1432 my $email = $entry->[0];
1433 my $role = $entry->[1];
1434 my $sel = "";
1435 $sel = "*" if ($selected{$count});
1436 my $commit_author = $commit_author_hash{$email};
1437 my $commit_signer = $commit_signer_hash{$email};
1438 my $authored = 0;
1439 my $signed = 0;
1440 $authored++ for (@{$commit_author});
1441 $signed++ for (@{$commit_signer});
1442 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1443 printf STDERR "%4d %4d", $authored, $signed
1444 if ($authored > 0 || $signed > 0);
1445 printf STDERR "\n %s\n", $role;
1446 if ($authored{$count}) {
1447 my $commit_author = $commit_author_hash{$email};
1448 foreach my $ref (@{$commit_author}) {
1449 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001450 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001451 }
Joe Perches683c6f82010-10-26 14:22:55 -07001452 if ($signed{$count}) {
1453 my $commit_signer = $commit_signer_hash{$email};
1454 foreach my $ref (@{$commit_signer}) {
1455 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1456 }
1457 }
1458
1459 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001460 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001461 }
Joe Perches683c6f82010-10-26 14:22:55 -07001462 my $date_ref = \$email_git_since;
1463 $date_ref = \$email_hg_since if (vcs_is_hg());
1464 if ($print_options) {
1465 $print_options = 0;
1466 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001467 print STDERR <<EOT
1468
1469Version Control options:
1470g use git history [$email_git]
1471gf use git-fallback [$email_git_fallback]
1472b use git blame [$email_git_blame]
1473bs use blame signatures [$email_git_blame_signatures]
1474c# minimum commits [$email_git_min_signatures]
1475%# min percent [$email_git_min_percent]
1476d# history to use [$$date_ref]
1477x# max maintainers [$email_git_max_maintainers]
1478t all signature types [$email_git_all_signature_types]
1479m use .mailmap [$email_use_mailmap]
1480EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001481 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001482 print STDERR <<EOT
1483
1484Additional options:
14850 toggle all
1486tm toggle maintainers
1487tg toggle git entries
1488tl toggle open list entries
1489ts toggle subscriber list entries
1490f emails in file [$file_emails]
1491k keywords in file [$keywords]
1492r remove duplicates [$email_remove_duplicates]
1493p# pattern match depth [$pattern_depth]
1494EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001495 }
1496 print STDERR
1497"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1498
1499 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001500 chomp($input);
1501
Joe Perches683c6f82010-10-26 14:22:55 -07001502 $redraw = 1;
1503 my $rerun = 0;
1504 my @wish = split(/[, ]+/, $input);
1505 foreach my $nr (@wish) {
1506 $nr = lc($nr);
1507 my $sel = substr($nr, 0, 1);
1508 my $str = substr($nr, 1);
1509 my $val = 0;
1510 $val = $1 if $str =~ /^(\d+)$/;
1511
1512 if ($sel eq "y") {
1513 $interactive = 0;
1514 $done = 1;
1515 $output_rolestats = 0;
1516 $output_roles = 0;
1517 last;
1518 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1519 $selected{$nr - 1} = !$selected{$nr - 1};
1520 } elsif ($sel eq "*" || $sel eq '^') {
1521 my $toggle = 0;
1522 $toggle = 1 if ($sel eq '*');
1523 for (my $i = 0; $i < $count; $i++) {
1524 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001525 }
Joe Perches683c6f82010-10-26 14:22:55 -07001526 } elsif ($sel eq "0") {
1527 for (my $i = 0; $i < $count; $i++) {
1528 $selected{$i} = !$selected{$i};
1529 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001530 } elsif ($sel eq "t") {
1531 if (lc($str) eq "m") {
1532 for (my $i = 0; $i < $count; $i++) {
1533 $selected{$i} = !$selected{$i}
1534 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1535 }
1536 } elsif (lc($str) eq "g") {
1537 for (my $i = 0; $i < $count; $i++) {
1538 $selected{$i} = !$selected{$i}
1539 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1540 }
1541 } elsif (lc($str) eq "l") {
1542 for (my $i = 0; $i < $count; $i++) {
1543 $selected{$i} = !$selected{$i}
1544 if ($list[$i]->[1] =~ /^(open list)/i);
1545 }
1546 } elsif (lc($str) eq "s") {
1547 for (my $i = 0; $i < $count; $i++) {
1548 $selected{$i} = !$selected{$i}
1549 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1550 }
1551 }
Joe Perches683c6f82010-10-26 14:22:55 -07001552 } elsif ($sel eq "a") {
1553 if ($val > 0 && $val <= $count) {
1554 $authored{$val - 1} = !$authored{$val - 1};
1555 } elsif ($str eq '*' || $str eq '^') {
1556 my $toggle = 0;
1557 $toggle = 1 if ($str eq '*');
1558 for (my $i = 0; $i < $count; $i++) {
1559 $authored{$i} = $toggle;
1560 }
1561 }
1562 } elsif ($sel eq "s") {
1563 if ($val > 0 && $val <= $count) {
1564 $signed{$val - 1} = !$signed{$val - 1};
1565 } elsif ($str eq '*' || $str eq '^') {
1566 my $toggle = 0;
1567 $toggle = 1 if ($str eq '*');
1568 for (my $i = 0; $i < $count; $i++) {
1569 $signed{$i} = $toggle;
1570 }
1571 }
1572 } elsif ($sel eq "o") {
1573 $print_options = 1;
1574 $redraw = 1;
1575 } elsif ($sel eq "g") {
1576 if ($str eq "f") {
1577 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001578 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001579 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001580 }
Joe Perches683c6f82010-10-26 14:22:55 -07001581 $rerun = 1;
1582 } elsif ($sel eq "b") {
1583 if ($str eq "s") {
1584 bool_invert(\$email_git_blame_signatures);
1585 } else {
1586 bool_invert(\$email_git_blame);
1587 }
1588 $rerun = 1;
1589 } elsif ($sel eq "c") {
1590 if ($val > 0) {
1591 $email_git_min_signatures = $val;
1592 $rerun = 1;
1593 }
1594 } elsif ($sel eq "x") {
1595 if ($val > 0) {
1596 $email_git_max_maintainers = $val;
1597 $rerun = 1;
1598 }
1599 } elsif ($sel eq "%") {
1600 if ($str ne "" && $val >= 0) {
1601 $email_git_min_percent = $val;
1602 $rerun = 1;
1603 }
1604 } elsif ($sel eq "d") {
1605 if (vcs_is_git()) {
1606 $email_git_since = $str;
1607 } elsif (vcs_is_hg()) {
1608 $email_hg_since = $str;
1609 }
1610 $rerun = 1;
1611 } elsif ($sel eq "t") {
1612 bool_invert(\$email_git_all_signature_types);
1613 $rerun = 1;
1614 } elsif ($sel eq "f") {
1615 bool_invert(\$file_emails);
1616 $rerun = 1;
1617 } elsif ($sel eq "r") {
1618 bool_invert(\$email_remove_duplicates);
1619 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001620 } elsif ($sel eq "m") {
1621 bool_invert(\$email_use_mailmap);
1622 read_mailmap();
1623 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001624 } elsif ($sel eq "k") {
1625 bool_invert(\$keywords);
1626 $rerun = 1;
1627 } elsif ($sel eq "p") {
1628 if ($str ne "" && $val >= 0) {
1629 $pattern_depth = $val;
1630 $rerun = 1;
1631 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001632 } elsif ($sel eq "h" || $sel eq "?") {
1633 print STDERR <<EOT
1634
1635Interactive mode allows you to select the various maintainers, submitters,
1636commit signers and mailing lists that could be CC'd on a patch.
1637
1638Any *'d entry is selected.
1639
Joe Perches47abc722010-10-26 14:22:57 -07001640If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001641history of files in the patch. Also, each line of the current file can
1642be matched to its commit author and that commits signers with blame.
1643
1644Various knobs exist to control the length of time for active commit
1645tracking, the maximum number of commit authors and signers to add,
1646and such.
1647
1648Enter selections at the prompt until you are satisfied that the selected
1649maintainers are appropriate. You may enter multiple selections separated
1650by either commas or spaces.
1651
1652EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001653 } else {
1654 print STDERR "invalid option: '$nr'\n";
1655 $redraw = 0;
1656 }
1657 }
1658 if ($rerun) {
1659 print STDERR "git-blame can be very slow, please have patience..."
1660 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001661 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001662 }
1663 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001664
1665 #drop not selected entries
1666 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001667 my @new_emailto = ();
1668 foreach my $entry (@list) {
1669 if ($selected{$count}) {
1670 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001671 }
1672 $count++;
1673 }
Joe Perches683c6f82010-10-26 14:22:55 -07001674 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001675}
1676
Joe Perches683c6f82010-10-26 14:22:55 -07001677sub bool_invert {
1678 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001679
Joe Perches683c6f82010-10-26 14:22:55 -07001680 if ($$bool_ref) {
1681 $$bool_ref = 0;
1682 } else {
1683 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001684 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001685}
1686
Joe Perchesb9e23312010-10-26 14:22:58 -07001687sub deduplicate_email {
1688 my ($email) = @_;
1689
1690 my $matched = 0;
1691 my ($name, $address) = parse_email($email);
1692 $email = format_email($name, $address, 1);
1693 $email = mailmap_email($email);
1694
1695 return $email if (!$email_remove_duplicates);
1696
1697 ($name, $address) = parse_email($email);
1698
Joe Perchesfae99202010-10-26 14:22:58 -07001699 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001700 $name = $deduplicate_name_hash{lc($name)}->[0];
1701 $address = $deduplicate_name_hash{lc($name)}->[1];
1702 $matched = 1;
1703 } elsif ($deduplicate_address_hash{lc($address)}) {
1704 $name = $deduplicate_address_hash{lc($address)}->[0];
1705 $address = $deduplicate_address_hash{lc($address)}->[1];
1706 $matched = 1;
1707 }
1708 if (!$matched) {
1709 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1710 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1711 }
1712 $email = format_email($name, $address, 1);
1713 $email = mailmap_email($email);
1714 return $email;
1715}
1716
Joe Perches683c6f82010-10-26 14:22:55 -07001717sub save_commits_by_author {
1718 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001719
Joe Perches683c6f82010-10-26 14:22:55 -07001720 my @authors = ();
1721 my @commits = ();
1722 my @subjects = ();
1723
1724 foreach my $line (@lines) {
1725 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1726 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001727 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001728 push(@authors, $author);
1729 }
1730 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1731 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1732 }
1733
1734 for (my $i = 0; $i < @authors; $i++) {
1735 my $exists = 0;
1736 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1737 if (@{$ref}[0] eq $commits[$i] &&
1738 @{$ref}[1] eq $subjects[$i]) {
1739 $exists = 1;
1740 last;
1741 }
1742 }
1743 if (!$exists) {
1744 push(@{$commit_author_hash{$authors[$i]}},
1745 [ ($commits[$i], $subjects[$i]) ]);
1746 }
1747 }
1748}
1749
1750sub save_commits_by_signer {
1751 my (@lines) = @_;
1752
1753 my $commit = "";
1754 my $subject = "";
1755
1756 foreach my $line (@lines) {
1757 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1758 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1759 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1760 my @signatures = ($line);
1761 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1762 my @types = @$types_ref;
1763 my @signers = @$signers_ref;
1764
1765 my $type = $types[0];
1766 my $signer = $signers[0];
1767
Joe Perchesb9e23312010-10-26 14:22:58 -07001768 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001769
Joe Perches683c6f82010-10-26 14:22:55 -07001770 my $exists = 0;
1771 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1772 if (@{$ref}[0] eq $commit &&
1773 @{$ref}[1] eq $subject &&
1774 @{$ref}[2] eq $type) {
1775 $exists = 1;
1776 last;
1777 }
1778 }
1779 if (!$exists) {
1780 push(@{$commit_signer_hash{$signer}},
1781 [ ($commit, $subject, $type) ]);
1782 }
1783 }
1784 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001785}
1786
Joe Perches60db31a2009-12-14 18:00:50 -08001787sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001788 my ($role, $divisor, @lines) = @_;
1789
1790 my %hash;
1791 my $count = 0;
1792
Joe Perchesa8af2432009-12-14 18:00:49 -08001793 return if (@lines <= 0);
1794
1795 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001796 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001797 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001798 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001799
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001800 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001801
Joe Perches63ab52d2010-10-26 14:22:51 -07001802 return if (@lines <= 0);
1803
Joe Perches0e70e832009-09-21 17:04:20 -07001804 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001805
Joe Perches11ecf532009-09-21 17:04:22 -07001806 # uniq -c
1807 $hash{$_}++ for @lines;
1808
1809 # sort -rn
1810 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1811 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001812 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001813
Joe Perchesa8af2432009-12-14 18:00:49 -08001814 $percent = 100 if ($percent > 100);
Joe Perches11ecf532009-09-21 17:04:22 -07001815 $count++;
1816 last if ($sign_offs < $email_git_min_signatures ||
1817 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001818 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001819 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001820 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001821 my $fmt_percent = sprintf("%.0f", $percent);
1822 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1823 } else {
1824 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001825 }
Joe Perchesf5492662009-09-21 17:04:13 -07001826 }
1827}
1828
Joe Perches60db31a2009-12-14 18:00:50 -08001829sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001830 my ($file) = @_;
1831
1832 my @signers = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001833 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001834
Joe Perches683c6f82010-10-26 14:22:55 -07001835 $vcs_used = vcs_exists();
1836 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001837
Joe Perches60db31a2009-12-14 18:00:50 -08001838 my $cmd = $VCS_cmds{"find_signers_cmd"};
1839 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1840
1841 ($commits, @signers) = vcs_find_signers($cmd);
Joe Perchesb9e23312010-10-26 14:22:58 -07001842
1843 foreach my $signer (@signers) {
1844 $signer = deduplicate_email($signer);
1845 }
1846
Joe Perches60db31a2009-12-14 18:00:50 -08001847 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001848}
1849
Joe Perches60db31a2009-12-14 18:00:50 -08001850sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001851 my ($file) = @_;
1852
Joe Perches60db31a2009-12-14 18:00:50 -08001853 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001854 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001855 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001856 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001857 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001858
Joe Perches683c6f82010-10-26 14:22:55 -07001859 $vcs_used = vcs_exists();
1860 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07001861
Joe Perches63ab52d2010-10-26 14:22:51 -07001862 @all_commits = vcs_blame($file);
1863 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08001864 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001865 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001866
Joe Perches683c6f82010-10-26 14:22:55 -07001867 if ($email_git_blame_signatures) {
1868 if (vcs_is_hg()) {
1869 my $commit_count;
1870 my @commit_signers = ();
1871 my $commit = join(" -r ", @commits);
1872 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07001873
Joe Perches683c6f82010-10-26 14:22:55 -07001874 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1875 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08001876
Joe Perches683c6f82010-10-26 14:22:55 -07001877 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
Joe Perches63ab52d2010-10-26 14:22:51 -07001878
Joe Perches683c6f82010-10-26 14:22:55 -07001879 push(@signers, @commit_signers);
1880 } else {
1881 foreach my $commit (@commits) {
1882 my $commit_count;
1883 my @commit_signers = ();
1884 my $cmd;
1885
1886 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1887 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1888
1889 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
1890
1891 push(@signers, @commit_signers);
1892 }
1893 }
Joe Perchesf5492662009-09-21 17:04:13 -07001894 }
1895
Joe Perchesa8af2432009-12-14 18:00:49 -08001896 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07001897 if ($output_rolestats) {
1898 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07001899 if (vcs_is_hg()) {{ # Double brace for last exit
1900 my $commit_count;
1901 my @commit_signers = ();
1902 @commits = uniq(@commits);
1903 @commits = sort(@commits);
1904 my $commit = join(" -r ", @commits);
1905 my $cmd;
1906
1907 $cmd = $VCS_cmds{"find_commit_author_cmd"};
1908 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1909
1910 my @lines = ();
1911
1912 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1913
1914 if (!$email_git_penguin_chiefs) {
1915 @lines = grep(!/${penguin_chiefs}/i, @lines);
1916 }
1917
1918 last if !@lines;
1919
1920 my @authors = ();
1921 foreach my $line (@lines) {
1922 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1923 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001924 $author = deduplicate_email($author);
1925 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07001926 }
1927 }
1928
1929 save_commits_by_author(@lines) if ($interactive);
1930 save_commits_by_signer(@lines) if ($interactive);
1931
1932 push(@signers, @authors);
1933 }}
1934 else {
1935 foreach my $commit (@commits) {
1936 my $i;
1937 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
1938 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1939 my @author = vcs_find_author($cmd);
1940 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07001941
1942 my $formatted_author = deduplicate_email($author[0]);
1943
Joe Perches683c6f82010-10-26 14:22:55 -07001944 my $count = grep(/$commit/, @all_commits);
1945 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001946 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07001947 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001948 }
1949 }
1950 if (@blame_signers) {
1951 vcs_assign("authored lines", $total_lines, @blame_signers);
1952 }
1953 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001954 foreach my $signer (@signers) {
1955 $signer = deduplicate_email($signer);
1956 }
Joe Perches60db31a2009-12-14 18:00:50 -08001957 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001958 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07001959 foreach my $signer (@signers) {
1960 $signer = deduplicate_email($signer);
1961 }
Joe Perches60db31a2009-12-14 18:00:50 -08001962 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07001963 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001964}
1965
1966sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001967 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001968
1969 my %saw;
1970 @parms = grep(!$saw{$_}++, @parms);
1971 return @parms;
1972}
1973
1974sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001975 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001976
1977 my %saw;
1978 @parms = sort @parms;
1979 @parms = grep(!$saw{$_}++, @parms);
1980 return @parms;
1981}
1982
Joe Perches03372db2010-03-05 13:43:00 -08001983sub clean_file_emails {
1984 my (@file_emails) = @_;
1985 my @fmt_emails = ();
1986
1987 foreach my $email (@file_emails) {
1988 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
1989 my ($name, $address) = parse_email($email);
1990 if ($name eq '"[,\.]"') {
1991 $name = "";
1992 }
1993
1994 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
1995 if (@nw > 2) {
1996 my $first = $nw[@nw - 3];
1997 my $middle = $nw[@nw - 2];
1998 my $last = $nw[@nw - 1];
1999
2000 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2001 (length($first) == 2 && substr($first, -1) eq ".")) ||
2002 (length($middle) == 1 ||
2003 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2004 $name = "$first $middle $last";
2005 } else {
2006 $name = "$middle $last";
2007 }
2008 }
2009
2010 if (substr($name, -1) =~ /[,\.]/) {
2011 $name = substr($name, 0, length($name) - 1);
2012 } elsif (substr($name, -2) =~ /[,\.]"/) {
2013 $name = substr($name, 0, length($name) - 2) . '"';
2014 }
2015
2016 if (substr($name, 0, 1) =~ /[,\.]/) {
2017 $name = substr($name, 1, length($name) - 1);
2018 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2019 $name = '"' . substr($name, 2, length($name) - 2);
2020 }
2021
2022 my $fmt_email = format_email($name, $address, $email_usename);
2023 push(@fmt_emails, $fmt_email);
2024 }
2025 return @fmt_emails;
2026}
2027
Joe Perches3c7385b2009-12-14 18:00:46 -08002028sub merge_email {
2029 my @lines;
2030 my %saw;
2031
2032 for (@_) {
2033 my ($address, $role) = @$_;
2034 if (!$saw{$address}) {
2035 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002036 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002037 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002038 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002039 }
2040 $saw{$address} = 1;
2041 }
2042 }
2043
2044 return @lines;
2045}
2046
Joe Perchescb7301c2009-04-07 20:40:12 -07002047sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002048 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002049
2050 if ($output_multiline) {
2051 foreach my $line (@parms) {
2052 print("${line}\n");
2053 }
2054 } else {
2055 print(join($output_separator, @parms));
2056 print("\n");
2057 }
2058}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002059
2060my $rfc822re;
2061
2062sub make_rfc822re {
2063# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2064# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2065# This regexp will only work on addresses which have had comments stripped
2066# and replaced with rfc822_lwsp.
2067
2068 my $specials = '()<>@,;:\\\\".\\[\\]';
2069 my $controls = '\\000-\\037\\177';
2070
2071 my $dtext = "[^\\[\\]\\r\\\\]";
2072 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2073
2074 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2075
2076# Use zero-width assertion to spot the limit of an atom. A simple
2077# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2078 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2079 my $word = "(?:$atom|$quoted_string)";
2080 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2081
2082 my $sub_domain = "(?:$atom|$domain_literal)";
2083 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2084
2085 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2086
2087 my $phrase = "$word*";
2088 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2089 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2090 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2091
2092 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2093 my $address = "(?:$mailbox|$group)";
2094
2095 return "$rfc822_lwsp*$address";
2096}
2097
2098sub rfc822_strip_comments {
2099 my $s = shift;
2100# Recursively remove comments, and replace with a single space. The simpler
2101# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2102# chars in atoms, for example.
2103
2104 while ($s =~ s/^((?:[^"\\]|\\.)*
2105 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2106 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2107 return $s;
2108}
2109
2110# valid: returns true if the parameter is an RFC822 valid address
2111#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002112sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002113 my $s = rfc822_strip_comments(shift);
2114
2115 if (!$rfc822re) {
2116 $rfc822re = make_rfc822re();
2117 }
2118
2119 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2120}
2121
2122# validlist: In scalar context, returns true if the parameter is an RFC822
2123# valid list of addresses.
2124#
2125# In list context, returns an empty list on failure (an invalid
2126# address was found); otherwise a list whose first element is the
2127# number of addresses found and whose remaining elements are the
2128# addresses. This is needed to disambiguate failure (invalid)
2129# from success with no addresses found, because an empty string is
2130# a valid list.
2131
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002132sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002133 my $s = rfc822_strip_comments(shift);
2134
2135 if (!$rfc822re) {
2136 $rfc822re = make_rfc822re();
2137 }
2138 # * null list items are valid according to the RFC
2139 # * the '1' business is to aid in distinguishing failure from no results
2140
2141 my @r;
2142 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2143 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002144 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002145 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002146 }
2147 return wantarray ? (scalar(@r), @r) : 1;
2148 }
Joe Perches60db31a2009-12-14 18:00:50 -08002149 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002150}