blob: d21ec3a89603b0fa8155671587deaebaa52b6163 [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 Perchesfae99202010-10-26 14:22:58 -070016my $V = '0.26-beta6';
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;
43my $output_rolestats = 0;
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 Perches6ef1c522010-10-26 14:22:56 -0700497sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700498 %email_hash_name = ();
499 %email_hash_address = ();
500 %commit_author_hash = ();
501 %commit_signer_hash = ();
502 @email_to = ();
503 %hash_list_to = ();
504 @list_to = ();
505 @scm = ();
506 @web = ();
507 @subsystem = ();
508 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700509 %deduplicate_name_hash = ();
510 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700511 if ($email_git_all_signature_types) {
512 $signature_pattern = "(.+?)[Bb][Yy]:";
513 } else {
514 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
515 }
516
517 # Find responsible parties
518
Joe Perchesb9e23312010-10-26 14:22:58 -0700519 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700520
Joe Perches683c6f82010-10-26 14:22:55 -0700521 foreach my $file (@files) {
522
523 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700524 my $tvi = find_first_section();
525 while ($tvi < @typevalue) {
526 my $start = find_starting_index($tvi);
527 my $end = find_ending_index($tvi);
528 my $exclude = 0;
529 my $i;
530
531 #Do not match excluded file patterns
532
533 for ($i = $start; $i < $end; $i++) {
534 my $line = $typevalue[$i];
535 if ($line =~ m/^(\C):\s*(.*)/) {
536 my $type = $1;
537 my $value = $2;
538 if ($type eq 'X') {
539 if (file_match_pattern($file, $value)) {
540 $exclude = 1;
541 last;
542 }
543 }
544 }
545 }
546
547 if (!$exclude) {
548 for ($i = $start; $i < $end; $i++) {
549 my $line = $typevalue[$i];
550 if ($line =~ m/^(\C):\s*(.*)/) {
551 my $type = $1;
552 my $value = $2;
553 if ($type eq 'F') {
554 if (file_match_pattern($file, $value)) {
555 my $value_pd = ($value =~ tr@/@@);
556 my $file_pd = ($file =~ tr@/@@);
557 $value_pd++ if (substr($value,-1,1) ne "/");
558 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perches6ef1c522010-10-26 14:22:56 -0700559 if ($value_pd >= $file_pd) {
560 $exact_pattern_match_hash{$file} = 1;
561 }
Joe Perches683c6f82010-10-26 14:22:55 -0700562 if ($pattern_depth == 0 ||
563 (($file_pd - $value_pd) < $pattern_depth)) {
564 $hash{$tvi} = $value_pd;
565 }
566 }
567 }
568 }
569 }
570 }
571 $tvi = $end + 1;
572 }
573
574 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
575 add_categories($line);
576 if ($sections) {
577 my $i;
578 my $start = find_starting_index($line);
579 my $end = find_ending_index($line);
580 for ($i = $start; $i < $end; $i++) {
581 my $line = $typevalue[$i];
582 if ($line =~ /^[FX]:/) { ##Restore file patterns
583 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
584 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
585 $line =~ s/\\\./\./g; ##Convert \. to .
586 $line =~ s/\.\*/\*/g; ##Convert .* to *
587 }
588 $line =~ s/^([A-Z]):/$1:\t/g;
589 print("$line\n");
590 }
591 print("\n");
592 }
593 }
Joe Perches683c6f82010-10-26 14:22:55 -0700594 }
595
596 if ($keywords) {
597 @keyword_tvi = sort_and_uniq(@keyword_tvi);
598 foreach my $line (@keyword_tvi) {
599 add_categories($line);
600 }
601 }
602
Joe Perchesb9e23312010-10-26 14:22:58 -0700603 foreach my $email (@email_to, @list_to) {
604 $email->[0] = deduplicate_email($email->[0]);
605 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700606
607 foreach my $file (@files) {
608 if ($email &&
609 ($email_git || ($email_git_fallback &&
610 !$exact_pattern_match_hash{$file}))) {
611 vcs_file_signoffs($file);
612 }
613 if ($email && $email_git_blame) {
614 vcs_file_blame($file);
615 }
616 }
617
Joe Perches683c6f82010-10-26 14:22:55 -0700618 if ($email) {
619 foreach my $chief (@penguin_chief) {
620 if ($chief =~ m/^(.*):(.*)/) {
621 my $email_address;
622
623 $email_address = format_email($1, $2, $email_usename);
624 if ($email_git_penguin_chiefs) {
625 push(@email_to, [$email_address, 'chief penguin']);
626 } else {
627 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
628 }
629 }
630 }
631
632 foreach my $email (@file_emails) {
633 my ($name, $address) = parse_email($email);
634
635 my $tmp_email = format_email($name, $address, $email_usename);
636 push_email_address($tmp_email, '');
637 add_role($tmp_email, 'in file');
638 }
639 }
640
641 my @to = ();
642 if ($email || $email_list) {
643 if ($email) {
644 @to = (@to, @email_to);
645 }
646 if ($email_list) {
647 @to = (@to, @list_to);
648 }
649 }
650
Joe Perches6ef1c522010-10-26 14:22:56 -0700651 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700652 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700653 }
Joe Perches683c6f82010-10-26 14:22:55 -0700654
655 return @to;
656}
657
Joe Perchescb7301c2009-04-07 20:40:12 -0700658sub file_match_pattern {
659 my ($file, $pattern) = @_;
660 if (substr($pattern, -1) eq "/") {
661 if ($file =~ m@^$pattern@) {
662 return 1;
663 }
664 } else {
665 if ($file =~ m@^$pattern@) {
666 my $s1 = ($file =~ tr@/@@);
667 my $s2 = ($pattern =~ tr@/@@);
668 if ($s1 == $s2) {
669 return 1;
670 }
671 }
672 }
673 return 0;
674}
675
676sub usage {
677 print <<EOT;
678usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700679 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700680version: $V
681
682MAINTAINER field selection options:
683 --email => print email address(es) if any
684 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700685 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700686 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700687 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700688 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700689 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
690 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
691 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700692 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700693 --git-since => git history to use (default: $email_git_since)
694 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700695 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700696 --m => include maintainer(s) if any
697 --n => include name 'Full Name <addr\@domain.tld>'
698 --l => include list(s) if any
699 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700700 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800701 --roles => show roles (status:subsystem, git-signer, list, etc...)
702 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800703 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700704 --scm => print SCM tree(s) if any
705 --status => print status if any
706 --subsystem => print subsystem name if any
707 --web => print website(s) if any
708
709Output type options:
710 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700711 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700712 --multiline => print 1 entry per line
713
Joe Perchescb7301c2009-04-07 20:40:12 -0700714Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700715 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700716 --keywords => scan patch for keywords (default: $keywords)
717 --sections => print all of the subsystem sections with pattern matches
718 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700719 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700720 --help => show this help information
721
Joe Perches3fb55652009-09-21 17:04:17 -0700722Default options:
Joe Perches11ecf532009-09-21 17:04:22 -0700723 [--email --git --m --n --l --multiline --pattern-depth=0 --remove-duplicates]
Joe Perches3fb55652009-09-21 17:04:17 -0700724
Joe Perches870020f2009-07-29 15:04:28 -0700725Notes:
726 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700727 Used with "--git", git signators for _all_ files in and below
728 directory are examined as git recurses directories.
729 Any specified X: (exclude) pattern matches are _not_ ignored.
730 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800731 no individual file within the directory or subdirectory
732 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700733 Used with "--git-blame", does not iterate all files in directory
734 Using "--git-blame" is slow and may add old committers and authors
735 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800736 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
737 other automated tools that expect only ["name"] <email address>
738 may not work because of additional output after <email address>.
739 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
740 not the percentage of the entire file authored. # of commits is
741 not a good measure of amount of code authored. 1 major commit may
742 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800743 If git is not installed, but mercurial (hg) is installed and an .hg
744 repository exists, the following options apply to mercurial:
745 --git,
746 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
747 --git-blame
748 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700749 File ".get_maintainer.conf", if it exists in the linux kernel source root
750 directory, can change whatever get_maintainer defaults are desired.
751 Entries in this file can be any command line argument.
752 This file is prepended to any additional command line arguments.
753 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700754EOT
755}
756
757sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700758 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700759
Joe Perches47abc722010-10-26 14:22:57 -0700760 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
761 $lk_path .= "/";
762 }
763 if ( (-f "${lk_path}COPYING")
764 && (-f "${lk_path}CREDITS")
765 && (-f "${lk_path}Kbuild")
766 && (-f "${lk_path}MAINTAINERS")
767 && (-f "${lk_path}Makefile")
768 && (-f "${lk_path}README")
769 && (-d "${lk_path}Documentation")
770 && (-d "${lk_path}arch")
771 && (-d "${lk_path}include")
772 && (-d "${lk_path}drivers")
773 && (-d "${lk_path}fs")
774 && (-d "${lk_path}init")
775 && (-d "${lk_path}ipc")
776 && (-d "${lk_path}kernel")
777 && (-d "${lk_path}lib")
778 && (-d "${lk_path}scripts")) {
779 return 1;
780 }
781 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700782}
783
Joe Perches0e70e832009-09-21 17:04:20 -0700784sub parse_email {
785 my ($formatted_email) = @_;
786
787 my $name = "";
788 my $address = "";
789
Joe Perches11ecf532009-09-21 17:04:22 -0700790 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700791 $name = $1;
792 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700793 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700794 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700795 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700796 $address = $1;
797 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700798
799 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700800 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700801 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700802
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800803 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700804 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700805 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700806 }
Joe Perches0e70e832009-09-21 17:04:20 -0700807
808 return ($name, $address);
809}
810
811sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800812 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700813
814 my $formatted_email;
815
816 $name =~ s/^\s+|\s+$//g;
817 $name =~ s/^\"|\"$//g;
818 $address =~ s/^\s+|\s+$//g;
819
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800820 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700821 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
822 $name = "\"$name\"";
823 }
824
Joe Perchesa8af2432009-12-14 18:00:49 -0800825 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700826 if ("$name" eq "") {
827 $formatted_email = "$address";
828 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800829 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700830 }
831 } else {
832 $formatted_email = $address;
833 }
834
Joe Perchescb7301c2009-04-07 20:40:12 -0700835 return $formatted_email;
836}
837
Joe Perches272a8972010-01-08 14:42:48 -0800838sub find_first_section {
839 my $index = 0;
840
841 while ($index < @typevalue) {
842 my $tv = $typevalue[$index];
843 if (($tv =~ m/^(\C):\s*(.*)/)) {
844 last;
845 }
846 $index++;
847 }
848
849 return $index;
850}
851
Joe Perchesb7816552009-09-21 17:04:24 -0700852sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700853 my ($index) = @_;
854
855 while ($index > 0) {
856 my $tv = $typevalue[$index];
857 if (!($tv =~ m/^(\C):\s*(.*)/)) {
858 last;
859 }
860 $index--;
861 }
862
863 return $index;
864}
865
866sub find_ending_index {
867 my ($index) = @_;
868
869 while ($index < @typevalue) {
870 my $tv = $typevalue[$index];
871 if (!($tv =~ m/^(\C):\s*(.*)/)) {
872 last;
873 }
874 $index++;
875 }
876
877 return $index;
878}
879
Joe Perches3c7385b2009-12-14 18:00:46 -0800880sub get_maintainer_role {
881 my ($index) = @_;
882
883 my $i;
884 my $start = find_starting_index($index);
885 my $end = find_ending_index($index);
886
887 my $role;
888 my $subsystem = $typevalue[$start];
889 if (length($subsystem) > 20) {
890 $subsystem = substr($subsystem, 0, 17);
891 $subsystem =~ s/\s*$//;
892 $subsystem = $subsystem . "...";
893 }
894
895 for ($i = $start + 1; $i < $end; $i++) {
896 my $tv = $typevalue[$i];
897 if ($tv =~ m/^(\C):\s*(.*)/) {
898 my $ptype = $1;
899 my $pvalue = $2;
900 if ($ptype eq "S") {
901 $role = $pvalue;
902 }
903 }
904 }
905
906 $role = lc($role);
907 if ($role eq "supported") {
908 $role = "supporter";
909 } elsif ($role eq "maintained") {
910 $role = "maintainer";
911 } elsif ($role eq "odd fixes") {
912 $role = "odd fixer";
913 } elsif ($role eq "orphan") {
914 $role = "orphan minder";
915 } elsif ($role eq "obsolete") {
916 $role = "obsolete minder";
917 } elsif ($role eq "buried alive in reporters") {
918 $role = "chief penguin";
919 }
920
921 return $role . ":" . $subsystem;
922}
923
924sub get_list_role {
925 my ($index) = @_;
926
927 my $i;
928 my $start = find_starting_index($index);
929 my $end = find_ending_index($index);
930
931 my $subsystem = $typevalue[$start];
932 if (length($subsystem) > 20) {
933 $subsystem = substr($subsystem, 0, 17);
934 $subsystem =~ s/\s*$//;
935 $subsystem = $subsystem . "...";
936 }
937
938 if ($subsystem eq "THE REST") {
939 $subsystem = "";
940 }
941
942 return $subsystem;
943}
944
Joe Perchescb7301c2009-04-07 20:40:12 -0700945sub add_categories {
946 my ($index) = @_;
947
Joe Perchesb7816552009-09-21 17:04:24 -0700948 my $i;
949 my $start = find_starting_index($index);
950 my $end = find_ending_index($index);
951
952 push(@subsystem, $typevalue[$start]);
953
954 for ($i = $start + 1; $i < $end; $i++) {
955 my $tv = $typevalue[$i];
Joe Perches290603c2009-06-16 15:33:58 -0700956 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700957 my $ptype = $1;
958 my $pvalue = $2;
959 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -0700960 my $list_address = $pvalue;
961 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -0800962 my $list_role = get_list_role($i);
963
964 if ($list_role ne "") {
965 $list_role = ":" . $list_role;
966 }
Joe Perches290603c2009-06-16 15:33:58 -0700967 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
968 $list_address = $1;
969 $list_additional = $2;
970 }
Joe Perchesbdf7c682009-06-16 15:33:59 -0700971 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700972 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700973 if (!$hash_list_to{lc($list_address)}) {
974 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -0700975 push(@list_to, [$list_address,
976 "subscriber list${list_role}"]);
977 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700978 }
979 } else {
980 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700981 if (!$hash_list_to{lc($list_address)}) {
982 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -0700983 push(@list_to, [$list_address,
984 "open list${list_role}"]);
985 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700986 }
987 }
988 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -0700989 my ($name, $address) = parse_email($pvalue);
990 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -0700991 if ($i > 0) {
992 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -0700993 if ($tv =~ m/^(\C):\s*(.*)/) {
994 if ($1 eq "P") {
995 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -0800996 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -0700997 }
998 }
999 }
1000 }
Joe Perches0e70e832009-09-21 17:04:20 -07001001 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001002 my $role = get_maintainer_role($i);
1003 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001004 }
1005 } elsif ($ptype eq "T") {
1006 push(@scm, $pvalue);
1007 } elsif ($ptype eq "W") {
1008 push(@web, $pvalue);
1009 } elsif ($ptype eq "S") {
1010 push(@status, $pvalue);
1011 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001012 }
1013 }
1014}
1015
Joe Perches11ecf532009-09-21 17:04:22 -07001016sub email_inuse {
1017 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001018
Joe Perches11ecf532009-09-21 17:04:22 -07001019 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001020 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1021 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001022
Joe Perches0e70e832009-09-21 17:04:20 -07001023 return 0;
1024}
1025
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001026sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001027 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001028
Joe Perches0e70e832009-09-21 17:04:20 -07001029 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001030
Joe Perchesb7816552009-09-21 17:04:24 -07001031 if ($address eq "") {
1032 return 0;
1033 }
1034
Joe Perches11ecf532009-09-21 17:04:22 -07001035 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001036 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001037 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001038 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001039 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001040 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001041 }
Joe Perchesb7816552009-09-21 17:04:24 -07001042
1043 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001044}
1045
1046sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001047 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001048
1049 my @address_list = ();
1050
Joe Perches5f2441e2009-06-16 15:34:02 -07001051 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001052 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001053 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001054 my $array_count = shift(@address_list);
1055 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001056 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001057 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001058 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001059 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001060 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1061 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001062 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001063}
1064
Joe Perches3c7385b2009-12-14 18:00:46 -08001065sub add_role {
1066 my ($line, $role) = @_;
1067
1068 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001069 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001070
1071 foreach my $entry (@email_to) {
1072 if ($email_remove_duplicates) {
1073 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001074 if (($name eq $entry_name || $address eq $entry_address)
1075 && ($role eq "" || !($entry->[1] =~ m/$role/))
1076 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001077 if ($entry->[1] eq "") {
1078 $entry->[1] = "$role";
1079 } else {
1080 $entry->[1] = "$entry->[1],$role";
1081 }
1082 }
1083 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001084 if ($email eq $entry->[0]
1085 && ($role eq "" || !($entry->[1] =~ m/$role/))
1086 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001087 if ($entry->[1] eq "") {
1088 $entry->[1] = "$role";
1089 } else {
1090 $entry->[1] = "$entry->[1],$role";
1091 }
1092 }
1093 }
1094 }
1095}
1096
Joe Perchescb7301c2009-04-07 20:40:12 -07001097sub which {
1098 my ($bin) = @_;
1099
Joe Perchesf5f50782009-06-16 15:34:00 -07001100 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001101 if (-e "$path/$bin") {
1102 return "$path/$bin";
1103 }
1104 }
1105
1106 return "";
1107}
1108
Joe Perchesbcde44e2010-10-26 14:22:53 -07001109sub which_conf {
1110 my ($conf) = @_;
1111
1112 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1113 if (-e "$path/$conf") {
1114 return "$path/$conf";
1115 }
1116 }
1117
1118 return "";
1119}
1120
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001121sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001122 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001123
Joe Perches47abc722010-10-26 14:22:57 -07001124 my ($name, $address) = parse_email($line);
1125 my $email = format_email($name, $address, 1);
1126 my $real_name = $name;
1127 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001128
Joe Perches47abc722010-10-26 14:22:57 -07001129 if (exists $mailmap->{names}->{$email} ||
1130 exists $mailmap->{addresses}->{$email}) {
1131 if (exists $mailmap->{names}->{$email}) {
1132 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001133 }
Joe Perches47abc722010-10-26 14:22:57 -07001134 if (exists $mailmap->{addresses}->{$email}) {
1135 $real_address = $mailmap->{addresses}->{$email};
1136 }
1137 } else {
1138 if (exists $mailmap->{names}->{$address}) {
1139 $real_name = $mailmap->{names}->{$address};
1140 }
1141 if (exists $mailmap->{addresses}->{$address}) {
1142 $real_address = $mailmap->{addresses}->{$address};
1143 }
1144 }
1145 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001146}
1147
1148sub mailmap {
1149 my (@addresses) = @_;
1150
Joe Perchesb9e23312010-10-26 14:22:58 -07001151 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001152 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001153 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001154 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001155 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1156 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001157}
1158
1159sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001160 my %address_map;
1161 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001162
Joe Perches47abc722010-10-26 14:22:57 -07001163 foreach my $email (@emails) {
1164 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001165 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001166 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001167 $email = format_email($name, $address, 1);
1168 } else {
1169 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001170 }
Joe Perches47abc722010-10-26 14:22:57 -07001171 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001172}
1173
Joe Perches60db31a2009-12-14 18:00:50 -08001174sub git_execute_cmd {
1175 my ($cmd) = @_;
1176 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001177
Joe Perches60db31a2009-12-14 18:00:50 -08001178 my $output = `$cmd`;
1179 $output =~ s/^\s*//gm;
1180 @lines = split("\n", $output);
1181
1182 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001183}
1184
Joe Perches60db31a2009-12-14 18:00:50 -08001185sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001186 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001187 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001188
Joe Perches60db31a2009-12-14 18:00:50 -08001189 my $output = `$cmd`;
1190 @lines = split("\n", $output);
1191
1192 return @lines;
1193}
1194
Joe Perches683c6f82010-10-26 14:22:55 -07001195sub extract_formatted_signatures {
1196 my (@signature_lines) = @_;
1197
1198 my @type = @signature_lines;
1199
1200 s/\s*(.*):.*/$1/ for (@type);
1201
1202 # cut -f2- -d":"
1203 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1204
1205## Reformat email addresses (with names) to avoid badly written signatures
1206
1207 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001208 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001209 }
1210
1211 return (\@type, \@signature_lines);
1212}
1213
Joe Perches60db31a2009-12-14 18:00:50 -08001214sub vcs_find_signers {
1215 my ($cmd) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001216 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001217 my @lines = ();
1218 my @signatures = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001219
Joe Perches60db31a2009-12-14 18:00:50 -08001220 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001221
Joe Perches60db31a2009-12-14 18:00:50 -08001222 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchescb7301c2009-04-07 20:40:12 -07001223
Joe Perches60db31a2009-12-14 18:00:50 -08001224 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001225
Joe Perches683c6f82010-10-26 14:22:55 -07001226 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
1227
1228 return (0, @signatures) if !@signatures;
1229
1230 save_commits_by_author(@lines) if ($interactive);
1231 save_commits_by_signer(@lines) if ($interactive);
1232
Joe Perches0e70e832009-09-21 17:04:20 -07001233 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001234 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001235 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001236
Joe Perches683c6f82010-10-26 14:22:55 -07001237 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001238
Joe Perches683c6f82010-10-26 14:22:55 -07001239 return ($commits, @$signers_ref);
Joe Perchesa8af2432009-12-14 18:00:49 -08001240}
1241
Joe Perches63ab52d2010-10-26 14:22:51 -07001242sub vcs_find_author {
1243 my ($cmd) = @_;
1244 my @lines = ();
1245
1246 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1247
1248 if (!$email_git_penguin_chiefs) {
1249 @lines = grep(!/${penguin_chiefs}/i, @lines);
1250 }
1251
1252 return @lines if !@lines;
1253
Joe Perches683c6f82010-10-26 14:22:55 -07001254 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001255 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001256 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1257 my $author = $1;
1258 my ($name, $address) = parse_email($author);
1259 $author = format_email($name, $address, 1);
1260 push(@authors, $author);
1261 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001262 }
1263
Joe Perches683c6f82010-10-26 14:22:55 -07001264 save_commits_by_author(@lines) if ($interactive);
1265 save_commits_by_signer(@lines) if ($interactive);
1266
1267 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001268}
1269
Joe Perches60db31a2009-12-14 18:00:50 -08001270sub vcs_save_commits {
1271 my ($cmd) = @_;
1272 my @lines = ();
1273 my @commits = ();
1274
1275 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1276
1277 foreach my $line (@lines) {
1278 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1279 push(@commits, $1);
1280 }
1281 }
1282
1283 return @commits;
1284}
1285
1286sub vcs_blame {
1287 my ($file) = @_;
1288 my $cmd;
1289 my @commits = ();
1290
1291 return @commits if (!(-f $file));
1292
1293 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1294 my @all_commits = ();
1295
1296 $cmd = $VCS_cmds{"blame_file_cmd"};
1297 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1298 @all_commits = vcs_save_commits($cmd);
1299
1300 foreach my $file_range_diff (@range) {
1301 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1302 my $diff_file = $1;
1303 my $diff_start = $2;
1304 my $diff_length = $3;
1305 next if ("$file" ne "$diff_file");
1306 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1307 push(@commits, $all_commits[$i]);
1308 }
1309 }
1310 } elsif (@range) {
1311 foreach my $file_range_diff (@range) {
1312 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1313 my $diff_file = $1;
1314 my $diff_start = $2;
1315 my $diff_length = $3;
1316 next if ("$file" ne "$diff_file");
1317 $cmd = $VCS_cmds{"blame_range_cmd"};
1318 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1319 push(@commits, vcs_save_commits($cmd));
1320 }
1321 } else {
1322 $cmd = $VCS_cmds{"blame_file_cmd"};
1323 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1324 @commits = vcs_save_commits($cmd);
1325 }
1326
Joe Perches63ab52d2010-10-26 14:22:51 -07001327 foreach my $commit (@commits) {
1328 $commit =~ s/^\^//g;
1329 }
1330
Joe Perches60db31a2009-12-14 18:00:50 -08001331 return @commits;
1332}
1333
1334my $printed_novcs = 0;
1335sub vcs_exists {
1336 %VCS_cmds = %VCS_cmds_git;
1337 return 1 if eval $VCS_cmds{"available"};
1338 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001339 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001340 %VCS_cmds = ();
1341 if (!$printed_novcs) {
1342 warn("$P: No supported VCS found. Add --nogit to options?\n");
1343 warn("Using a git repository produces better results.\n");
1344 warn("Try Linus Torvalds' latest git repository using:\n");
1345 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git\n");
1346 $printed_novcs = 1;
1347 }
1348 return 0;
1349}
1350
Joe Perches683c6f82010-10-26 14:22:55 -07001351sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001352 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001353 return $vcs_used == 1;
1354}
1355
1356sub vcs_is_hg {
1357 return $vcs_used == 2;
1358}
1359
Joe Perches6ef1c522010-10-26 14:22:56 -07001360sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001361 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001362 my @list = @$list_ref;
1363
Joe Perches683c6f82010-10-26 14:22:55 -07001364 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001365
1366 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001367 my %authored;
1368 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001369 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001370 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001371 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001372 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1373 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001374 $authored{$count} = 0;
1375 $signed{$count} = 0;
1376 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001377 }
1378
1379 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001380 my $done = 0;
1381 my $print_options = 0;
1382 my $redraw = 1;
1383 while (!$done) {
1384 $count = 0;
1385 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001386 printf STDERR "\n%1s %2s %-65s",
1387 "*", "#", "email/list and role:stats";
1388 if ($email_git ||
1389 ($email_git_fallback && !$maintained) ||
1390 $email_git_blame) {
1391 print STDERR "auth sign";
1392 }
1393 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001394 foreach my $entry (@list) {
1395 my $email = $entry->[0];
1396 my $role = $entry->[1];
1397 my $sel = "";
1398 $sel = "*" if ($selected{$count});
1399 my $commit_author = $commit_author_hash{$email};
1400 my $commit_signer = $commit_signer_hash{$email};
1401 my $authored = 0;
1402 my $signed = 0;
1403 $authored++ for (@{$commit_author});
1404 $signed++ for (@{$commit_signer});
1405 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1406 printf STDERR "%4d %4d", $authored, $signed
1407 if ($authored > 0 || $signed > 0);
1408 printf STDERR "\n %s\n", $role;
1409 if ($authored{$count}) {
1410 my $commit_author = $commit_author_hash{$email};
1411 foreach my $ref (@{$commit_author}) {
1412 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001413 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001414 }
Joe Perches683c6f82010-10-26 14:22:55 -07001415 if ($signed{$count}) {
1416 my $commit_signer = $commit_signer_hash{$email};
1417 foreach my $ref (@{$commit_signer}) {
1418 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1419 }
1420 }
1421
1422 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001423 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001424 }
Joe Perches683c6f82010-10-26 14:22:55 -07001425 my $date_ref = \$email_git_since;
1426 $date_ref = \$email_hg_since if (vcs_is_hg());
1427 if ($print_options) {
1428 $print_options = 0;
1429 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001430 print STDERR <<EOT
1431
1432Version Control options:
1433g use git history [$email_git]
1434gf use git-fallback [$email_git_fallback]
1435b use git blame [$email_git_blame]
1436bs use blame signatures [$email_git_blame_signatures]
1437c# minimum commits [$email_git_min_signatures]
1438%# min percent [$email_git_min_percent]
1439d# history to use [$$date_ref]
1440x# max maintainers [$email_git_max_maintainers]
1441t all signature types [$email_git_all_signature_types]
1442m use .mailmap [$email_use_mailmap]
1443EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001444 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001445 print STDERR <<EOT
1446
1447Additional options:
14480 toggle all
1449tm toggle maintainers
1450tg toggle git entries
1451tl toggle open list entries
1452ts toggle subscriber list entries
1453f emails in file [$file_emails]
1454k keywords in file [$keywords]
1455r remove duplicates [$email_remove_duplicates]
1456p# pattern match depth [$pattern_depth]
1457EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001458 }
1459 print STDERR
1460"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1461
1462 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001463 chomp($input);
1464
Joe Perches683c6f82010-10-26 14:22:55 -07001465 $redraw = 1;
1466 my $rerun = 0;
1467 my @wish = split(/[, ]+/, $input);
1468 foreach my $nr (@wish) {
1469 $nr = lc($nr);
1470 my $sel = substr($nr, 0, 1);
1471 my $str = substr($nr, 1);
1472 my $val = 0;
1473 $val = $1 if $str =~ /^(\d+)$/;
1474
1475 if ($sel eq "y") {
1476 $interactive = 0;
1477 $done = 1;
1478 $output_rolestats = 0;
1479 $output_roles = 0;
1480 last;
1481 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1482 $selected{$nr - 1} = !$selected{$nr - 1};
1483 } elsif ($sel eq "*" || $sel eq '^') {
1484 my $toggle = 0;
1485 $toggle = 1 if ($sel eq '*');
1486 for (my $i = 0; $i < $count; $i++) {
1487 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001488 }
Joe Perches683c6f82010-10-26 14:22:55 -07001489 } elsif ($sel eq "0") {
1490 for (my $i = 0; $i < $count; $i++) {
1491 $selected{$i} = !$selected{$i};
1492 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001493 } elsif ($sel eq "t") {
1494 if (lc($str) eq "m") {
1495 for (my $i = 0; $i < $count; $i++) {
1496 $selected{$i} = !$selected{$i}
1497 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1498 }
1499 } elsif (lc($str) eq "g") {
1500 for (my $i = 0; $i < $count; $i++) {
1501 $selected{$i} = !$selected{$i}
1502 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1503 }
1504 } elsif (lc($str) eq "l") {
1505 for (my $i = 0; $i < $count; $i++) {
1506 $selected{$i} = !$selected{$i}
1507 if ($list[$i]->[1] =~ /^(open list)/i);
1508 }
1509 } elsif (lc($str) eq "s") {
1510 for (my $i = 0; $i < $count; $i++) {
1511 $selected{$i} = !$selected{$i}
1512 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1513 }
1514 }
Joe Perches683c6f82010-10-26 14:22:55 -07001515 } elsif ($sel eq "a") {
1516 if ($val > 0 && $val <= $count) {
1517 $authored{$val - 1} = !$authored{$val - 1};
1518 } elsif ($str eq '*' || $str eq '^') {
1519 my $toggle = 0;
1520 $toggle = 1 if ($str eq '*');
1521 for (my $i = 0; $i < $count; $i++) {
1522 $authored{$i} = $toggle;
1523 }
1524 }
1525 } elsif ($sel eq "s") {
1526 if ($val > 0 && $val <= $count) {
1527 $signed{$val - 1} = !$signed{$val - 1};
1528 } elsif ($str eq '*' || $str eq '^') {
1529 my $toggle = 0;
1530 $toggle = 1 if ($str eq '*');
1531 for (my $i = 0; $i < $count; $i++) {
1532 $signed{$i} = $toggle;
1533 }
1534 }
1535 } elsif ($sel eq "o") {
1536 $print_options = 1;
1537 $redraw = 1;
1538 } elsif ($sel eq "g") {
1539 if ($str eq "f") {
1540 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001541 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001542 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001543 }
Joe Perches683c6f82010-10-26 14:22:55 -07001544 $rerun = 1;
1545 } elsif ($sel eq "b") {
1546 if ($str eq "s") {
1547 bool_invert(\$email_git_blame_signatures);
1548 } else {
1549 bool_invert(\$email_git_blame);
1550 }
1551 $rerun = 1;
1552 } elsif ($sel eq "c") {
1553 if ($val > 0) {
1554 $email_git_min_signatures = $val;
1555 $rerun = 1;
1556 }
1557 } elsif ($sel eq "x") {
1558 if ($val > 0) {
1559 $email_git_max_maintainers = $val;
1560 $rerun = 1;
1561 }
1562 } elsif ($sel eq "%") {
1563 if ($str ne "" && $val >= 0) {
1564 $email_git_min_percent = $val;
1565 $rerun = 1;
1566 }
1567 } elsif ($sel eq "d") {
1568 if (vcs_is_git()) {
1569 $email_git_since = $str;
1570 } elsif (vcs_is_hg()) {
1571 $email_hg_since = $str;
1572 }
1573 $rerun = 1;
1574 } elsif ($sel eq "t") {
1575 bool_invert(\$email_git_all_signature_types);
1576 $rerun = 1;
1577 } elsif ($sel eq "f") {
1578 bool_invert(\$file_emails);
1579 $rerun = 1;
1580 } elsif ($sel eq "r") {
1581 bool_invert(\$email_remove_duplicates);
1582 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001583 } elsif ($sel eq "m") {
1584 bool_invert(\$email_use_mailmap);
1585 read_mailmap();
1586 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001587 } elsif ($sel eq "k") {
1588 bool_invert(\$keywords);
1589 $rerun = 1;
1590 } elsif ($sel eq "p") {
1591 if ($str ne "" && $val >= 0) {
1592 $pattern_depth = $val;
1593 $rerun = 1;
1594 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001595 } elsif ($sel eq "h" || $sel eq "?") {
1596 print STDERR <<EOT
1597
1598Interactive mode allows you to select the various maintainers, submitters,
1599commit signers and mailing lists that could be CC'd on a patch.
1600
1601Any *'d entry is selected.
1602
Joe Perches47abc722010-10-26 14:22:57 -07001603If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001604history of files in the patch. Also, each line of the current file can
1605be matched to its commit author and that commits signers with blame.
1606
1607Various knobs exist to control the length of time for active commit
1608tracking, the maximum number of commit authors and signers to add,
1609and such.
1610
1611Enter selections at the prompt until you are satisfied that the selected
1612maintainers are appropriate. You may enter multiple selections separated
1613by either commas or spaces.
1614
1615EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001616 } else {
1617 print STDERR "invalid option: '$nr'\n";
1618 $redraw = 0;
1619 }
1620 }
1621 if ($rerun) {
1622 print STDERR "git-blame can be very slow, please have patience..."
1623 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001624 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001625 }
1626 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001627
1628 #drop not selected entries
1629 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001630 my @new_emailto = ();
1631 foreach my $entry (@list) {
1632 if ($selected{$count}) {
1633 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001634 }
1635 $count++;
1636 }
Joe Perches683c6f82010-10-26 14:22:55 -07001637 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001638}
1639
Joe Perches683c6f82010-10-26 14:22:55 -07001640sub bool_invert {
1641 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001642
Joe Perches683c6f82010-10-26 14:22:55 -07001643 if ($$bool_ref) {
1644 $$bool_ref = 0;
1645 } else {
1646 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001647 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001648}
1649
Joe Perchesb9e23312010-10-26 14:22:58 -07001650sub deduplicate_email {
1651 my ($email) = @_;
1652
1653 my $matched = 0;
1654 my ($name, $address) = parse_email($email);
1655 $email = format_email($name, $address, 1);
1656 $email = mailmap_email($email);
1657
1658 return $email if (!$email_remove_duplicates);
1659
1660 ($name, $address) = parse_email($email);
1661
Joe Perchesfae99202010-10-26 14:22:58 -07001662 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001663 $name = $deduplicate_name_hash{lc($name)}->[0];
1664 $address = $deduplicate_name_hash{lc($name)}->[1];
1665 $matched = 1;
1666 } elsif ($deduplicate_address_hash{lc($address)}) {
1667 $name = $deduplicate_address_hash{lc($address)}->[0];
1668 $address = $deduplicate_address_hash{lc($address)}->[1];
1669 $matched = 1;
1670 }
1671 if (!$matched) {
1672 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1673 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1674 }
1675 $email = format_email($name, $address, 1);
1676 $email = mailmap_email($email);
1677 return $email;
1678}
1679
Joe Perches683c6f82010-10-26 14:22:55 -07001680sub save_commits_by_author {
1681 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001682
Joe Perches683c6f82010-10-26 14:22:55 -07001683 my @authors = ();
1684 my @commits = ();
1685 my @subjects = ();
1686
1687 foreach my $line (@lines) {
1688 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1689 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001690 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001691 push(@authors, $author);
1692 }
1693 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1694 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1695 }
1696
1697 for (my $i = 0; $i < @authors; $i++) {
1698 my $exists = 0;
1699 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1700 if (@{$ref}[0] eq $commits[$i] &&
1701 @{$ref}[1] eq $subjects[$i]) {
1702 $exists = 1;
1703 last;
1704 }
1705 }
1706 if (!$exists) {
1707 push(@{$commit_author_hash{$authors[$i]}},
1708 [ ($commits[$i], $subjects[$i]) ]);
1709 }
1710 }
1711}
1712
1713sub save_commits_by_signer {
1714 my (@lines) = @_;
1715
1716 my $commit = "";
1717 my $subject = "";
1718
1719 foreach my $line (@lines) {
1720 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1721 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1722 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1723 my @signatures = ($line);
1724 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1725 my @types = @$types_ref;
1726 my @signers = @$signers_ref;
1727
1728 my $type = $types[0];
1729 my $signer = $signers[0];
1730
Joe Perchesb9e23312010-10-26 14:22:58 -07001731 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001732
Joe Perches683c6f82010-10-26 14:22:55 -07001733 my $exists = 0;
1734 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1735 if (@{$ref}[0] eq $commit &&
1736 @{$ref}[1] eq $subject &&
1737 @{$ref}[2] eq $type) {
1738 $exists = 1;
1739 last;
1740 }
1741 }
1742 if (!$exists) {
1743 push(@{$commit_signer_hash{$signer}},
1744 [ ($commit, $subject, $type) ]);
1745 }
1746 }
1747 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001748}
1749
Joe Perches60db31a2009-12-14 18:00:50 -08001750sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001751 my ($role, $divisor, @lines) = @_;
1752
1753 my %hash;
1754 my $count = 0;
1755
Joe Perchesa8af2432009-12-14 18:00:49 -08001756 return if (@lines <= 0);
1757
1758 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001759 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001760 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001761 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001762
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001763 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001764
Joe Perches63ab52d2010-10-26 14:22:51 -07001765 return if (@lines <= 0);
1766
Joe Perches0e70e832009-09-21 17:04:20 -07001767 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001768
Joe Perches11ecf532009-09-21 17:04:22 -07001769 # uniq -c
1770 $hash{$_}++ for @lines;
1771
1772 # sort -rn
1773 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1774 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001775 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001776
Joe Perchesa8af2432009-12-14 18:00:49 -08001777 $percent = 100 if ($percent > 100);
Joe Perches11ecf532009-09-21 17:04:22 -07001778 $count++;
1779 last if ($sign_offs < $email_git_min_signatures ||
1780 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001781 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001782 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001783 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001784 my $fmt_percent = sprintf("%.0f", $percent);
1785 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1786 } else {
1787 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001788 }
Joe Perchesf5492662009-09-21 17:04:13 -07001789 }
1790}
1791
Joe Perches60db31a2009-12-14 18:00:50 -08001792sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001793 my ($file) = @_;
1794
1795 my @signers = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001796 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001797
Joe Perches683c6f82010-10-26 14:22:55 -07001798 $vcs_used = vcs_exists();
1799 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001800
Joe Perches60db31a2009-12-14 18:00:50 -08001801 my $cmd = $VCS_cmds{"find_signers_cmd"};
1802 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1803
1804 ($commits, @signers) = vcs_find_signers($cmd);
Joe Perchesb9e23312010-10-26 14:22:58 -07001805
1806 foreach my $signer (@signers) {
1807 $signer = deduplicate_email($signer);
1808 }
1809
Joe Perches60db31a2009-12-14 18:00:50 -08001810 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001811}
1812
Joe Perches60db31a2009-12-14 18:00:50 -08001813sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001814 my ($file) = @_;
1815
Joe Perches60db31a2009-12-14 18:00:50 -08001816 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001817 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001818 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001819 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001820 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001821
Joe Perches683c6f82010-10-26 14:22:55 -07001822 $vcs_used = vcs_exists();
1823 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07001824
Joe Perches63ab52d2010-10-26 14:22:51 -07001825 @all_commits = vcs_blame($file);
1826 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08001827 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001828 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001829
Joe Perches683c6f82010-10-26 14:22:55 -07001830 if ($email_git_blame_signatures) {
1831 if (vcs_is_hg()) {
1832 my $commit_count;
1833 my @commit_signers = ();
1834 my $commit = join(" -r ", @commits);
1835 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07001836
Joe Perches683c6f82010-10-26 14:22:55 -07001837 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1838 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08001839
Joe Perches683c6f82010-10-26 14:22:55 -07001840 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
Joe Perches63ab52d2010-10-26 14:22:51 -07001841
Joe Perches683c6f82010-10-26 14:22:55 -07001842 push(@signers, @commit_signers);
1843 } else {
1844 foreach my $commit (@commits) {
1845 my $commit_count;
1846 my @commit_signers = ();
1847 my $cmd;
1848
1849 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1850 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1851
1852 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
1853
1854 push(@signers, @commit_signers);
1855 }
1856 }
Joe Perchesf5492662009-09-21 17:04:13 -07001857 }
1858
Joe Perchesa8af2432009-12-14 18:00:49 -08001859 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07001860 if ($output_rolestats) {
1861 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07001862 if (vcs_is_hg()) {{ # Double brace for last exit
1863 my $commit_count;
1864 my @commit_signers = ();
1865 @commits = uniq(@commits);
1866 @commits = sort(@commits);
1867 my $commit = join(" -r ", @commits);
1868 my $cmd;
1869
1870 $cmd = $VCS_cmds{"find_commit_author_cmd"};
1871 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1872
1873 my @lines = ();
1874
1875 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1876
1877 if (!$email_git_penguin_chiefs) {
1878 @lines = grep(!/${penguin_chiefs}/i, @lines);
1879 }
1880
1881 last if !@lines;
1882
1883 my @authors = ();
1884 foreach my $line (@lines) {
1885 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1886 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001887 $author = deduplicate_email($author);
1888 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07001889 }
1890 }
1891
1892 save_commits_by_author(@lines) if ($interactive);
1893 save_commits_by_signer(@lines) if ($interactive);
1894
1895 push(@signers, @authors);
1896 }}
1897 else {
1898 foreach my $commit (@commits) {
1899 my $i;
1900 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
1901 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1902 my @author = vcs_find_author($cmd);
1903 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07001904
1905 my $formatted_author = deduplicate_email($author[0]);
1906
Joe Perches683c6f82010-10-26 14:22:55 -07001907 my $count = grep(/$commit/, @all_commits);
1908 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001909 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07001910 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001911 }
1912 }
1913 if (@blame_signers) {
1914 vcs_assign("authored lines", $total_lines, @blame_signers);
1915 }
1916 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001917 foreach my $signer (@signers) {
1918 $signer = deduplicate_email($signer);
1919 }
Joe Perches60db31a2009-12-14 18:00:50 -08001920 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001921 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07001922 foreach my $signer (@signers) {
1923 $signer = deduplicate_email($signer);
1924 }
Joe Perches60db31a2009-12-14 18:00:50 -08001925 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07001926 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001927}
1928
1929sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001930 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001931
1932 my %saw;
1933 @parms = grep(!$saw{$_}++, @parms);
1934 return @parms;
1935}
1936
1937sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001938 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001939
1940 my %saw;
1941 @parms = sort @parms;
1942 @parms = grep(!$saw{$_}++, @parms);
1943 return @parms;
1944}
1945
Joe Perches03372db2010-03-05 13:43:00 -08001946sub clean_file_emails {
1947 my (@file_emails) = @_;
1948 my @fmt_emails = ();
1949
1950 foreach my $email (@file_emails) {
1951 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
1952 my ($name, $address) = parse_email($email);
1953 if ($name eq '"[,\.]"') {
1954 $name = "";
1955 }
1956
1957 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
1958 if (@nw > 2) {
1959 my $first = $nw[@nw - 3];
1960 my $middle = $nw[@nw - 2];
1961 my $last = $nw[@nw - 1];
1962
1963 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
1964 (length($first) == 2 && substr($first, -1) eq ".")) ||
1965 (length($middle) == 1 ||
1966 (length($middle) == 2 && substr($middle, -1) eq "."))) {
1967 $name = "$first $middle $last";
1968 } else {
1969 $name = "$middle $last";
1970 }
1971 }
1972
1973 if (substr($name, -1) =~ /[,\.]/) {
1974 $name = substr($name, 0, length($name) - 1);
1975 } elsif (substr($name, -2) =~ /[,\.]"/) {
1976 $name = substr($name, 0, length($name) - 2) . '"';
1977 }
1978
1979 if (substr($name, 0, 1) =~ /[,\.]/) {
1980 $name = substr($name, 1, length($name) - 1);
1981 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
1982 $name = '"' . substr($name, 2, length($name) - 2);
1983 }
1984
1985 my $fmt_email = format_email($name, $address, $email_usename);
1986 push(@fmt_emails, $fmt_email);
1987 }
1988 return @fmt_emails;
1989}
1990
Joe Perches3c7385b2009-12-14 18:00:46 -08001991sub merge_email {
1992 my @lines;
1993 my %saw;
1994
1995 for (@_) {
1996 my ($address, $role) = @$_;
1997 if (!$saw{$address}) {
1998 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08001999 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002000 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002001 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002002 }
2003 $saw{$address} = 1;
2004 }
2005 }
2006
2007 return @lines;
2008}
2009
Joe Perchescb7301c2009-04-07 20:40:12 -07002010sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002011 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002012
2013 if ($output_multiline) {
2014 foreach my $line (@parms) {
2015 print("${line}\n");
2016 }
2017 } else {
2018 print(join($output_separator, @parms));
2019 print("\n");
2020 }
2021}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002022
2023my $rfc822re;
2024
2025sub make_rfc822re {
2026# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2027# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2028# This regexp will only work on addresses which have had comments stripped
2029# and replaced with rfc822_lwsp.
2030
2031 my $specials = '()<>@,;:\\\\".\\[\\]';
2032 my $controls = '\\000-\\037\\177';
2033
2034 my $dtext = "[^\\[\\]\\r\\\\]";
2035 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2036
2037 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2038
2039# Use zero-width assertion to spot the limit of an atom. A simple
2040# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2041 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2042 my $word = "(?:$atom|$quoted_string)";
2043 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2044
2045 my $sub_domain = "(?:$atom|$domain_literal)";
2046 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2047
2048 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2049
2050 my $phrase = "$word*";
2051 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2052 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2053 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2054
2055 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2056 my $address = "(?:$mailbox|$group)";
2057
2058 return "$rfc822_lwsp*$address";
2059}
2060
2061sub rfc822_strip_comments {
2062 my $s = shift;
2063# Recursively remove comments, and replace with a single space. The simpler
2064# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2065# chars in atoms, for example.
2066
2067 while ($s =~ s/^((?:[^"\\]|\\.)*
2068 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2069 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2070 return $s;
2071}
2072
2073# valid: returns true if the parameter is an RFC822 valid address
2074#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002075sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002076 my $s = rfc822_strip_comments(shift);
2077
2078 if (!$rfc822re) {
2079 $rfc822re = make_rfc822re();
2080 }
2081
2082 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2083}
2084
2085# validlist: In scalar context, returns true if the parameter is an RFC822
2086# valid list of addresses.
2087#
2088# In list context, returns an empty list on failure (an invalid
2089# address was found); otherwise a list whose first element is the
2090# number of addresses found and whose remaining elements are the
2091# addresses. This is needed to disambiguate failure (invalid)
2092# from success with no addresses found, because an empty string is
2093# a valid list.
2094
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002095sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002096 my $s = rfc822_strip_comments(shift);
2097
2098 if (!$rfc822re) {
2099 $rfc822re = make_rfc822re();
2100 }
2101 # * null list items are valid according to the RFC
2102 # * the '1' business is to aid in distinguishing failure from no results
2103
2104 my @r;
2105 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2106 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002107 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002108 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002109 }
2110 return wantarray ? (scalar(@r), @r) : 1;
2111 }
Joe Perches60db31a2009-12-14 18:00:50 -08002112 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002113}