blob: 8b673dd4627fb20f4ab16a8af6de9524bd84a426 [file] [log] [blame]
Joe Perchescb7301c2009-04-07 20:40:12 -07001#!/usr/bin/perl -w
2# (c) 2007, Joe Perches <joe@perches.com>
3# created from checkpatch.pl
4#
5# Print selected MAINTAINERS information for
6# the files modified in a patch or for a file
7#
Roel Kluin3bd7bf52009-11-11 14:26:13 -08008# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
9# perl scripts/get_maintainer.pl [OPTIONS] -f <file>
Joe Perchescb7301c2009-04-07 20:40:12 -070010#
11# Licensed under the terms of the GNU GPL License version 2
12
13use strict;
14
15my $P = $0;
Joe Perches7e1863a2011-01-12 16:59:49 -080016my $V = '0.26';
Joe Perchescb7301c2009-04-07 20:40:12 -070017
18use Getopt::Long qw(:config no_auto_abbrev);
19
20my $lk_path = "./";
21my $email = 1;
22my $email_usename = 1;
23my $email_maintainer = 1;
24my $email_list = 1;
25my $email_subscriber_list = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070026my $email_git_penguin_chiefs = 0;
Joe Perchese3e9d112010-10-26 14:22:53 -070027my $email_git = 0;
Florian Mickler0fa05592010-05-24 14:33:20 -070028my $email_git_all_signature_types = 0;
Joe Perches60db31a2009-12-14 18:00:50 -080029my $email_git_blame = 0;
Joe Perches683c6f82010-10-26 14:22:55 -070030my $email_git_blame_signatures = 1;
Joe Perchese3e9d112010-10-26 14:22:53 -070031my $email_git_fallback = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070032my $email_git_min_signatures = 1;
33my $email_git_max_maintainers = 5;
Joe Perchesafa81ee2009-07-29 15:04:28 -070034my $email_git_min_percent = 5;
Joe Perchescb7301c2009-04-07 20:40:12 -070035my $email_git_since = "1-year-ago";
Joe Perches60db31a2009-12-14 18:00:50 -080036my $email_hg_since = "-365";
Florian Micklerdace8e32010-10-26 14:22:54 -070037my $interactive = 0;
Joe Perches11ecf532009-09-21 17:04:22 -070038my $email_remove_duplicates = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -070039my $email_use_mailmap = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070040my $output_multiline = 1;
41my $output_separator = ", ";
Joe Perches3c7385b2009-12-14 18:00:46 -080042my $output_roles = 0;
Joe Perches7e1863a2011-01-12 16:59:49 -080043my $output_rolestats = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070044my $scm = 0;
45my $web = 0;
46my $subsystem = 0;
47my $status = 0;
Joe Perchesdcf36a92009-10-26 16:49:47 -070048my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080049my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080050my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070051my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070052my $pattern_depth = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070053my $version = 0;
54my $help = 0;
55
Joe Perches683c6f82010-10-26 14:22:55 -070056my $vcs_used = 0;
57
Joe Perchescb7301c2009-04-07 20:40:12 -070058my $exit = 0;
59
Joe Perches683c6f82010-10-26 14:22:55 -070060my %commit_author_hash;
61my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070062
Joe Perchescb7301c2009-04-07 20:40:12 -070063my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070064push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070065#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070066#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070067
68my @penguin_chief_names = ();
69foreach my $chief (@penguin_chief) {
70 if ($chief =~ m/^(.*):(.*)/) {
71 my $chief_name = $1;
72 my $chief_addr = $2;
73 push(@penguin_chief_names, $chief_name);
74 }
75}
Joe Perchese4d26b02010-05-24 14:33:17 -070076my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
77
78# Signature types of people who are either
79# a) responsible for the code in question, or
80# b) familiar enough with it to give relevant feedback
81my @signature_tags = ();
82push(@signature_tags, "Signed-off-by:");
83push(@signature_tags, "Reviewed-by:");
84push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070085
Joe Perches7dea2682012-06-20 12:53:02 -070086my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
87
Joe Perches5f2441e2009-06-16 15:34:02 -070088# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070089my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070090my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070091
Joe Perches60db31a2009-12-14 18:00:50 -080092# VCS command support: class-like functions and strings
93
94my %VCS_cmds;
95
96my %VCS_cmds_git = (
97 "execute_cmd" => \&git_execute_cmd,
98 "available" => '(which("git") ne "") && (-d ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -070099 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800100 "git log --no-color --follow --since=\$email_git_since " .
Joe Perches683c6f82010-10-26 14:22:55 -0700101 '--format="GitCommit: %H%n' .
102 'GitAuthor: %an <%ae>%n' .
103 'GitDate: %aD%n' .
104 'GitSubject: %s%n' .
105 '%b%n"' .
106 " -- \$file",
107 "find_commit_signers_cmd" =>
108 "git log --no-color " .
109 '--format="GitCommit: %H%n' .
110 'GitAuthor: %an <%ae>%n' .
111 'GitDate: %aD%n' .
112 'GitSubject: %s%n' .
113 '%b%n"' .
114 " -1 \$commit",
115 "find_commit_author_cmd" =>
116 "git log --no-color " .
117 '--format="GitCommit: %H%n' .
118 'GitAuthor: %an <%ae>%n' .
119 'GitDate: %aD%n' .
120 'GitSubject: %s%n"' .
121 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800122 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
123 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700124 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700125 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700126 "author_pattern" => "^GitAuthor: (.*)",
127 "subject_pattern" => "^GitSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800128);
129
130my %VCS_cmds_hg = (
131 "execute_cmd" => \&hg_execute_cmd,
132 "available" => '(which("hg") ne "") && (-d ".hg")',
133 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700134 "hg log --date=\$email_hg_since " .
135 "--template='HgCommit: {node}\\n" .
136 "HgAuthor: {author}\\n" .
137 "HgSubject: {desc}\\n'" .
138 " -- \$file",
139 "find_commit_signers_cmd" =>
140 "hg log " .
141 "--template='HgSubject: {desc}\\n'" .
142 " -r \$commit",
143 "find_commit_author_cmd" =>
144 "hg log " .
145 "--template='HgCommit: {node}\\n" .
146 "HgAuthor: {author}\\n" .
147 "HgSubject: {desc|firstline}\\n'" .
148 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800149 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700150 "blame_file_cmd" => "hg blame -n \$file",
151 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
152 "blame_commit_pattern" => "^([ 0-9a-f]+):",
153 "author_pattern" => "^HgAuthor: (.*)",
154 "subject_pattern" => "^HgSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800155);
156
Joe Perchesbcde44e2010-10-26 14:22:53 -0700157my $conf = which_conf(".get_maintainer.conf");
158if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700159 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700160 open(my $conffile, '<', "$conf")
161 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
162
Joe Perches368669d2010-05-24 14:33:19 -0700163 while (<$conffile>) {
164 my $line = $_;
165
166 $line =~ s/\s*\n?$//g;
167 $line =~ s/^\s*//g;
168 $line =~ s/\s+/ /g;
169
170 next if ($line =~ m/^\s*#/);
171 next if ($line =~ m/^\s*$/);
172
173 my @words = split(" ", $line);
174 foreach my $word (@words) {
175 last if ($word =~ m/^#/);
176 push (@conf_args, $word);
177 }
178 }
179 close($conffile);
180 unshift(@ARGV, @conf_args) if @conf_args;
181}
182
Joe Perchescb7301c2009-04-07 20:40:12 -0700183if (!GetOptions(
184 'email!' => \$email,
185 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700186 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800187 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700188 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700189 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700190 'git-chief-penguins!' => \$email_git_penguin_chiefs,
191 'git-min-signatures=i' => \$email_git_min_signatures,
192 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700193 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700194 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800195 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700196 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700197 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700198 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700199 'm!' => \$email_maintainer,
200 'n!' => \$email_usename,
201 'l!' => \$email_list,
202 's!' => \$email_subscriber_list,
203 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800204 'roles!' => \$output_roles,
205 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700206 'separator=s' => \$output_separator,
207 'subsystem!' => \$subsystem,
208 'status!' => \$status,
209 'scm!' => \$scm,
210 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700211 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700212 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800213 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800214 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700215 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700216 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800217 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700218 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800219 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700220}
221
222if ($help != 0) {
223 usage();
224 exit 0;
225}
226
227if ($version != 0) {
228 print("${P} ${V}\n");
229 exit 0;
230}
231
Joe Perches64f77f32010-03-05 13:43:04 -0800232if (-t STDIN && !@ARGV) {
233 # We're talking to a terminal, but have no command line arguments.
234 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700235}
236
Joe Perches683c6f82010-10-26 14:22:55 -0700237$output_multiline = 0 if ($output_separator ne ", ");
238$output_rolestats = 1 if ($interactive);
239$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800240
Joe Perches4b76c9d2010-03-05 13:43:03 -0800241if ($sections) {
242 $email = 0;
243 $email_list = 0;
244 $scm = 0;
245 $status = 0;
246 $subsystem = 0;
247 $web = 0;
248 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700249 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800250} else {
251 my $selections = $email + $scm + $status + $subsystem + $web;
252 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800253 die "$P: Missing required option: email, scm, status, subsystem or web\n";
254 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700255}
256
Joe Perchesf5492662009-09-21 17:04:13 -0700257if ($email &&
258 ($email_maintainer + $email_list + $email_subscriber_list +
259 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700260 die "$P: Please select at least 1 email option\n";
261}
262
263if (!top_of_kernel_tree($lk_path)) {
264 die "$P: The current directory does not appear to be "
265 . "a linux kernel source tree.\n";
266}
267
268## Read MAINTAINERS for type/value pairs
269
270my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700271my %keyword_hash;
272
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800273open (my $maint, '<', "${lk_path}MAINTAINERS")
274 or die "$P: Can't open MAINTAINERS: $!\n";
275while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700276 my $line = $_;
277
278 if ($line =~ m/^(\C):\s*(.*)/) {
279 my $type = $1;
280 my $value = $2;
281
282 ##Filename pattern matching
283 if ($type eq "F" || $type eq "X") {
284 $value =~ s@\.@\\\.@g; ##Convert . to \.
285 $value =~ s/\*/\.\*/g; ##Convert * to .*
286 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700287 ##if pattern is a directory and it lacks a trailing slash, add one
288 if ((-d $value)) {
289 $value =~ s@([^/])$@$1/@;
290 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700291 } elsif ($type eq "K") {
292 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700293 }
294 push(@typevalue, "$type:$value");
295 } elsif (!/^(\s)*$/) {
296 $line =~ s/\n$//g;
297 push(@typevalue, $line);
298 }
299}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800300close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700301
Joe Perches8cbb3a72009-09-21 17:04:21 -0700302
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700303#
304# Read mail address map
305#
306
Joe Perchesb9e23312010-10-26 14:22:58 -0700307my $mailmap;
308
309read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700310
311sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700312 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700313 names => {},
314 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700315 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700316
Joe Perchesb9e23312010-10-26 14:22:58 -0700317 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700318
319 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800320 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700321
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700322 while (<$mailmap_file>) {
323 s/#.*$//; #strip comments
324 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700325
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700326 next if (/^\s*$/); #skip empty lines
327 #entries have one of the following formats:
328 # name1 <mail1>
329 # <mail1> <mail2>
330 # name1 <mail1> <mail2>
331 # name1 <mail1> name2 <mail2>
332 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700333
334 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700335 my $real_name = $1;
336 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700337
Joe Perches47abc722010-10-26 14:22:57 -0700338 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700339 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700340 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700341
Joe Perches0334b382011-07-25 17:13:13 -0700342 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700343 my $real_address = $1;
344 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700345
Joe Perches47abc722010-10-26 14:22:57 -0700346 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700347
Joe Perches0334b382011-07-25 17:13:13 -0700348 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700349 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700350 my $real_address = $2;
351 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700352
Joe Perches47abc722010-10-26 14:22:57 -0700353 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700354 ($real_name, $real_address) =
355 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700356 $mailmap->{names}->{$wrong_address} = $real_name;
357 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700358
Joe Perches0334b382011-07-25 17:13:13 -0700359 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700360 my $real_name = $1;
361 my $real_address = $2;
362 my $wrong_name = $3;
363 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700364
Joe Perches47abc722010-10-26 14:22:57 -0700365 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700366 ($real_name, $real_address) =
367 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700368
Joe Perchesb9e23312010-10-26 14:22:58 -0700369 $wrong_name =~ s/\s+$//;
370 ($wrong_name, $wrong_address) =
371 parse_email("$wrong_name <$wrong_address>");
372
373 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
374 $mailmap->{names}->{$wrong_email} = $real_name;
375 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700376 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700377 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700378 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700379}
380
Joe Perches4a7fdb52009-04-10 12:28:57 -0700381## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700382
383my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700384my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700385my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800386my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700387
Joe Perches64f77f32010-03-05 13:43:04 -0800388if (!@ARGV) {
389 push(@ARGV, "&STDIN");
390}
391
Joe Perches4a7fdb52009-04-10 12:28:57 -0700392foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800393 if ($file ne "&STDIN") {
394 ##if $file is a directory and it lacks a trailing slash, add one
395 if ((-d $file)) {
396 $file =~ s@([^/])$@$1/@;
397 } elsif (!(-f $file)) {
398 die "$P: file '${file}' not found\n";
399 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700400 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700401 if ($from_filename) {
402 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700403 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800404 open(my $f, '<', $file)
405 or die "$P: Can't open $file: $!\n";
406 my $text = do { local($/) ; <$f> };
407 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800408 if ($keywords) {
409 foreach my $line (keys %keyword_hash) {
410 if ($text =~ m/$keyword_hash{$line}/x) {
411 push(@keyword_tvi, $line);
412 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700413 }
414 }
Joe Perches03372db2010-03-05 13:43:00 -0800415 if ($file_emails) {
416 my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g;
417 push(@file_emails, clean_file_emails(@poss_addr));
418 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700419 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700420 } else {
421 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700422 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800423
Wolfram Sang3a4df132010-03-23 13:35:18 -0700424 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800425 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700426
427 # We can check arbitrary information before the patch
428 # like the commit message, mail headers, etc...
429 # This allows us to match arbitrary keywords against any part
430 # of a git format-patch generated file (subject tags, etc...)
431
432 my $patch_prefix = ""; #Parsing the intro
433
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800434 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700435 my $patch_line = $_;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700436 if (m/^\+\+\+\s+(\S+)/) {
437 my $filename = $1;
438 $filename =~ s@^[^/]*/@@;
439 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700440 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700441 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700442 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700443 } elsif (m/^\@\@ -(\d+),(\d+)/) {
444 if ($email_git_blame) {
445 push(@range, "$lastfile:$1:$2");
446 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700447 } elsif ($keywords) {
448 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700449 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700450 push(@keyword_tvi, $line);
451 }
452 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700453 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700454 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800455 close($patch);
456
Joe Perches4a7fdb52009-04-10 12:28:57 -0700457 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700458 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700459 . "Add -f to options?\n";
460 }
461 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700462 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700463}
464
Joe Perches03372db2010-03-05 13:43:00 -0800465@file_emails = uniq(@file_emails);
466
Joe Perches683c6f82010-10-26 14:22:55 -0700467my %email_hash_name;
468my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700469my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700470my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700471my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700472my @scm = ();
473my @web = ();
474my @subsystem = ();
475my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700476my %deduplicate_name_hash = ();
477my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700478
Joe Perches6ef1c522010-10-26 14:22:56 -0700479my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700480
Joe Perches6ef1c522010-10-26 14:22:56 -0700481if (@maintainers) {
482 @maintainers = merge_email(@maintainers);
483 output(@maintainers);
484}
Joe Perchescb7301c2009-04-07 20:40:12 -0700485
486if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700487 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700488 output(@scm);
489}
Joe Perches683c6f82010-10-26 14:22:55 -0700490
Joe Perchescb7301c2009-04-07 20:40:12 -0700491if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700492 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700493 output(@status);
494}
495
496if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700497 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700498 output(@subsystem);
499}
500
501if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700502 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700503 output(@web);
504}
505
506exit($exit);
507
Joe Perchesab6c9372011-01-12 16:59:50 -0800508sub range_is_maintained {
509 my ($start, $end) = @_;
510
511 for (my $i = $start; $i < $end; $i++) {
512 my $line = $typevalue[$i];
513 if ($line =~ m/^(\C):\s*(.*)/) {
514 my $type = $1;
515 my $value = $2;
516 if ($type eq 'S') {
517 if ($value =~ /(maintain|support)/i) {
518 return 1;
519 }
520 }
521 }
522 }
523 return 0;
524}
525
526sub range_has_maintainer {
527 my ($start, $end) = @_;
528
529 for (my $i = $start; $i < $end; $i++) {
530 my $line = $typevalue[$i];
531 if ($line =~ m/^(\C):\s*(.*)/) {
532 my $type = $1;
533 my $value = $2;
534 if ($type eq 'M') {
535 return 1;
536 }
537 }
538 }
539 return 0;
540}
541
Joe Perches6ef1c522010-10-26 14:22:56 -0700542sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700543 %email_hash_name = ();
544 %email_hash_address = ();
545 %commit_author_hash = ();
546 %commit_signer_hash = ();
547 @email_to = ();
548 %hash_list_to = ();
549 @list_to = ();
550 @scm = ();
551 @web = ();
552 @subsystem = ();
553 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700554 %deduplicate_name_hash = ();
555 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700556 if ($email_git_all_signature_types) {
557 $signature_pattern = "(.+?)[Bb][Yy]:";
558 } else {
559 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
560 }
561
562 # Find responsible parties
563
Joe Perchesb9e23312010-10-26 14:22:58 -0700564 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700565
Joe Perches683c6f82010-10-26 14:22:55 -0700566 foreach my $file (@files) {
567
568 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700569 my $tvi = find_first_section();
570 while ($tvi < @typevalue) {
571 my $start = find_starting_index($tvi);
572 my $end = find_ending_index($tvi);
573 my $exclude = 0;
574 my $i;
575
576 #Do not match excluded file patterns
577
578 for ($i = $start; $i < $end; $i++) {
579 my $line = $typevalue[$i];
580 if ($line =~ m/^(\C):\s*(.*)/) {
581 my $type = $1;
582 my $value = $2;
583 if ($type eq 'X') {
584 if (file_match_pattern($file, $value)) {
585 $exclude = 1;
586 last;
587 }
588 }
589 }
590 }
591
592 if (!$exclude) {
593 for ($i = $start; $i < $end; $i++) {
594 my $line = $typevalue[$i];
595 if ($line =~ m/^(\C):\s*(.*)/) {
596 my $type = $1;
597 my $value = $2;
598 if ($type eq 'F') {
599 if (file_match_pattern($file, $value)) {
600 my $value_pd = ($value =~ tr@/@@);
601 my $file_pd = ($file =~ tr@/@@);
602 $value_pd++ if (substr($value,-1,1) ne "/");
603 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800604 if ($value_pd >= $file_pd &&
605 range_is_maintained($start, $end) &&
606 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700607 $exact_pattern_match_hash{$file} = 1;
608 }
Joe Perches683c6f82010-10-26 14:22:55 -0700609 if ($pattern_depth == 0 ||
610 (($file_pd - $value_pd) < $pattern_depth)) {
611 $hash{$tvi} = $value_pd;
612 }
613 }
614 }
615 }
616 }
617 }
618 $tvi = $end + 1;
619 }
620
621 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
622 add_categories($line);
623 if ($sections) {
624 my $i;
625 my $start = find_starting_index($line);
626 my $end = find_ending_index($line);
627 for ($i = $start; $i < $end; $i++) {
628 my $line = $typevalue[$i];
629 if ($line =~ /^[FX]:/) { ##Restore file patterns
630 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
631 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
632 $line =~ s/\\\./\./g; ##Convert \. to .
633 $line =~ s/\.\*/\*/g; ##Convert .* to *
634 }
635 $line =~ s/^([A-Z]):/$1:\t/g;
636 print("$line\n");
637 }
638 print("\n");
639 }
640 }
Joe Perches683c6f82010-10-26 14:22:55 -0700641 }
642
643 if ($keywords) {
644 @keyword_tvi = sort_and_uniq(@keyword_tvi);
645 foreach my $line (@keyword_tvi) {
646 add_categories($line);
647 }
648 }
649
Joe Perchesb9e23312010-10-26 14:22:58 -0700650 foreach my $email (@email_to, @list_to) {
651 $email->[0] = deduplicate_email($email->[0]);
652 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700653
654 foreach my $file (@files) {
655 if ($email &&
656 ($email_git || ($email_git_fallback &&
657 !$exact_pattern_match_hash{$file}))) {
658 vcs_file_signoffs($file);
659 }
660 if ($email && $email_git_blame) {
661 vcs_file_blame($file);
662 }
663 }
664
Joe Perches683c6f82010-10-26 14:22:55 -0700665 if ($email) {
666 foreach my $chief (@penguin_chief) {
667 if ($chief =~ m/^(.*):(.*)/) {
668 my $email_address;
669
670 $email_address = format_email($1, $2, $email_usename);
671 if ($email_git_penguin_chiefs) {
672 push(@email_to, [$email_address, 'chief penguin']);
673 } else {
674 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
675 }
676 }
677 }
678
679 foreach my $email (@file_emails) {
680 my ($name, $address) = parse_email($email);
681
682 my $tmp_email = format_email($name, $address, $email_usename);
683 push_email_address($tmp_email, '');
684 add_role($tmp_email, 'in file');
685 }
686 }
687
688 my @to = ();
689 if ($email || $email_list) {
690 if ($email) {
691 @to = (@to, @email_to);
692 }
693 if ($email_list) {
694 @to = (@to, @list_to);
695 }
696 }
697
Joe Perches6ef1c522010-10-26 14:22:56 -0700698 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700699 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700700 }
Joe Perches683c6f82010-10-26 14:22:55 -0700701
702 return @to;
703}
704
Joe Perchescb7301c2009-04-07 20:40:12 -0700705sub file_match_pattern {
706 my ($file, $pattern) = @_;
707 if (substr($pattern, -1) eq "/") {
708 if ($file =~ m@^$pattern@) {
709 return 1;
710 }
711 } else {
712 if ($file =~ m@^$pattern@) {
713 my $s1 = ($file =~ tr@/@@);
714 my $s2 = ($pattern =~ tr@/@@);
715 if ($s1 == $s2) {
716 return 1;
717 }
718 }
719 }
720 return 0;
721}
722
723sub usage {
724 print <<EOT;
725usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700726 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700727version: $V
728
729MAINTAINER field selection options:
730 --email => print email address(es) if any
731 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700732 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700733 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700734 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700735 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700736 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
737 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
738 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700739 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700740 --git-since => git history to use (default: $email_git_since)
741 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700742 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700743 --m => include maintainer(s) if any
744 --n => include name 'Full Name <addr\@domain.tld>'
745 --l => include list(s) if any
746 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700747 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800748 --roles => show roles (status:subsystem, git-signer, list, etc...)
749 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800750 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700751 --scm => print SCM tree(s) if any
752 --status => print status if any
753 --subsystem => print subsystem name if any
754 --web => print website(s) if any
755
756Output type options:
757 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700758 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700759 --multiline => print 1 entry per line
760
Joe Perchescb7301c2009-04-07 20:40:12 -0700761Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700762 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700763 --keywords => scan patch for keywords (default: $keywords)
764 --sections => print all of the subsystem sections with pattern matches
765 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700766 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700767 --help => show this help information
768
Joe Perches3fb55652009-09-21 17:04:17 -0700769Default options:
Joe Perches7e1863a2011-01-12 16:59:49 -0800770 [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
771 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700772
Joe Perches870020f2009-07-29 15:04:28 -0700773Notes:
774 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700775 Used with "--git", git signators for _all_ files in and below
776 directory are examined as git recurses directories.
777 Any specified X: (exclude) pattern matches are _not_ ignored.
778 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800779 no individual file within the directory or subdirectory
780 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700781 Used with "--git-blame", does not iterate all files in directory
782 Using "--git-blame" is slow and may add old committers and authors
783 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800784 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
785 other automated tools that expect only ["name"] <email address>
786 may not work because of additional output after <email address>.
787 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
788 not the percentage of the entire file authored. # of commits is
789 not a good measure of amount of code authored. 1 major commit may
790 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800791 If git is not installed, but mercurial (hg) is installed and an .hg
792 repository exists, the following options apply to mercurial:
793 --git,
794 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
795 --git-blame
796 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700797 File ".get_maintainer.conf", if it exists in the linux kernel source root
798 directory, can change whatever get_maintainer defaults are desired.
799 Entries in this file can be any command line argument.
800 This file is prepended to any additional command line arguments.
801 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700802EOT
803}
804
805sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700806 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700807
Joe Perches47abc722010-10-26 14:22:57 -0700808 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
809 $lk_path .= "/";
810 }
811 if ( (-f "${lk_path}COPYING")
812 && (-f "${lk_path}CREDITS")
813 && (-f "${lk_path}Kbuild")
814 && (-f "${lk_path}MAINTAINERS")
815 && (-f "${lk_path}Makefile")
816 && (-f "${lk_path}README")
817 && (-d "${lk_path}Documentation")
818 && (-d "${lk_path}arch")
819 && (-d "${lk_path}include")
820 && (-d "${lk_path}drivers")
821 && (-d "${lk_path}fs")
822 && (-d "${lk_path}init")
823 && (-d "${lk_path}ipc")
824 && (-d "${lk_path}kernel")
825 && (-d "${lk_path}lib")
826 && (-d "${lk_path}scripts")) {
827 return 1;
828 }
829 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700830}
831
Joe Perches0e70e832009-09-21 17:04:20 -0700832sub parse_email {
833 my ($formatted_email) = @_;
834
835 my $name = "";
836 my $address = "";
837
Joe Perches11ecf532009-09-21 17:04:22 -0700838 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700839 $name = $1;
840 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700841 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700842 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700843 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700844 $address = $1;
845 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700846
847 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700848 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700849 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700850
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800851 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700852 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700853 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700854 }
Joe Perches0e70e832009-09-21 17:04:20 -0700855
856 return ($name, $address);
857}
858
859sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800860 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700861
862 my $formatted_email;
863
864 $name =~ s/^\s+|\s+$//g;
865 $name =~ s/^\"|\"$//g;
866 $address =~ s/^\s+|\s+$//g;
867
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800868 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700869 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
870 $name = "\"$name\"";
871 }
872
Joe Perchesa8af2432009-12-14 18:00:49 -0800873 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700874 if ("$name" eq "") {
875 $formatted_email = "$address";
876 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800877 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700878 }
879 } else {
880 $formatted_email = $address;
881 }
882
Joe Perchescb7301c2009-04-07 20:40:12 -0700883 return $formatted_email;
884}
885
Joe Perches272a8972010-01-08 14:42:48 -0800886sub find_first_section {
887 my $index = 0;
888
889 while ($index < @typevalue) {
890 my $tv = $typevalue[$index];
891 if (($tv =~ m/^(\C):\s*(.*)/)) {
892 last;
893 }
894 $index++;
895 }
896
897 return $index;
898}
899
Joe Perchesb7816552009-09-21 17:04:24 -0700900sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700901 my ($index) = @_;
902
903 while ($index > 0) {
904 my $tv = $typevalue[$index];
905 if (!($tv =~ m/^(\C):\s*(.*)/)) {
906 last;
907 }
908 $index--;
909 }
910
911 return $index;
912}
913
914sub find_ending_index {
915 my ($index) = @_;
916
917 while ($index < @typevalue) {
918 my $tv = $typevalue[$index];
919 if (!($tv =~ m/^(\C):\s*(.*)/)) {
920 last;
921 }
922 $index++;
923 }
924
925 return $index;
926}
927
Joe Perches3c7385b2009-12-14 18:00:46 -0800928sub get_maintainer_role {
929 my ($index) = @_;
930
931 my $i;
932 my $start = find_starting_index($index);
933 my $end = find_ending_index($index);
934
Joe Perches0ede2742012-03-23 15:01:56 -0700935 my $role = "unknown";
Joe Perches3c7385b2009-12-14 18:00:46 -0800936 my $subsystem = $typevalue[$start];
937 if (length($subsystem) > 20) {
938 $subsystem = substr($subsystem, 0, 17);
939 $subsystem =~ s/\s*$//;
940 $subsystem = $subsystem . "...";
941 }
942
943 for ($i = $start + 1; $i < $end; $i++) {
944 my $tv = $typevalue[$i];
945 if ($tv =~ m/^(\C):\s*(.*)/) {
946 my $ptype = $1;
947 my $pvalue = $2;
948 if ($ptype eq "S") {
949 $role = $pvalue;
950 }
951 }
952 }
953
954 $role = lc($role);
955 if ($role eq "supported") {
956 $role = "supporter";
957 } elsif ($role eq "maintained") {
958 $role = "maintainer";
959 } elsif ($role eq "odd fixes") {
960 $role = "odd fixer";
961 } elsif ($role eq "orphan") {
962 $role = "orphan minder";
963 } elsif ($role eq "obsolete") {
964 $role = "obsolete minder";
965 } elsif ($role eq "buried alive in reporters") {
966 $role = "chief penguin";
967 }
968
969 return $role . ":" . $subsystem;
970}
971
972sub get_list_role {
973 my ($index) = @_;
974
975 my $i;
976 my $start = find_starting_index($index);
977 my $end = find_ending_index($index);
978
979 my $subsystem = $typevalue[$start];
980 if (length($subsystem) > 20) {
981 $subsystem = substr($subsystem, 0, 17);
982 $subsystem =~ s/\s*$//;
983 $subsystem = $subsystem . "...";
984 }
985
986 if ($subsystem eq "THE REST") {
987 $subsystem = "";
988 }
989
990 return $subsystem;
991}
992
Joe Perchescb7301c2009-04-07 20:40:12 -0700993sub add_categories {
994 my ($index) = @_;
995
Joe Perchesb7816552009-09-21 17:04:24 -0700996 my $i;
997 my $start = find_starting_index($index);
998 my $end = find_ending_index($index);
999
1000 push(@subsystem, $typevalue[$start]);
1001
1002 for ($i = $start + 1; $i < $end; $i++) {
1003 my $tv = $typevalue[$i];
Joe Perches290603c2009-06-16 15:33:58 -07001004 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001005 my $ptype = $1;
1006 my $pvalue = $2;
1007 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001008 my $list_address = $pvalue;
1009 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001010 my $list_role = get_list_role($i);
1011
1012 if ($list_role ne "") {
1013 $list_role = ":" . $list_role;
1014 }
Joe Perches290603c2009-06-16 15:33:58 -07001015 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1016 $list_address = $1;
1017 $list_additional = $2;
1018 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001019 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001020 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001021 if (!$hash_list_to{lc($list_address)}) {
1022 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001023 push(@list_to, [$list_address,
1024 "subscriber list${list_role}"]);
1025 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001026 }
1027 } else {
1028 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001029 if (!$hash_list_to{lc($list_address)}) {
1030 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001031 if ($list_additional =~ m/moderated/) {
1032 push(@list_to, [$list_address,
1033 "moderated list${list_role}"]);
1034 } else {
1035 push(@list_to, [$list_address,
1036 "open list${list_role}"]);
1037 }
Joe Perches683c6f82010-10-26 14:22:55 -07001038 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001039 }
1040 }
1041 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001042 my ($name, $address) = parse_email($pvalue);
1043 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001044 if ($i > 0) {
1045 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -07001046 if ($tv =~ m/^(\C):\s*(.*)/) {
1047 if ($1 eq "P") {
1048 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001049 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001050 }
1051 }
1052 }
1053 }
Joe Perches0e70e832009-09-21 17:04:20 -07001054 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001055 my $role = get_maintainer_role($i);
1056 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001057 }
1058 } elsif ($ptype eq "T") {
1059 push(@scm, $pvalue);
1060 } elsif ($ptype eq "W") {
1061 push(@web, $pvalue);
1062 } elsif ($ptype eq "S") {
1063 push(@status, $pvalue);
1064 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001065 }
1066 }
1067}
1068
Joe Perches11ecf532009-09-21 17:04:22 -07001069sub email_inuse {
1070 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001071
Joe Perches11ecf532009-09-21 17:04:22 -07001072 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001073 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1074 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001075
Joe Perches0e70e832009-09-21 17:04:20 -07001076 return 0;
1077}
1078
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001079sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001080 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001081
Joe Perches0e70e832009-09-21 17:04:20 -07001082 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001083
Joe Perchesb7816552009-09-21 17:04:24 -07001084 if ($address eq "") {
1085 return 0;
1086 }
1087
Joe Perches11ecf532009-09-21 17:04:22 -07001088 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001089 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001090 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001091 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001092 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001093 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001094 }
Joe Perchesb7816552009-09-21 17:04:24 -07001095
1096 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001097}
1098
1099sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001100 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001101
1102 my @address_list = ();
1103
Joe Perches5f2441e2009-06-16 15:34:02 -07001104 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001105 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001106 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001107 my $array_count = shift(@address_list);
1108 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001109 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001110 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001111 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001112 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001113 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1114 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001115 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001116}
1117
Joe Perches3c7385b2009-12-14 18:00:46 -08001118sub add_role {
1119 my ($line, $role) = @_;
1120
1121 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001122 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001123
1124 foreach my $entry (@email_to) {
1125 if ($email_remove_duplicates) {
1126 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001127 if (($name eq $entry_name || $address eq $entry_address)
1128 && ($role eq "" || !($entry->[1] =~ m/$role/))
1129 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001130 if ($entry->[1] eq "") {
1131 $entry->[1] = "$role";
1132 } else {
1133 $entry->[1] = "$entry->[1],$role";
1134 }
1135 }
1136 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001137 if ($email eq $entry->[0]
1138 && ($role eq "" || !($entry->[1] =~ m/$role/))
1139 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001140 if ($entry->[1] eq "") {
1141 $entry->[1] = "$role";
1142 } else {
1143 $entry->[1] = "$entry->[1],$role";
1144 }
1145 }
1146 }
1147 }
1148}
1149
Joe Perchescb7301c2009-04-07 20:40:12 -07001150sub which {
1151 my ($bin) = @_;
1152
Joe Perchesf5f50782009-06-16 15:34:00 -07001153 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001154 if (-e "$path/$bin") {
1155 return "$path/$bin";
1156 }
1157 }
1158
1159 return "";
1160}
1161
Joe Perchesbcde44e2010-10-26 14:22:53 -07001162sub which_conf {
1163 my ($conf) = @_;
1164
1165 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1166 if (-e "$path/$conf") {
1167 return "$path/$conf";
1168 }
1169 }
1170
1171 return "";
1172}
1173
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001174sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001175 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001176
Joe Perches47abc722010-10-26 14:22:57 -07001177 my ($name, $address) = parse_email($line);
1178 my $email = format_email($name, $address, 1);
1179 my $real_name = $name;
1180 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001181
Joe Perches47abc722010-10-26 14:22:57 -07001182 if (exists $mailmap->{names}->{$email} ||
1183 exists $mailmap->{addresses}->{$email}) {
1184 if (exists $mailmap->{names}->{$email}) {
1185 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001186 }
Joe Perches47abc722010-10-26 14:22:57 -07001187 if (exists $mailmap->{addresses}->{$email}) {
1188 $real_address = $mailmap->{addresses}->{$email};
1189 }
1190 } else {
1191 if (exists $mailmap->{names}->{$address}) {
1192 $real_name = $mailmap->{names}->{$address};
1193 }
1194 if (exists $mailmap->{addresses}->{$address}) {
1195 $real_address = $mailmap->{addresses}->{$address};
1196 }
1197 }
1198 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001199}
1200
1201sub mailmap {
1202 my (@addresses) = @_;
1203
Joe Perchesb9e23312010-10-26 14:22:58 -07001204 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001205 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001206 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001207 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001208 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1209 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001210}
1211
1212sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001213 my %address_map;
1214 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001215
Joe Perches47abc722010-10-26 14:22:57 -07001216 foreach my $email (@emails) {
1217 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001218 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001219 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001220 $email = format_email($name, $address, 1);
1221 } else {
1222 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001223 }
Joe Perches47abc722010-10-26 14:22:57 -07001224 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001225}
1226
Joe Perches60db31a2009-12-14 18:00:50 -08001227sub git_execute_cmd {
1228 my ($cmd) = @_;
1229 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001230
Joe Perches60db31a2009-12-14 18:00:50 -08001231 my $output = `$cmd`;
1232 $output =~ s/^\s*//gm;
1233 @lines = split("\n", $output);
1234
1235 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001236}
1237
Joe Perches60db31a2009-12-14 18:00:50 -08001238sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001239 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001240 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001241
Joe Perches60db31a2009-12-14 18:00:50 -08001242 my $output = `$cmd`;
1243 @lines = split("\n", $output);
1244
1245 return @lines;
1246}
1247
Joe Perches683c6f82010-10-26 14:22:55 -07001248sub extract_formatted_signatures {
1249 my (@signature_lines) = @_;
1250
1251 my @type = @signature_lines;
1252
1253 s/\s*(.*):.*/$1/ for (@type);
1254
1255 # cut -f2- -d":"
1256 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1257
1258## Reformat email addresses (with names) to avoid badly written signatures
1259
1260 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001261 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001262 }
1263
1264 return (\@type, \@signature_lines);
1265}
1266
Joe Perches60db31a2009-12-14 18:00:50 -08001267sub vcs_find_signers {
1268 my ($cmd) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001269 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001270 my @lines = ();
1271 my @signatures = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001272
Joe Perches60db31a2009-12-14 18:00:50 -08001273 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001274
Joe Perches60db31a2009-12-14 18:00:50 -08001275 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchescb7301c2009-04-07 20:40:12 -07001276
Joe Perches60db31a2009-12-14 18:00:50 -08001277 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001278
Joe Perches683c6f82010-10-26 14:22:55 -07001279 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
1280
1281 return (0, @signatures) if !@signatures;
1282
1283 save_commits_by_author(@lines) if ($interactive);
1284 save_commits_by_signer(@lines) if ($interactive);
1285
Joe Perches0e70e832009-09-21 17:04:20 -07001286 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001287 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001288 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001289
Joe Perches683c6f82010-10-26 14:22:55 -07001290 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001291
Joe Perches683c6f82010-10-26 14:22:55 -07001292 return ($commits, @$signers_ref);
Joe Perchesa8af2432009-12-14 18:00:49 -08001293}
1294
Joe Perches63ab52d2010-10-26 14:22:51 -07001295sub vcs_find_author {
1296 my ($cmd) = @_;
1297 my @lines = ();
1298
1299 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1300
1301 if (!$email_git_penguin_chiefs) {
1302 @lines = grep(!/${penguin_chiefs}/i, @lines);
1303 }
1304
1305 return @lines if !@lines;
1306
Joe Perches683c6f82010-10-26 14:22:55 -07001307 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001308 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001309 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1310 my $author = $1;
1311 my ($name, $address) = parse_email($author);
1312 $author = format_email($name, $address, 1);
1313 push(@authors, $author);
1314 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001315 }
1316
Joe Perches683c6f82010-10-26 14:22:55 -07001317 save_commits_by_author(@lines) if ($interactive);
1318 save_commits_by_signer(@lines) if ($interactive);
1319
1320 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001321}
1322
Joe Perches60db31a2009-12-14 18:00:50 -08001323sub vcs_save_commits {
1324 my ($cmd) = @_;
1325 my @lines = ();
1326 my @commits = ();
1327
1328 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1329
1330 foreach my $line (@lines) {
1331 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1332 push(@commits, $1);
1333 }
1334 }
1335
1336 return @commits;
1337}
1338
1339sub vcs_blame {
1340 my ($file) = @_;
1341 my $cmd;
1342 my @commits = ();
1343
1344 return @commits if (!(-f $file));
1345
1346 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1347 my @all_commits = ();
1348
1349 $cmd = $VCS_cmds{"blame_file_cmd"};
1350 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1351 @all_commits = vcs_save_commits($cmd);
1352
1353 foreach my $file_range_diff (@range) {
1354 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1355 my $diff_file = $1;
1356 my $diff_start = $2;
1357 my $diff_length = $3;
1358 next if ("$file" ne "$diff_file");
1359 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1360 push(@commits, $all_commits[$i]);
1361 }
1362 }
1363 } elsif (@range) {
1364 foreach my $file_range_diff (@range) {
1365 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1366 my $diff_file = $1;
1367 my $diff_start = $2;
1368 my $diff_length = $3;
1369 next if ("$file" ne "$diff_file");
1370 $cmd = $VCS_cmds{"blame_range_cmd"};
1371 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1372 push(@commits, vcs_save_commits($cmd));
1373 }
1374 } else {
1375 $cmd = $VCS_cmds{"blame_file_cmd"};
1376 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1377 @commits = vcs_save_commits($cmd);
1378 }
1379
Joe Perches63ab52d2010-10-26 14:22:51 -07001380 foreach my $commit (@commits) {
1381 $commit =~ s/^\^//g;
1382 }
1383
Joe Perches60db31a2009-12-14 18:00:50 -08001384 return @commits;
1385}
1386
1387my $printed_novcs = 0;
1388sub vcs_exists {
1389 %VCS_cmds = %VCS_cmds_git;
1390 return 1 if eval $VCS_cmds{"available"};
1391 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001392 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001393 %VCS_cmds = ();
1394 if (!$printed_novcs) {
1395 warn("$P: No supported VCS found. Add --nogit to options?\n");
1396 warn("Using a git repository produces better results.\n");
1397 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001398 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001399 $printed_novcs = 1;
1400 }
1401 return 0;
1402}
1403
Joe Perches683c6f82010-10-26 14:22:55 -07001404sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001405 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001406 return $vcs_used == 1;
1407}
1408
1409sub vcs_is_hg {
1410 return $vcs_used == 2;
1411}
1412
Joe Perches6ef1c522010-10-26 14:22:56 -07001413sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001414 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001415 my @list = @$list_ref;
1416
Joe Perches683c6f82010-10-26 14:22:55 -07001417 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001418
1419 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001420 my %authored;
1421 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001422 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001423 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001424 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001425 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1426 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001427 $authored{$count} = 0;
1428 $signed{$count} = 0;
1429 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001430 }
1431
1432 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001433 my $done = 0;
1434 my $print_options = 0;
1435 my $redraw = 1;
1436 while (!$done) {
1437 $count = 0;
1438 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001439 printf STDERR "\n%1s %2s %-65s",
1440 "*", "#", "email/list and role:stats";
1441 if ($email_git ||
1442 ($email_git_fallback && !$maintained) ||
1443 $email_git_blame) {
1444 print STDERR "auth sign";
1445 }
1446 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001447 foreach my $entry (@list) {
1448 my $email = $entry->[0];
1449 my $role = $entry->[1];
1450 my $sel = "";
1451 $sel = "*" if ($selected{$count});
1452 my $commit_author = $commit_author_hash{$email};
1453 my $commit_signer = $commit_signer_hash{$email};
1454 my $authored = 0;
1455 my $signed = 0;
1456 $authored++ for (@{$commit_author});
1457 $signed++ for (@{$commit_signer});
1458 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1459 printf STDERR "%4d %4d", $authored, $signed
1460 if ($authored > 0 || $signed > 0);
1461 printf STDERR "\n %s\n", $role;
1462 if ($authored{$count}) {
1463 my $commit_author = $commit_author_hash{$email};
1464 foreach my $ref (@{$commit_author}) {
1465 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001466 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001467 }
Joe Perches683c6f82010-10-26 14:22:55 -07001468 if ($signed{$count}) {
1469 my $commit_signer = $commit_signer_hash{$email};
1470 foreach my $ref (@{$commit_signer}) {
1471 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1472 }
1473 }
1474
1475 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001476 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001477 }
Joe Perches683c6f82010-10-26 14:22:55 -07001478 my $date_ref = \$email_git_since;
1479 $date_ref = \$email_hg_since if (vcs_is_hg());
1480 if ($print_options) {
1481 $print_options = 0;
1482 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001483 print STDERR <<EOT
1484
1485Version Control options:
1486g use git history [$email_git]
1487gf use git-fallback [$email_git_fallback]
1488b use git blame [$email_git_blame]
1489bs use blame signatures [$email_git_blame_signatures]
1490c# minimum commits [$email_git_min_signatures]
1491%# min percent [$email_git_min_percent]
1492d# history to use [$$date_ref]
1493x# max maintainers [$email_git_max_maintainers]
1494t all signature types [$email_git_all_signature_types]
1495m use .mailmap [$email_use_mailmap]
1496EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001497 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001498 print STDERR <<EOT
1499
1500Additional options:
15010 toggle all
1502tm toggle maintainers
1503tg toggle git entries
1504tl toggle open list entries
1505ts toggle subscriber list entries
1506f emails in file [$file_emails]
1507k keywords in file [$keywords]
1508r remove duplicates [$email_remove_duplicates]
1509p# pattern match depth [$pattern_depth]
1510EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001511 }
1512 print STDERR
1513"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1514
1515 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001516 chomp($input);
1517
Joe Perches683c6f82010-10-26 14:22:55 -07001518 $redraw = 1;
1519 my $rerun = 0;
1520 my @wish = split(/[, ]+/, $input);
1521 foreach my $nr (@wish) {
1522 $nr = lc($nr);
1523 my $sel = substr($nr, 0, 1);
1524 my $str = substr($nr, 1);
1525 my $val = 0;
1526 $val = $1 if $str =~ /^(\d+)$/;
1527
1528 if ($sel eq "y") {
1529 $interactive = 0;
1530 $done = 1;
1531 $output_rolestats = 0;
1532 $output_roles = 0;
1533 last;
1534 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1535 $selected{$nr - 1} = !$selected{$nr - 1};
1536 } elsif ($sel eq "*" || $sel eq '^') {
1537 my $toggle = 0;
1538 $toggle = 1 if ($sel eq '*');
1539 for (my $i = 0; $i < $count; $i++) {
1540 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001541 }
Joe Perches683c6f82010-10-26 14:22:55 -07001542 } elsif ($sel eq "0") {
1543 for (my $i = 0; $i < $count; $i++) {
1544 $selected{$i} = !$selected{$i};
1545 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001546 } elsif ($sel eq "t") {
1547 if (lc($str) eq "m") {
1548 for (my $i = 0; $i < $count; $i++) {
1549 $selected{$i} = !$selected{$i}
1550 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1551 }
1552 } elsif (lc($str) eq "g") {
1553 for (my $i = 0; $i < $count; $i++) {
1554 $selected{$i} = !$selected{$i}
1555 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1556 }
1557 } elsif (lc($str) eq "l") {
1558 for (my $i = 0; $i < $count; $i++) {
1559 $selected{$i} = !$selected{$i}
1560 if ($list[$i]->[1] =~ /^(open list)/i);
1561 }
1562 } elsif (lc($str) eq "s") {
1563 for (my $i = 0; $i < $count; $i++) {
1564 $selected{$i} = !$selected{$i}
1565 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1566 }
1567 }
Joe Perches683c6f82010-10-26 14:22:55 -07001568 } elsif ($sel eq "a") {
1569 if ($val > 0 && $val <= $count) {
1570 $authored{$val - 1} = !$authored{$val - 1};
1571 } elsif ($str eq '*' || $str eq '^') {
1572 my $toggle = 0;
1573 $toggle = 1 if ($str eq '*');
1574 for (my $i = 0; $i < $count; $i++) {
1575 $authored{$i} = $toggle;
1576 }
1577 }
1578 } elsif ($sel eq "s") {
1579 if ($val > 0 && $val <= $count) {
1580 $signed{$val - 1} = !$signed{$val - 1};
1581 } elsif ($str eq '*' || $str eq '^') {
1582 my $toggle = 0;
1583 $toggle = 1 if ($str eq '*');
1584 for (my $i = 0; $i < $count; $i++) {
1585 $signed{$i} = $toggle;
1586 }
1587 }
1588 } elsif ($sel eq "o") {
1589 $print_options = 1;
1590 $redraw = 1;
1591 } elsif ($sel eq "g") {
1592 if ($str eq "f") {
1593 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001594 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001595 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001596 }
Joe Perches683c6f82010-10-26 14:22:55 -07001597 $rerun = 1;
1598 } elsif ($sel eq "b") {
1599 if ($str eq "s") {
1600 bool_invert(\$email_git_blame_signatures);
1601 } else {
1602 bool_invert(\$email_git_blame);
1603 }
1604 $rerun = 1;
1605 } elsif ($sel eq "c") {
1606 if ($val > 0) {
1607 $email_git_min_signatures = $val;
1608 $rerun = 1;
1609 }
1610 } elsif ($sel eq "x") {
1611 if ($val > 0) {
1612 $email_git_max_maintainers = $val;
1613 $rerun = 1;
1614 }
1615 } elsif ($sel eq "%") {
1616 if ($str ne "" && $val >= 0) {
1617 $email_git_min_percent = $val;
1618 $rerun = 1;
1619 }
1620 } elsif ($sel eq "d") {
1621 if (vcs_is_git()) {
1622 $email_git_since = $str;
1623 } elsif (vcs_is_hg()) {
1624 $email_hg_since = $str;
1625 }
1626 $rerun = 1;
1627 } elsif ($sel eq "t") {
1628 bool_invert(\$email_git_all_signature_types);
1629 $rerun = 1;
1630 } elsif ($sel eq "f") {
1631 bool_invert(\$file_emails);
1632 $rerun = 1;
1633 } elsif ($sel eq "r") {
1634 bool_invert(\$email_remove_duplicates);
1635 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001636 } elsif ($sel eq "m") {
1637 bool_invert(\$email_use_mailmap);
1638 read_mailmap();
1639 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001640 } elsif ($sel eq "k") {
1641 bool_invert(\$keywords);
1642 $rerun = 1;
1643 } elsif ($sel eq "p") {
1644 if ($str ne "" && $val >= 0) {
1645 $pattern_depth = $val;
1646 $rerun = 1;
1647 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001648 } elsif ($sel eq "h" || $sel eq "?") {
1649 print STDERR <<EOT
1650
1651Interactive mode allows you to select the various maintainers, submitters,
1652commit signers and mailing lists that could be CC'd on a patch.
1653
1654Any *'d entry is selected.
1655
Joe Perches47abc722010-10-26 14:22:57 -07001656If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001657history of files in the patch. Also, each line of the current file can
1658be matched to its commit author and that commits signers with blame.
1659
1660Various knobs exist to control the length of time for active commit
1661tracking, the maximum number of commit authors and signers to add,
1662and such.
1663
1664Enter selections at the prompt until you are satisfied that the selected
1665maintainers are appropriate. You may enter multiple selections separated
1666by either commas or spaces.
1667
1668EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001669 } else {
1670 print STDERR "invalid option: '$nr'\n";
1671 $redraw = 0;
1672 }
1673 }
1674 if ($rerun) {
1675 print STDERR "git-blame can be very slow, please have patience..."
1676 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001677 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001678 }
1679 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001680
1681 #drop not selected entries
1682 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001683 my @new_emailto = ();
1684 foreach my $entry (@list) {
1685 if ($selected{$count}) {
1686 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001687 }
1688 $count++;
1689 }
Joe Perches683c6f82010-10-26 14:22:55 -07001690 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001691}
1692
Joe Perches683c6f82010-10-26 14:22:55 -07001693sub bool_invert {
1694 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001695
Joe Perches683c6f82010-10-26 14:22:55 -07001696 if ($$bool_ref) {
1697 $$bool_ref = 0;
1698 } else {
1699 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001700 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001701}
1702
Joe Perchesb9e23312010-10-26 14:22:58 -07001703sub deduplicate_email {
1704 my ($email) = @_;
1705
1706 my $matched = 0;
1707 my ($name, $address) = parse_email($email);
1708 $email = format_email($name, $address, 1);
1709 $email = mailmap_email($email);
1710
1711 return $email if (!$email_remove_duplicates);
1712
1713 ($name, $address) = parse_email($email);
1714
Joe Perchesfae99202010-10-26 14:22:58 -07001715 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001716 $name = $deduplicate_name_hash{lc($name)}->[0];
1717 $address = $deduplicate_name_hash{lc($name)}->[1];
1718 $matched = 1;
1719 } elsif ($deduplicate_address_hash{lc($address)}) {
1720 $name = $deduplicate_address_hash{lc($address)}->[0];
1721 $address = $deduplicate_address_hash{lc($address)}->[1];
1722 $matched = 1;
1723 }
1724 if (!$matched) {
1725 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1726 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1727 }
1728 $email = format_email($name, $address, 1);
1729 $email = mailmap_email($email);
1730 return $email;
1731}
1732
Joe Perches683c6f82010-10-26 14:22:55 -07001733sub save_commits_by_author {
1734 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001735
Joe Perches683c6f82010-10-26 14:22:55 -07001736 my @authors = ();
1737 my @commits = ();
1738 my @subjects = ();
1739
1740 foreach my $line (@lines) {
1741 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1742 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001743 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001744 push(@authors, $author);
1745 }
1746 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1747 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1748 }
1749
1750 for (my $i = 0; $i < @authors; $i++) {
1751 my $exists = 0;
1752 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1753 if (@{$ref}[0] eq $commits[$i] &&
1754 @{$ref}[1] eq $subjects[$i]) {
1755 $exists = 1;
1756 last;
1757 }
1758 }
1759 if (!$exists) {
1760 push(@{$commit_author_hash{$authors[$i]}},
1761 [ ($commits[$i], $subjects[$i]) ]);
1762 }
1763 }
1764}
1765
1766sub save_commits_by_signer {
1767 my (@lines) = @_;
1768
1769 my $commit = "";
1770 my $subject = "";
1771
1772 foreach my $line (@lines) {
1773 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1774 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1775 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1776 my @signatures = ($line);
1777 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1778 my @types = @$types_ref;
1779 my @signers = @$signers_ref;
1780
1781 my $type = $types[0];
1782 my $signer = $signers[0];
1783
Joe Perchesb9e23312010-10-26 14:22:58 -07001784 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001785
Joe Perches683c6f82010-10-26 14:22:55 -07001786 my $exists = 0;
1787 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1788 if (@{$ref}[0] eq $commit &&
1789 @{$ref}[1] eq $subject &&
1790 @{$ref}[2] eq $type) {
1791 $exists = 1;
1792 last;
1793 }
1794 }
1795 if (!$exists) {
1796 push(@{$commit_signer_hash{$signer}},
1797 [ ($commit, $subject, $type) ]);
1798 }
1799 }
1800 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001801}
1802
Joe Perches60db31a2009-12-14 18:00:50 -08001803sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001804 my ($role, $divisor, @lines) = @_;
1805
1806 my %hash;
1807 my $count = 0;
1808
Joe Perchesa8af2432009-12-14 18:00:49 -08001809 return if (@lines <= 0);
1810
1811 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001812 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001813 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001814 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001815
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001816 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001817
Joe Perches63ab52d2010-10-26 14:22:51 -07001818 return if (@lines <= 0);
1819
Joe Perches0e70e832009-09-21 17:04:20 -07001820 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001821
Joe Perches11ecf532009-09-21 17:04:22 -07001822 # uniq -c
1823 $hash{$_}++ for @lines;
1824
1825 # sort -rn
1826 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1827 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001828 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001829
Joe Perchesa8af2432009-12-14 18:00:49 -08001830 $percent = 100 if ($percent > 100);
Joe Perches11ecf532009-09-21 17:04:22 -07001831 $count++;
1832 last if ($sign_offs < $email_git_min_signatures ||
1833 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001834 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001835 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001836 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001837 my $fmt_percent = sprintf("%.0f", $percent);
1838 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1839 } else {
1840 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001841 }
Joe Perchesf5492662009-09-21 17:04:13 -07001842 }
1843}
1844
Joe Perches60db31a2009-12-14 18:00:50 -08001845sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001846 my ($file) = @_;
1847
1848 my @signers = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001849 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001850
Joe Perches683c6f82010-10-26 14:22:55 -07001851 $vcs_used = vcs_exists();
1852 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001853
Joe Perches60db31a2009-12-14 18:00:50 -08001854 my $cmd = $VCS_cmds{"find_signers_cmd"};
1855 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1856
1857 ($commits, @signers) = vcs_find_signers($cmd);
Joe Perchesb9e23312010-10-26 14:22:58 -07001858
1859 foreach my $signer (@signers) {
1860 $signer = deduplicate_email($signer);
1861 }
1862
Joe Perches60db31a2009-12-14 18:00:50 -08001863 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001864}
1865
Joe Perches60db31a2009-12-14 18:00:50 -08001866sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001867 my ($file) = @_;
1868
Joe Perches60db31a2009-12-14 18:00:50 -08001869 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001870 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001871 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001872 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001873 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001874
Joe Perches683c6f82010-10-26 14:22:55 -07001875 $vcs_used = vcs_exists();
1876 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07001877
Joe Perches63ab52d2010-10-26 14:22:51 -07001878 @all_commits = vcs_blame($file);
1879 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08001880 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001881 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001882
Joe Perches683c6f82010-10-26 14:22:55 -07001883 if ($email_git_blame_signatures) {
1884 if (vcs_is_hg()) {
1885 my $commit_count;
1886 my @commit_signers = ();
1887 my $commit = join(" -r ", @commits);
1888 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07001889
Joe Perches683c6f82010-10-26 14:22:55 -07001890 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1891 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08001892
Joe Perches683c6f82010-10-26 14:22:55 -07001893 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
Joe Perches63ab52d2010-10-26 14:22:51 -07001894
Joe Perches683c6f82010-10-26 14:22:55 -07001895 push(@signers, @commit_signers);
1896 } else {
1897 foreach my $commit (@commits) {
1898 my $commit_count;
1899 my @commit_signers = ();
1900 my $cmd;
1901
1902 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1903 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1904
1905 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
1906
1907 push(@signers, @commit_signers);
1908 }
1909 }
Joe Perchesf5492662009-09-21 17:04:13 -07001910 }
1911
Joe Perchesa8af2432009-12-14 18:00:49 -08001912 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07001913 if ($output_rolestats) {
1914 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07001915 if (vcs_is_hg()) {{ # Double brace for last exit
1916 my $commit_count;
1917 my @commit_signers = ();
1918 @commits = uniq(@commits);
1919 @commits = sort(@commits);
1920 my $commit = join(" -r ", @commits);
1921 my $cmd;
1922
1923 $cmd = $VCS_cmds{"find_commit_author_cmd"};
1924 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1925
1926 my @lines = ();
1927
1928 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1929
1930 if (!$email_git_penguin_chiefs) {
1931 @lines = grep(!/${penguin_chiefs}/i, @lines);
1932 }
1933
1934 last if !@lines;
1935
1936 my @authors = ();
1937 foreach my $line (@lines) {
1938 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1939 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001940 $author = deduplicate_email($author);
1941 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07001942 }
1943 }
1944
1945 save_commits_by_author(@lines) if ($interactive);
1946 save_commits_by_signer(@lines) if ($interactive);
1947
1948 push(@signers, @authors);
1949 }}
1950 else {
1951 foreach my $commit (@commits) {
1952 my $i;
1953 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
1954 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1955 my @author = vcs_find_author($cmd);
1956 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07001957
1958 my $formatted_author = deduplicate_email($author[0]);
1959
Joe Perches683c6f82010-10-26 14:22:55 -07001960 my $count = grep(/$commit/, @all_commits);
1961 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001962 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07001963 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001964 }
1965 }
1966 if (@blame_signers) {
1967 vcs_assign("authored lines", $total_lines, @blame_signers);
1968 }
1969 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001970 foreach my $signer (@signers) {
1971 $signer = deduplicate_email($signer);
1972 }
Joe Perches60db31a2009-12-14 18:00:50 -08001973 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001974 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07001975 foreach my $signer (@signers) {
1976 $signer = deduplicate_email($signer);
1977 }
Joe Perches60db31a2009-12-14 18:00:50 -08001978 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07001979 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001980}
1981
1982sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001983 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001984
1985 my %saw;
1986 @parms = grep(!$saw{$_}++, @parms);
1987 return @parms;
1988}
1989
1990sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001991 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001992
1993 my %saw;
1994 @parms = sort @parms;
1995 @parms = grep(!$saw{$_}++, @parms);
1996 return @parms;
1997}
1998
Joe Perches03372db2010-03-05 13:43:00 -08001999sub clean_file_emails {
2000 my (@file_emails) = @_;
2001 my @fmt_emails = ();
2002
2003 foreach my $email (@file_emails) {
2004 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2005 my ($name, $address) = parse_email($email);
2006 if ($name eq '"[,\.]"') {
2007 $name = "";
2008 }
2009
2010 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2011 if (@nw > 2) {
2012 my $first = $nw[@nw - 3];
2013 my $middle = $nw[@nw - 2];
2014 my $last = $nw[@nw - 1];
2015
2016 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2017 (length($first) == 2 && substr($first, -1) eq ".")) ||
2018 (length($middle) == 1 ||
2019 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2020 $name = "$first $middle $last";
2021 } else {
2022 $name = "$middle $last";
2023 }
2024 }
2025
2026 if (substr($name, -1) =~ /[,\.]/) {
2027 $name = substr($name, 0, length($name) - 1);
2028 } elsif (substr($name, -2) =~ /[,\.]"/) {
2029 $name = substr($name, 0, length($name) - 2) . '"';
2030 }
2031
2032 if (substr($name, 0, 1) =~ /[,\.]/) {
2033 $name = substr($name, 1, length($name) - 1);
2034 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2035 $name = '"' . substr($name, 2, length($name) - 2);
2036 }
2037
2038 my $fmt_email = format_email($name, $address, $email_usename);
2039 push(@fmt_emails, $fmt_email);
2040 }
2041 return @fmt_emails;
2042}
2043
Joe Perches3c7385b2009-12-14 18:00:46 -08002044sub merge_email {
2045 my @lines;
2046 my %saw;
2047
2048 for (@_) {
2049 my ($address, $role) = @$_;
2050 if (!$saw{$address}) {
2051 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002052 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002053 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002054 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002055 }
2056 $saw{$address} = 1;
2057 }
2058 }
2059
2060 return @lines;
2061}
2062
Joe Perchescb7301c2009-04-07 20:40:12 -07002063sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002064 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002065
2066 if ($output_multiline) {
2067 foreach my $line (@parms) {
2068 print("${line}\n");
2069 }
2070 } else {
2071 print(join($output_separator, @parms));
2072 print("\n");
2073 }
2074}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002075
2076my $rfc822re;
2077
2078sub make_rfc822re {
2079# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2080# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2081# This regexp will only work on addresses which have had comments stripped
2082# and replaced with rfc822_lwsp.
2083
2084 my $specials = '()<>@,;:\\\\".\\[\\]';
2085 my $controls = '\\000-\\037\\177';
2086
2087 my $dtext = "[^\\[\\]\\r\\\\]";
2088 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2089
2090 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2091
2092# Use zero-width assertion to spot the limit of an atom. A simple
2093# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2094 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2095 my $word = "(?:$atom|$quoted_string)";
2096 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2097
2098 my $sub_domain = "(?:$atom|$domain_literal)";
2099 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2100
2101 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2102
2103 my $phrase = "$word*";
2104 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2105 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2106 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2107
2108 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2109 my $address = "(?:$mailbox|$group)";
2110
2111 return "$rfc822_lwsp*$address";
2112}
2113
2114sub rfc822_strip_comments {
2115 my $s = shift;
2116# Recursively remove comments, and replace with a single space. The simpler
2117# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2118# chars in atoms, for example.
2119
2120 while ($s =~ s/^((?:[^"\\]|\\.)*
2121 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2122 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2123 return $s;
2124}
2125
2126# valid: returns true if the parameter is an RFC822 valid address
2127#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002128sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002129 my $s = rfc822_strip_comments(shift);
2130
2131 if (!$rfc822re) {
2132 $rfc822re = make_rfc822re();
2133 }
2134
2135 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2136}
2137
2138# validlist: In scalar context, returns true if the parameter is an RFC822
2139# valid list of addresses.
2140#
2141# In list context, returns an empty list on failure (an invalid
2142# address was found); otherwise a list whose first element is the
2143# number of addresses found and whose remaining elements are the
2144# addresses. This is needed to disambiguate failure (invalid)
2145# from success with no addresses found, because an empty string is
2146# a valid list.
2147
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002148sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002149 my $s = rfc822_strip_comments(shift);
2150
2151 if (!$rfc822re) {
2152 $rfc822re = make_rfc822re();
2153 }
2154 # * null list items are valid according to the RFC
2155 # * the '1' business is to aid in distinguishing failure from no results
2156
2157 my @r;
2158 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2159 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002160 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002161 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002162 }
2163 return wantarray ? (scalar(@r), @r) : 1;
2164 }
Joe Perches60db31a2009-12-14 18:00:50 -08002165 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002166}