blob: cab641a12dd587b39ab7a745d5c9623ee852c66c [file] [log] [blame]
Joe Perchescb7301c2009-04-07 20:40:12 -07001#!/usr/bin/perl -w
2# (c) 2007, Joe Perches <joe@perches.com>
3# created from checkpatch.pl
4#
5# Print selected MAINTAINERS information for
6# the files modified in a patch or for a file
7#
Roel Kluin3bd7bf52009-11-11 14:26:13 -08008# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch>
9# perl scripts/get_maintainer.pl [OPTIONS] -f <file>
Joe Perchescb7301c2009-04-07 20:40:12 -070010#
11# Licensed under the terms of the GNU GPL License version 2
12
13use strict;
14
15my $P = $0;
Joe Perches7e1863a2011-01-12 16:59:49 -080016my $V = '0.26';
Joe Perchescb7301c2009-04-07 20:40:12 -070017
18use Getopt::Long qw(:config no_auto_abbrev);
19
20my $lk_path = "./";
21my $email = 1;
22my $email_usename = 1;
23my $email_maintainer = 1;
Joe Perchesc1c3f2c2014-06-02 12:05:17 -070024my $email_reviewer = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070025my $email_list = 1;
26my $email_subscriber_list = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070027my $email_git_penguin_chiefs = 0;
Joe Perchese3e9d112010-10-26 14:22:53 -070028my $email_git = 0;
Florian Mickler0fa05592010-05-24 14:33:20 -070029my $email_git_all_signature_types = 0;
Joe Perches60db31a2009-12-14 18:00:50 -080030my $email_git_blame = 0;
Joe Perches683c6f82010-10-26 14:22:55 -070031my $email_git_blame_signatures = 1;
Joe Perchese3e9d112010-10-26 14:22:53 -070032my $email_git_fallback = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070033my $email_git_min_signatures = 1;
34my $email_git_max_maintainers = 5;
Joe Perchesafa81ee2009-07-29 15:04:28 -070035my $email_git_min_percent = 5;
Joe Perchescb7301c2009-04-07 20:40:12 -070036my $email_git_since = "1-year-ago";
Joe Perches60db31a2009-12-14 18:00:50 -080037my $email_hg_since = "-365";
Florian Micklerdace8e32010-10-26 14:22:54 -070038my $interactive = 0;
Joe Perches11ecf532009-09-21 17:04:22 -070039my $email_remove_duplicates = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -070040my $email_use_mailmap = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070041my $output_multiline = 1;
42my $output_separator = ", ";
Joe Perches3c7385b2009-12-14 18:00:46 -080043my $output_roles = 0;
Joe Perches7e1863a2011-01-12 16:59:49 -080044my $output_rolestats = 1;
Joe Perches364f68d2015-06-25 15:01:52 -070045my $output_section_maxlen = 50;
Joe Perchescb7301c2009-04-07 20:40:12 -070046my $scm = 0;
47my $web = 0;
48my $subsystem = 0;
49my $status = 0;
Joe Perchesdcf36a92009-10-26 16:49:47 -070050my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080051my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080052my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070053my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070054my $pattern_depth = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070055my $version = 0;
56my $help = 0;
57
Joe Perches683c6f82010-10-26 14:22:55 -070058my $vcs_used = 0;
59
Joe Perchescb7301c2009-04-07 20:40:12 -070060my $exit = 0;
61
Joe Perches683c6f82010-10-26 14:22:55 -070062my %commit_author_hash;
63my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070064
Joe Perchescb7301c2009-04-07 20:40:12 -070065my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070066push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070067#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070068#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070069
70my @penguin_chief_names = ();
71foreach my $chief (@penguin_chief) {
72 if ($chief =~ m/^(.*):(.*)/) {
73 my $chief_name = $1;
74 my $chief_addr = $2;
75 push(@penguin_chief_names, $chief_name);
76 }
77}
Joe Perchese4d26b02010-05-24 14:33:17 -070078my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
79
80# Signature types of people who are either
81# a) responsible for the code in question, or
82# b) familiar enough with it to give relevant feedback
83my @signature_tags = ();
84push(@signature_tags, "Signed-off-by:");
85push(@signature_tags, "Reviewed-by:");
86push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070087
Joe Perches7dea2682012-06-20 12:53:02 -070088my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
89
Joe Perches5f2441e2009-06-16 15:34:02 -070090# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070091my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070092my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070093
Joe Perches60db31a2009-12-14 18:00:50 -080094# VCS command support: class-like functions and strings
95
96my %VCS_cmds;
97
98my %VCS_cmds_git = (
99 "execute_cmd" => \&git_execute_cmd,
Richard Genoudec83b612014-02-10 14:25:31 -0800100 "available" => '(which("git") ne "") && (-e ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -0700101 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800102 "git log --no-color --follow --since=\$email_git_since " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800103 '--numstat --no-merges ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700104 '--format="GitCommit: %H%n' .
105 'GitAuthor: %an <%ae>%n' .
106 'GitDate: %aD%n' .
107 'GitSubject: %s%n' .
108 '%b%n"' .
109 " -- \$file",
110 "find_commit_signers_cmd" =>
111 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800112 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700113 '--format="GitCommit: %H%n' .
114 'GitAuthor: %an <%ae>%n' .
115 'GitDate: %aD%n' .
116 'GitSubject: %s%n' .
117 '%b%n"' .
118 " -1 \$commit",
119 "find_commit_author_cmd" =>
120 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800121 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700122 '--format="GitCommit: %H%n' .
123 'GitAuthor: %an <%ae>%n' .
124 'GitDate: %aD%n' .
125 'GitSubject: %s%n"' .
126 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800127 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
128 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700129 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700130 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700131 "author_pattern" => "^GitAuthor: (.*)",
132 "subject_pattern" => "^GitSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800133 "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
Joe Perches60db31a2009-12-14 18:00:50 -0800134);
135
136my %VCS_cmds_hg = (
137 "execute_cmd" => \&hg_execute_cmd,
138 "available" => '(which("hg") ne "") && (-d ".hg")',
139 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700140 "hg log --date=\$email_hg_since " .
141 "--template='HgCommit: {node}\\n" .
142 "HgAuthor: {author}\\n" .
143 "HgSubject: {desc}\\n'" .
144 " -- \$file",
145 "find_commit_signers_cmd" =>
146 "hg log " .
147 "--template='HgSubject: {desc}\\n'" .
148 " -r \$commit",
149 "find_commit_author_cmd" =>
150 "hg log " .
151 "--template='HgCommit: {node}\\n" .
152 "HgAuthor: {author}\\n" .
153 "HgSubject: {desc|firstline}\\n'" .
154 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800155 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700156 "blame_file_cmd" => "hg blame -n \$file",
157 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
158 "blame_commit_pattern" => "^([ 0-9a-f]+):",
159 "author_pattern" => "^HgAuthor: (.*)",
160 "subject_pattern" => "^HgSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800161 "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
Joe Perches60db31a2009-12-14 18:00:50 -0800162);
163
Joe Perchesbcde44e2010-10-26 14:22:53 -0700164my $conf = which_conf(".get_maintainer.conf");
165if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700166 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700167 open(my $conffile, '<', "$conf")
168 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
169
Joe Perches368669d2010-05-24 14:33:19 -0700170 while (<$conffile>) {
171 my $line = $_;
172
173 $line =~ s/\s*\n?$//g;
174 $line =~ s/^\s*//g;
175 $line =~ s/\s+/ /g;
176
177 next if ($line =~ m/^\s*#/);
178 next if ($line =~ m/^\s*$/);
179
180 my @words = split(" ", $line);
181 foreach my $word (@words) {
182 last if ($word =~ m/^#/);
183 push (@conf_args, $word);
184 }
185 }
186 close($conffile);
187 unshift(@ARGV, @conf_args) if @conf_args;
188}
189
Joe Perches435de072015-06-25 15:01:50 -0700190my @ignore_emails = ();
191my $ignore_file = which_conf(".get_maintainer.ignore");
192if (-f $ignore_file) {
193 open(my $ignore, '<', "$ignore_file")
194 or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
195 while (<$ignore>) {
196 my $line = $_;
197
198 $line =~ s/\s*\n?$//;
199 $line =~ s/^\s*//;
200 $line =~ s/\s+$//;
201 $line =~ s/#.*$//;
202
203 next if ($line =~ m/^\s*$/);
204 if (rfc822_valid($line)) {
205 push(@ignore_emails, $line);
206 }
207 }
208 close($ignore);
209}
210
Joe Perchescb7301c2009-04-07 20:40:12 -0700211if (!GetOptions(
212 'email!' => \$email,
213 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700214 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800215 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700216 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700217 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700218 'git-chief-penguins!' => \$email_git_penguin_chiefs,
219 'git-min-signatures=i' => \$email_git_min_signatures,
220 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700221 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700222 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800223 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700224 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700225 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700226 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700227 'm!' => \$email_maintainer,
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700228 'r!' => \$email_reviewer,
Joe Perchescb7301c2009-04-07 20:40:12 -0700229 'n!' => \$email_usename,
230 'l!' => \$email_list,
231 's!' => \$email_subscriber_list,
232 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800233 'roles!' => \$output_roles,
234 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700235 'separator=s' => \$output_separator,
236 'subsystem!' => \$subsystem,
237 'status!' => \$status,
238 'scm!' => \$scm,
239 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700240 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700241 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800242 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800243 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700244 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700245 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800246 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700247 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800248 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700249}
250
251if ($help != 0) {
252 usage();
253 exit 0;
254}
255
256if ($version != 0) {
257 print("${P} ${V}\n");
258 exit 0;
259}
260
Joe Perches64f77f32010-03-05 13:43:04 -0800261if (-t STDIN && !@ARGV) {
262 # We're talking to a terminal, but have no command line arguments.
263 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700264}
265
Joe Perches683c6f82010-10-26 14:22:55 -0700266$output_multiline = 0 if ($output_separator ne ", ");
267$output_rolestats = 1 if ($interactive);
268$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800269
Joe Perches4b76c9d2010-03-05 13:43:03 -0800270if ($sections) {
271 $email = 0;
272 $email_list = 0;
273 $scm = 0;
274 $status = 0;
275 $subsystem = 0;
276 $web = 0;
277 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700278 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800279} else {
280 my $selections = $email + $scm + $status + $subsystem + $web;
281 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800282 die "$P: Missing required option: email, scm, status, subsystem or web\n";
283 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700284}
285
Joe Perchesf5492662009-09-21 17:04:13 -0700286if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700287 ($email_maintainer + $email_reviewer +
288 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700289 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700290 die "$P: Please select at least 1 email option\n";
291}
292
293if (!top_of_kernel_tree($lk_path)) {
294 die "$P: The current directory does not appear to be "
295 . "a linux kernel source tree.\n";
296}
297
298## Read MAINTAINERS for type/value pairs
299
300my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700301my %keyword_hash;
302
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800303open (my $maint, '<', "${lk_path}MAINTAINERS")
304 or die "$P: Can't open MAINTAINERS: $!\n";
305while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700306 my $line = $_;
307
Joe Perchesce8155f2015-06-25 15:01:55 -0700308 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700309 my $type = $1;
310 my $value = $2;
311
312 ##Filename pattern matching
313 if ($type eq "F" || $type eq "X") {
314 $value =~ s@\.@\\\.@g; ##Convert . to \.
315 $value =~ s/\*/\.\*/g; ##Convert * to .*
316 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700317 ##if pattern is a directory and it lacks a trailing slash, add one
318 if ((-d $value)) {
319 $value =~ s@([^/])$@$1/@;
320 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700321 } elsif ($type eq "K") {
322 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700323 }
324 push(@typevalue, "$type:$value");
325 } elsif (!/^(\s)*$/) {
326 $line =~ s/\n$//g;
327 push(@typevalue, $line);
328 }
329}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800330close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700331
Joe Perches8cbb3a72009-09-21 17:04:21 -0700332
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700333#
334# Read mail address map
335#
336
Joe Perchesb9e23312010-10-26 14:22:58 -0700337my $mailmap;
338
339read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700340
341sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700342 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700343 names => {},
344 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700345 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700346
Joe Perchesb9e23312010-10-26 14:22:58 -0700347 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700348
349 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800350 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700351
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700352 while (<$mailmap_file>) {
353 s/#.*$//; #strip comments
354 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700355
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700356 next if (/^\s*$/); #skip empty lines
357 #entries have one of the following formats:
358 # name1 <mail1>
359 # <mail1> <mail2>
360 # name1 <mail1> <mail2>
361 # name1 <mail1> name2 <mail2>
362 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700363
364 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700365 my $real_name = $1;
366 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700367
Joe Perches47abc722010-10-26 14:22:57 -0700368 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700369 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700370 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700371
Joe Perches0334b382011-07-25 17:13:13 -0700372 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700373 my $real_address = $1;
374 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700375
Joe Perches47abc722010-10-26 14:22:57 -0700376 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700377
Joe Perches0334b382011-07-25 17:13:13 -0700378 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700379 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700380 my $real_address = $2;
381 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700382
Joe Perches47abc722010-10-26 14:22:57 -0700383 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700384 ($real_name, $real_address) =
385 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700386 $mailmap->{names}->{$wrong_address} = $real_name;
387 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700388
Joe Perches0334b382011-07-25 17:13:13 -0700389 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700390 my $real_name = $1;
391 my $real_address = $2;
392 my $wrong_name = $3;
393 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700394
Joe Perches47abc722010-10-26 14:22:57 -0700395 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700396 ($real_name, $real_address) =
397 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700398
Joe Perchesb9e23312010-10-26 14:22:58 -0700399 $wrong_name =~ s/\s+$//;
400 ($wrong_name, $wrong_address) =
401 parse_email("$wrong_name <$wrong_address>");
402
403 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
404 $mailmap->{names}->{$wrong_email} = $real_name;
405 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700406 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700407 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700408 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700409}
410
Joe Perches4a7fdb52009-04-10 12:28:57 -0700411## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700412
413my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700414my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700415my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800416my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700417
Joe Perches64f77f32010-03-05 13:43:04 -0800418if (!@ARGV) {
419 push(@ARGV, "&STDIN");
420}
421
Joe Perches4a7fdb52009-04-10 12:28:57 -0700422foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800423 if ($file ne "&STDIN") {
424 ##if $file is a directory and it lacks a trailing slash, add one
425 if ((-d $file)) {
426 $file =~ s@([^/])$@$1/@;
427 } elsif (!(-f $file)) {
428 die "$P: file '${file}' not found\n";
429 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700430 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700431 if ($from_filename) {
432 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700433 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800434 open(my $f, '<', $file)
435 or die "$P: Can't open $file: $!\n";
436 my $text = do { local($/) ; <$f> };
437 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800438 if ($keywords) {
439 foreach my $line (keys %keyword_hash) {
440 if ($text =~ m/$keyword_hash{$line}/x) {
441 push(@keyword_tvi, $line);
442 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700443 }
444 }
Joe Perches03372db2010-03-05 13:43:00 -0800445 if ($file_emails) {
446 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;
447 push(@file_emails, clean_file_emails(@poss_addr));
448 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700449 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700450 } else {
451 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700452 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800453
Wolfram Sang3a4df132010-03-23 13:35:18 -0700454 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800455 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700456
457 # We can check arbitrary information before the patch
458 # like the commit message, mail headers, etc...
459 # This allows us to match arbitrary keywords against any part
460 # of a git format-patch generated file (subject tags, etc...)
461
462 my $patch_prefix = ""; #Parsing the intro
463
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800464 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700465 my $patch_line = $_;
Geert Uytterhoeven6be07102013-02-21 16:43:12 -0800466 if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700467 my $filename = $1;
468 $filename =~ s@^[^/]*/@@;
469 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700470 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700471 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700472 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700473 } elsif (m/^\@\@ -(\d+),(\d+)/) {
474 if ($email_git_blame) {
475 push(@range, "$lastfile:$1:$2");
476 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700477 } elsif ($keywords) {
478 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700479 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700480 push(@keyword_tvi, $line);
481 }
482 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700483 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700484 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800485 close($patch);
486
Joe Perches4a7fdb52009-04-10 12:28:57 -0700487 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700488 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700489 . "Add -f to options?\n";
490 }
491 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700492 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700493}
494
Joe Perches03372db2010-03-05 13:43:00 -0800495@file_emails = uniq(@file_emails);
496
Joe Perches683c6f82010-10-26 14:22:55 -0700497my %email_hash_name;
498my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700499my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700500my %hash_list_to;
Joe Perches290603c12009-06-16 15:33:58 -0700501my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700502my @scm = ();
503my @web = ();
504my @subsystem = ();
505my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700506my %deduplicate_name_hash = ();
507my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700508
Joe Perches6ef1c522010-10-26 14:22:56 -0700509my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700510
Joe Perches6ef1c522010-10-26 14:22:56 -0700511if (@maintainers) {
512 @maintainers = merge_email(@maintainers);
513 output(@maintainers);
514}
Joe Perchescb7301c2009-04-07 20:40:12 -0700515
516if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700517 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700518 output(@scm);
519}
Joe Perches683c6f82010-10-26 14:22:55 -0700520
Joe Perchescb7301c2009-04-07 20:40:12 -0700521if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700522 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700523 output(@status);
524}
525
526if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700527 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700528 output(@subsystem);
529}
530
531if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700532 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700533 output(@web);
534}
535
536exit($exit);
537
Joe Perches435de072015-06-25 15:01:50 -0700538sub ignore_email_address {
539 my ($address) = @_;
540
541 foreach my $ignore (@ignore_emails) {
542 return 1 if ($ignore eq $address);
543 }
544
545 return 0;
546}
547
Joe Perchesab6c9372011-01-12 16:59:50 -0800548sub range_is_maintained {
549 my ($start, $end) = @_;
550
551 for (my $i = $start; $i < $end; $i++) {
552 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700553 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800554 my $type = $1;
555 my $value = $2;
556 if ($type eq 'S') {
557 if ($value =~ /(maintain|support)/i) {
558 return 1;
559 }
560 }
561 }
562 }
563 return 0;
564}
565
566sub range_has_maintainer {
567 my ($start, $end) = @_;
568
569 for (my $i = $start; $i < $end; $i++) {
570 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700571 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800572 my $type = $1;
573 my $value = $2;
574 if ($type eq 'M') {
575 return 1;
576 }
577 }
578 }
579 return 0;
580}
581
Joe Perches6ef1c522010-10-26 14:22:56 -0700582sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700583 %email_hash_name = ();
584 %email_hash_address = ();
585 %commit_author_hash = ();
586 %commit_signer_hash = ();
587 @email_to = ();
588 %hash_list_to = ();
589 @list_to = ();
590 @scm = ();
591 @web = ();
592 @subsystem = ();
593 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700594 %deduplicate_name_hash = ();
595 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700596 if ($email_git_all_signature_types) {
597 $signature_pattern = "(.+?)[Bb][Yy]:";
598 } else {
599 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
600 }
601
602 # Find responsible parties
603
Joe Perchesb9e23312010-10-26 14:22:58 -0700604 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700605
Joe Perches683c6f82010-10-26 14:22:55 -0700606 foreach my $file (@files) {
607
608 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700609 my $tvi = find_first_section();
610 while ($tvi < @typevalue) {
611 my $start = find_starting_index($tvi);
612 my $end = find_ending_index($tvi);
613 my $exclude = 0;
614 my $i;
615
616 #Do not match excluded file patterns
617
618 for ($i = $start; $i < $end; $i++) {
619 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700620 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700621 my $type = $1;
622 my $value = $2;
623 if ($type eq 'X') {
624 if (file_match_pattern($file, $value)) {
625 $exclude = 1;
626 last;
627 }
628 }
629 }
630 }
631
632 if (!$exclude) {
633 for ($i = $start; $i < $end; $i++) {
634 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700635 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700636 my $type = $1;
637 my $value = $2;
638 if ($type eq 'F') {
639 if (file_match_pattern($file, $value)) {
640 my $value_pd = ($value =~ tr@/@@);
641 my $file_pd = ($file =~ tr@/@@);
642 $value_pd++ if (substr($value,-1,1) ne "/");
643 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800644 if ($value_pd >= $file_pd &&
645 range_is_maintained($start, $end) &&
646 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700647 $exact_pattern_match_hash{$file} = 1;
648 }
Joe Perches683c6f82010-10-26 14:22:55 -0700649 if ($pattern_depth == 0 ||
650 (($file_pd - $value_pd) < $pattern_depth)) {
651 $hash{$tvi} = $value_pd;
652 }
653 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700654 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800655 if ($file =~ m/$value/x) {
656 $hash{$tvi} = 0;
657 }
Joe Perches683c6f82010-10-26 14:22:55 -0700658 }
659 }
660 }
661 }
662 $tvi = $end + 1;
663 }
664
665 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
666 add_categories($line);
667 if ($sections) {
668 my $i;
669 my $start = find_starting_index($line);
670 my $end = find_ending_index($line);
671 for ($i = $start; $i < $end; $i++) {
672 my $line = $typevalue[$i];
673 if ($line =~ /^[FX]:/) { ##Restore file patterns
674 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
675 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
676 $line =~ s/\\\./\./g; ##Convert \. to .
677 $line =~ s/\.\*/\*/g; ##Convert .* to *
678 }
679 $line =~ s/^([A-Z]):/$1:\t/g;
680 print("$line\n");
681 }
682 print("\n");
683 }
684 }
Joe Perches683c6f82010-10-26 14:22:55 -0700685 }
686
687 if ($keywords) {
688 @keyword_tvi = sort_and_uniq(@keyword_tvi);
689 foreach my $line (@keyword_tvi) {
690 add_categories($line);
691 }
692 }
693
Joe Perchesb9e23312010-10-26 14:22:58 -0700694 foreach my $email (@email_to, @list_to) {
695 $email->[0] = deduplicate_email($email->[0]);
696 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700697
698 foreach my $file (@files) {
699 if ($email &&
700 ($email_git || ($email_git_fallback &&
701 !$exact_pattern_match_hash{$file}))) {
702 vcs_file_signoffs($file);
703 }
704 if ($email && $email_git_blame) {
705 vcs_file_blame($file);
706 }
707 }
708
Joe Perches683c6f82010-10-26 14:22:55 -0700709 if ($email) {
710 foreach my $chief (@penguin_chief) {
711 if ($chief =~ m/^(.*):(.*)/) {
712 my $email_address;
713
714 $email_address = format_email($1, $2, $email_usename);
715 if ($email_git_penguin_chiefs) {
716 push(@email_to, [$email_address, 'chief penguin']);
717 } else {
718 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
719 }
720 }
721 }
722
723 foreach my $email (@file_emails) {
724 my ($name, $address) = parse_email($email);
725
726 my $tmp_email = format_email($name, $address, $email_usename);
727 push_email_address($tmp_email, '');
728 add_role($tmp_email, 'in file');
729 }
730 }
731
732 my @to = ();
733 if ($email || $email_list) {
734 if ($email) {
735 @to = (@to, @email_to);
736 }
737 if ($email_list) {
738 @to = (@to, @list_to);
739 }
740 }
741
Joe Perches6ef1c522010-10-26 14:22:56 -0700742 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700743 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700744 }
Joe Perches683c6f82010-10-26 14:22:55 -0700745
746 return @to;
747}
748
Joe Perchescb7301c2009-04-07 20:40:12 -0700749sub file_match_pattern {
750 my ($file, $pattern) = @_;
751 if (substr($pattern, -1) eq "/") {
752 if ($file =~ m@^$pattern@) {
753 return 1;
754 }
755 } else {
756 if ($file =~ m@^$pattern@) {
757 my $s1 = ($file =~ tr@/@@);
758 my $s2 = ($pattern =~ tr@/@@);
759 if ($s1 == $s2) {
760 return 1;
761 }
762 }
763 }
764 return 0;
765}
766
767sub usage {
768 print <<EOT;
769usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700770 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700771version: $V
772
773MAINTAINER field selection options:
774 --email => print email address(es) if any
775 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700776 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700777 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700778 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700779 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700780 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
781 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
782 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700783 --git-blame => use git blame to find modified commits for patch or file
Brian Norris3cbcca82015-11-06 16:30:41 -0800784 --git-blame-signatures => when used with --git-blame, also include all commit signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700785 --git-since => git history to use (default: $email_git_since)
786 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700787 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700788 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700789 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -0700790 --n => include name 'Full Name <addr\@domain.tld>'
791 --l => include list(s) if any
792 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700793 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800794 --roles => show roles (status:subsystem, git-signer, list, etc...)
795 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800796 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700797 --scm => print SCM tree(s) if any
798 --status => print status if any
799 --subsystem => print subsystem name if any
800 --web => print website(s) if any
801
802Output type options:
803 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700804 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700805 --multiline => print 1 entry per line
806
Joe Perchescb7301c2009-04-07 20:40:12 -0700807Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700808 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700809 --keywords => scan patch for keywords (default: $keywords)
810 --sections => print all of the subsystem sections with pattern matches
811 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f5078d2009-06-16 15:34:00 -0700812 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700813 --help => show this help information
814
Joe Perches3fb55652009-09-21 17:04:17 -0700815Default options:
Brian Norris4f075102015-11-06 16:30:49 -0800816 [--email --nogit --git-fallback --m --r --n --l --multiline --pattern-depth=0
Joe Perches7e1863a2011-01-12 16:59:49 -0800817 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700818
Joe Perches870020f2009-07-29 15:04:28 -0700819Notes:
820 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700821 Used with "--git", git signators for _all_ files in and below
822 directory are examined as git recurses directories.
823 Any specified X: (exclude) pattern matches are _not_ ignored.
824 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800825 no individual file within the directory or subdirectory
826 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700827 Used with "--git-blame", does not iterate all files in directory
828 Using "--git-blame" is slow and may add old committers and authors
829 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800830 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
831 other automated tools that expect only ["name"] <email address>
832 may not work because of additional output after <email address>.
833 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
834 not the percentage of the entire file authored. # of commits is
835 not a good measure of amount of code authored. 1 major commit may
836 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800837 If git is not installed, but mercurial (hg) is installed and an .hg
838 repository exists, the following options apply to mercurial:
839 --git,
840 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
841 --git-blame
842 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700843 File ".get_maintainer.conf", if it exists in the linux kernel source root
844 directory, can change whatever get_maintainer defaults are desired.
845 Entries in this file can be any command line argument.
846 This file is prepended to any additional command line arguments.
847 Multiple lines and # comments are allowed.
Brian Norrisb1312bfe2015-11-06 16:30:46 -0800848 Most options have both positive and negative forms.
849 The negative forms for --<foo> are --no<foo> and --no-<foo>.
850
Joe Perchescb7301c2009-04-07 20:40:12 -0700851EOT
852}
853
854sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700855 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700856
Joe Perches47abc722010-10-26 14:22:57 -0700857 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
858 $lk_path .= "/";
859 }
860 if ( (-f "${lk_path}COPYING")
861 && (-f "${lk_path}CREDITS")
862 && (-f "${lk_path}Kbuild")
863 && (-f "${lk_path}MAINTAINERS")
864 && (-f "${lk_path}Makefile")
865 && (-f "${lk_path}README")
866 && (-d "${lk_path}Documentation")
867 && (-d "${lk_path}arch")
868 && (-d "${lk_path}include")
869 && (-d "${lk_path}drivers")
870 && (-d "${lk_path}fs")
871 && (-d "${lk_path}init")
872 && (-d "${lk_path}ipc")
873 && (-d "${lk_path}kernel")
874 && (-d "${lk_path}lib")
875 && (-d "${lk_path}scripts")) {
876 return 1;
877 }
878 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700879}
880
Joe Perches0e70e832009-09-21 17:04:20 -0700881sub parse_email {
882 my ($formatted_email) = @_;
883
884 my $name = "";
885 my $address = "";
886
Joe Perches11ecf532009-09-21 17:04:22 -0700887 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700888 $name = $1;
889 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700890 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700891 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700892 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700893 $address = $1;
894 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700895
896 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700897 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700898 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700899
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800900 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700901 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700902 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700903 }
Joe Perches0e70e832009-09-21 17:04:20 -0700904
905 return ($name, $address);
906}
907
908sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800909 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700910
911 my $formatted_email;
912
913 $name =~ s/^\s+|\s+$//g;
914 $name =~ s/^\"|\"$//g;
915 $address =~ s/^\s+|\s+$//g;
916
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800917 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700918 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
919 $name = "\"$name\"";
920 }
921
Joe Perchesa8af2432009-12-14 18:00:49 -0800922 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700923 if ("$name" eq "") {
924 $formatted_email = "$address";
925 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800926 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700927 }
928 } else {
929 $formatted_email = $address;
930 }
931
Joe Perchescb7301c2009-04-07 20:40:12 -0700932 return $formatted_email;
933}
934
Joe Perches272a8972010-01-08 14:42:48 -0800935sub find_first_section {
936 my $index = 0;
937
938 while ($index < @typevalue) {
939 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700940 if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perches272a8972010-01-08 14:42:48 -0800941 last;
942 }
943 $index++;
944 }
945
946 return $index;
947}
948
Joe Perchesb7816552009-09-21 17:04:24 -0700949sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700950 my ($index) = @_;
951
952 while ($index > 0) {
953 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700954 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -0700955 last;
956 }
957 $index--;
958 }
959
960 return $index;
961}
962
963sub find_ending_index {
964 my ($index) = @_;
965
966 while ($index < @typevalue) {
967 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700968 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -0700969 last;
970 }
971 $index++;
972 }
973
974 return $index;
975}
976
Joe Perches2a7cb1d2015-11-06 16:30:52 -0800977sub get_subsystem_name {
978 my ($index) = @_;
979
980 my $start = find_starting_index($index);
981
982 my $subsystem = $typevalue[$start];
983 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
984 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
985 $subsystem =~ s/\s*$//;
986 $subsystem = $subsystem . "...";
987 }
988 return $subsystem;
989}
990
Joe Perches3c7385b2009-12-14 18:00:46 -0800991sub get_maintainer_role {
992 my ($index) = @_;
993
994 my $i;
995 my $start = find_starting_index($index);
996 my $end = find_ending_index($index);
997
Joe Perches0ede2742012-03-23 15:01:56 -0700998 my $role = "unknown";
Joe Perches2a7cb1d2015-11-06 16:30:52 -0800999 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001000
1001 for ($i = $start + 1; $i < $end; $i++) {
1002 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001003 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001004 my $ptype = $1;
1005 my $pvalue = $2;
1006 if ($ptype eq "S") {
1007 $role = $pvalue;
1008 }
1009 }
1010 }
1011
1012 $role = lc($role);
1013 if ($role eq "supported") {
1014 $role = "supporter";
1015 } elsif ($role eq "maintained") {
1016 $role = "maintainer";
1017 } elsif ($role eq "odd fixes") {
1018 $role = "odd fixer";
1019 } elsif ($role eq "orphan") {
1020 $role = "orphan minder";
1021 } elsif ($role eq "obsolete") {
1022 $role = "obsolete minder";
1023 } elsif ($role eq "buried alive in reporters") {
1024 $role = "chief penguin";
1025 }
1026
1027 return $role . ":" . $subsystem;
1028}
1029
1030sub get_list_role {
1031 my ($index) = @_;
1032
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001033 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001034
1035 if ($subsystem eq "THE REST") {
1036 $subsystem = "";
1037 }
1038
1039 return $subsystem;
1040}
1041
Joe Perchescb7301c2009-04-07 20:40:12 -07001042sub add_categories {
1043 my ($index) = @_;
1044
Joe Perchesb7816552009-09-21 17:04:24 -07001045 my $i;
1046 my $start = find_starting_index($index);
1047 my $end = find_ending_index($index);
1048
1049 push(@subsystem, $typevalue[$start]);
1050
1051 for ($i = $start + 1; $i < $end; $i++) {
1052 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001053 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001054 my $ptype = $1;
1055 my $pvalue = $2;
1056 if ($ptype eq "L") {
Joe Perches290603c12009-06-16 15:33:58 -07001057 my $list_address = $pvalue;
1058 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001059 my $list_role = get_list_role($i);
1060
1061 if ($list_role ne "") {
1062 $list_role = ":" . $list_role;
1063 }
Joe Perches290603c12009-06-16 15:33:58 -07001064 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1065 $list_address = $1;
1066 $list_additional = $2;
1067 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001068 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001069 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001070 if (!$hash_list_to{lc($list_address)}) {
1071 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001072 push(@list_to, [$list_address,
1073 "subscriber list${list_role}"]);
1074 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001075 }
1076 } else {
1077 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001078 if (!$hash_list_to{lc($list_address)}) {
1079 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001080 if ($list_additional =~ m/moderated/) {
1081 push(@list_to, [$list_address,
1082 "moderated list${list_role}"]);
1083 } else {
1084 push(@list_to, [$list_address,
1085 "open list${list_role}"]);
1086 }
Joe Perches683c6f82010-10-26 14:22:55 -07001087 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001088 }
1089 }
1090 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001091 my ($name, $address) = parse_email($pvalue);
1092 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001093 if ($i > 0) {
1094 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001095 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001096 if ($1 eq "P") {
1097 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001098 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001099 }
1100 }
1101 }
1102 }
Joe Perches0e70e832009-09-21 17:04:20 -07001103 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001104 my $role = get_maintainer_role($i);
1105 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001106 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001107 } elsif ($ptype eq "R") {
1108 my ($name, $address) = parse_email($pvalue);
1109 if ($name eq "") {
1110 if ($i > 0) {
1111 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001112 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001113 if ($1 eq "P") {
1114 $name = $2;
1115 $pvalue = format_email($name, $address, $email_usename);
1116 }
1117 }
1118 }
1119 }
1120 if ($email_reviewer) {
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001121 my $subsystem = get_subsystem_name($i);
1122 push_email_addresses($pvalue, "reviewer:$subsystem");
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001123 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001124 } elsif ($ptype eq "T") {
1125 push(@scm, $pvalue);
1126 } elsif ($ptype eq "W") {
1127 push(@web, $pvalue);
1128 } elsif ($ptype eq "S") {
1129 push(@status, $pvalue);
1130 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001131 }
1132 }
1133}
1134
Joe Perches11ecf532009-09-21 17:04:22 -07001135sub email_inuse {
1136 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001137
Joe Perches11ecf532009-09-21 17:04:22 -07001138 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001139 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1140 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001141
Joe Perches0e70e832009-09-21 17:04:20 -07001142 return 0;
1143}
1144
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001145sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001146 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001147
Joe Perches0e70e832009-09-21 17:04:20 -07001148 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001149
Joe Perchesb7816552009-09-21 17:04:24 -07001150 if ($address eq "") {
1151 return 0;
1152 }
1153
Joe Perches11ecf532009-09-21 17:04:22 -07001154 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001155 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001156 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001157 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001158 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001159 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001160 }
Joe Perchesb7816552009-09-21 17:04:24 -07001161
1162 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001163}
1164
1165sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001166 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001167
1168 my @address_list = ();
1169
Joe Perches5f2441e2009-06-16 15:34:02 -07001170 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001171 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001172 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001173 my $array_count = shift(@address_list);
1174 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001175 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001176 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001177 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001178 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001179 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1180 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001181 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001182}
1183
Joe Perches3c7385b2009-12-14 18:00:46 -08001184sub add_role {
1185 my ($line, $role) = @_;
1186
1187 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001188 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001189
1190 foreach my $entry (@email_to) {
1191 if ($email_remove_duplicates) {
1192 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001193 if (($name eq $entry_name || $address eq $entry_address)
1194 && ($role eq "" || !($entry->[1] =~ m/$role/))
1195 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001196 if ($entry->[1] eq "") {
1197 $entry->[1] = "$role";
1198 } else {
1199 $entry->[1] = "$entry->[1],$role";
1200 }
1201 }
1202 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001203 if ($email eq $entry->[0]
1204 && ($role eq "" || !($entry->[1] =~ m/$role/))
1205 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001206 if ($entry->[1] eq "") {
1207 $entry->[1] = "$role";
1208 } else {
1209 $entry->[1] = "$entry->[1],$role";
1210 }
1211 }
1212 }
1213 }
1214}
1215
Joe Perchescb7301c2009-04-07 20:40:12 -07001216sub which {
1217 my ($bin) = @_;
1218
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001219 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001220 if (-e "$path/$bin") {
1221 return "$path/$bin";
1222 }
1223 }
1224
1225 return "";
1226}
1227
Joe Perchesbcde44e2010-10-26 14:22:53 -07001228sub which_conf {
1229 my ($conf) = @_;
1230
1231 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1232 if (-e "$path/$conf") {
1233 return "$path/$conf";
1234 }
1235 }
1236
1237 return "";
1238}
1239
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001240sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001241 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001242
Joe Perches47abc722010-10-26 14:22:57 -07001243 my ($name, $address) = parse_email($line);
1244 my $email = format_email($name, $address, 1);
1245 my $real_name = $name;
1246 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001247
Joe Perches47abc722010-10-26 14:22:57 -07001248 if (exists $mailmap->{names}->{$email} ||
1249 exists $mailmap->{addresses}->{$email}) {
1250 if (exists $mailmap->{names}->{$email}) {
1251 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001252 }
Joe Perches47abc722010-10-26 14:22:57 -07001253 if (exists $mailmap->{addresses}->{$email}) {
1254 $real_address = $mailmap->{addresses}->{$email};
1255 }
1256 } else {
1257 if (exists $mailmap->{names}->{$address}) {
1258 $real_name = $mailmap->{names}->{$address};
1259 }
1260 if (exists $mailmap->{addresses}->{$address}) {
1261 $real_address = $mailmap->{addresses}->{$address};
1262 }
1263 }
1264 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001265}
1266
1267sub mailmap {
1268 my (@addresses) = @_;
1269
Joe Perchesb9e23312010-10-26 14:22:58 -07001270 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001271 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001272 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001273 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001274 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1275 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001276}
1277
1278sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001279 my %address_map;
1280 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001281
Joe Perches47abc722010-10-26 14:22:57 -07001282 foreach my $email (@emails) {
1283 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001284 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001285 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001286 $email = format_email($name, $address, 1);
1287 } else {
1288 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001289 }
Joe Perches47abc722010-10-26 14:22:57 -07001290 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001291}
1292
Joe Perches60db31a2009-12-14 18:00:50 -08001293sub git_execute_cmd {
1294 my ($cmd) = @_;
1295 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001296
Joe Perches60db31a2009-12-14 18:00:50 -08001297 my $output = `$cmd`;
1298 $output =~ s/^\s*//gm;
1299 @lines = split("\n", $output);
1300
1301 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001302}
1303
Joe Perches60db31a2009-12-14 18:00:50 -08001304sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001305 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001306 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001307
Joe Perches60db31a2009-12-14 18:00:50 -08001308 my $output = `$cmd`;
1309 @lines = split("\n", $output);
1310
1311 return @lines;
1312}
1313
Joe Perches683c6f82010-10-26 14:22:55 -07001314sub extract_formatted_signatures {
1315 my (@signature_lines) = @_;
1316
1317 my @type = @signature_lines;
1318
1319 s/\s*(.*):.*/$1/ for (@type);
1320
1321 # cut -f2- -d":"
1322 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1323
1324## Reformat email addresses (with names) to avoid badly written signatures
1325
1326 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001327 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001328 }
1329
1330 return (\@type, \@signature_lines);
1331}
1332
Joe Perches60db31a2009-12-14 18:00:50 -08001333sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001334 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001335 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001336 my @lines = ();
1337 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001338 my @authors = ();
1339 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001340
Joe Perches60db31a2009-12-14 18:00:50 -08001341 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001342
Joe Perches60db31a2009-12-14 18:00:50 -08001343 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001344 my $author_pattern = $VCS_cmds{"author_pattern"};
1345 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1346
1347 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001348
Joe Perches60db31a2009-12-14 18:00:50 -08001349 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001350
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001351 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001352 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001353 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001354
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001355# print("stats: <@stats>\n");
1356
1357 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001358
1359 save_commits_by_author(@lines) if ($interactive);
1360 save_commits_by_signer(@lines) if ($interactive);
1361
Joe Perches0e70e832009-09-21 17:04:20 -07001362 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001363 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001364 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001365
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001366 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001367 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001368
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001369 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001370}
1371
Joe Perches63ab52d2010-10-26 14:22:51 -07001372sub vcs_find_author {
1373 my ($cmd) = @_;
1374 my @lines = ();
1375
1376 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1377
1378 if (!$email_git_penguin_chiefs) {
1379 @lines = grep(!/${penguin_chiefs}/i, @lines);
1380 }
1381
1382 return @lines if !@lines;
1383
Joe Perches683c6f82010-10-26 14:22:55 -07001384 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001385 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001386 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1387 my $author = $1;
1388 my ($name, $address) = parse_email($author);
1389 $author = format_email($name, $address, 1);
1390 push(@authors, $author);
1391 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001392 }
1393
Joe Perches683c6f82010-10-26 14:22:55 -07001394 save_commits_by_author(@lines) if ($interactive);
1395 save_commits_by_signer(@lines) if ($interactive);
1396
1397 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001398}
1399
Joe Perches60db31a2009-12-14 18:00:50 -08001400sub vcs_save_commits {
1401 my ($cmd) = @_;
1402 my @lines = ();
1403 my @commits = ();
1404
1405 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1406
1407 foreach my $line (@lines) {
1408 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1409 push(@commits, $1);
1410 }
1411 }
1412
1413 return @commits;
1414}
1415
1416sub vcs_blame {
1417 my ($file) = @_;
1418 my $cmd;
1419 my @commits = ();
1420
1421 return @commits if (!(-f $file));
1422
1423 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1424 my @all_commits = ();
1425
1426 $cmd = $VCS_cmds{"blame_file_cmd"};
1427 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1428 @all_commits = vcs_save_commits($cmd);
1429
1430 foreach my $file_range_diff (@range) {
1431 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1432 my $diff_file = $1;
1433 my $diff_start = $2;
1434 my $diff_length = $3;
1435 next if ("$file" ne "$diff_file");
1436 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1437 push(@commits, $all_commits[$i]);
1438 }
1439 }
1440 } elsif (@range) {
1441 foreach my $file_range_diff (@range) {
1442 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1443 my $diff_file = $1;
1444 my $diff_start = $2;
1445 my $diff_length = $3;
1446 next if ("$file" ne "$diff_file");
1447 $cmd = $VCS_cmds{"blame_range_cmd"};
1448 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1449 push(@commits, vcs_save_commits($cmd));
1450 }
1451 } else {
1452 $cmd = $VCS_cmds{"blame_file_cmd"};
1453 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1454 @commits = vcs_save_commits($cmd);
1455 }
1456
Joe Perches63ab52d2010-10-26 14:22:51 -07001457 foreach my $commit (@commits) {
1458 $commit =~ s/^\^//g;
1459 }
1460
Joe Perches60db31a2009-12-14 18:00:50 -08001461 return @commits;
1462}
1463
1464my $printed_novcs = 0;
1465sub vcs_exists {
1466 %VCS_cmds = %VCS_cmds_git;
1467 return 1 if eval $VCS_cmds{"available"};
1468 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001469 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001470 %VCS_cmds = ();
1471 if (!$printed_novcs) {
1472 warn("$P: No supported VCS found. Add --nogit to options?\n");
1473 warn("Using a git repository produces better results.\n");
1474 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001475 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001476 $printed_novcs = 1;
1477 }
1478 return 0;
1479}
1480
Joe Perches683c6f82010-10-26 14:22:55 -07001481sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001482 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001483 return $vcs_used == 1;
1484}
1485
1486sub vcs_is_hg {
1487 return $vcs_used == 2;
1488}
1489
Joe Perches6ef1c522010-10-26 14:22:56 -07001490sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001491 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001492 my @list = @$list_ref;
1493
Joe Perches683c6f82010-10-26 14:22:55 -07001494 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001495
1496 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001497 my %authored;
1498 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001499 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001500 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001501 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001502 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1503 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001504 $authored{$count} = 0;
1505 $signed{$count} = 0;
1506 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001507 }
1508
1509 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001510 my $done = 0;
1511 my $print_options = 0;
1512 my $redraw = 1;
1513 while (!$done) {
1514 $count = 0;
1515 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001516 printf STDERR "\n%1s %2s %-65s",
1517 "*", "#", "email/list and role:stats";
1518 if ($email_git ||
1519 ($email_git_fallback && !$maintained) ||
1520 $email_git_blame) {
1521 print STDERR "auth sign";
1522 }
1523 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001524 foreach my $entry (@list) {
1525 my $email = $entry->[0];
1526 my $role = $entry->[1];
1527 my $sel = "";
1528 $sel = "*" if ($selected{$count});
1529 my $commit_author = $commit_author_hash{$email};
1530 my $commit_signer = $commit_signer_hash{$email};
1531 my $authored = 0;
1532 my $signed = 0;
1533 $authored++ for (@{$commit_author});
1534 $signed++ for (@{$commit_signer});
1535 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1536 printf STDERR "%4d %4d", $authored, $signed
1537 if ($authored > 0 || $signed > 0);
1538 printf STDERR "\n %s\n", $role;
1539 if ($authored{$count}) {
1540 my $commit_author = $commit_author_hash{$email};
1541 foreach my $ref (@{$commit_author}) {
1542 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001543 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001544 }
Joe Perches683c6f82010-10-26 14:22:55 -07001545 if ($signed{$count}) {
1546 my $commit_signer = $commit_signer_hash{$email};
1547 foreach my $ref (@{$commit_signer}) {
1548 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1549 }
1550 }
1551
1552 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001553 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001554 }
Joe Perches683c6f82010-10-26 14:22:55 -07001555 my $date_ref = \$email_git_since;
1556 $date_ref = \$email_hg_since if (vcs_is_hg());
1557 if ($print_options) {
1558 $print_options = 0;
1559 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001560 print STDERR <<EOT
1561
1562Version Control options:
1563g use git history [$email_git]
1564gf use git-fallback [$email_git_fallback]
1565b use git blame [$email_git_blame]
1566bs use blame signatures [$email_git_blame_signatures]
1567c# minimum commits [$email_git_min_signatures]
1568%# min percent [$email_git_min_percent]
1569d# history to use [$$date_ref]
1570x# max maintainers [$email_git_max_maintainers]
1571t all signature types [$email_git_all_signature_types]
1572m use .mailmap [$email_use_mailmap]
1573EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001574 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001575 print STDERR <<EOT
1576
1577Additional options:
15780 toggle all
1579tm toggle maintainers
1580tg toggle git entries
1581tl toggle open list entries
1582ts toggle subscriber list entries
1583f emails in file [$file_emails]
1584k keywords in file [$keywords]
1585r remove duplicates [$email_remove_duplicates]
1586p# pattern match depth [$pattern_depth]
1587EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001588 }
1589 print STDERR
1590"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1591
1592 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001593 chomp($input);
1594
Joe Perches683c6f82010-10-26 14:22:55 -07001595 $redraw = 1;
1596 my $rerun = 0;
1597 my @wish = split(/[, ]+/, $input);
1598 foreach my $nr (@wish) {
1599 $nr = lc($nr);
1600 my $sel = substr($nr, 0, 1);
1601 my $str = substr($nr, 1);
1602 my $val = 0;
1603 $val = $1 if $str =~ /^(\d+)$/;
1604
1605 if ($sel eq "y") {
1606 $interactive = 0;
1607 $done = 1;
1608 $output_rolestats = 0;
1609 $output_roles = 0;
1610 last;
1611 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1612 $selected{$nr - 1} = !$selected{$nr - 1};
1613 } elsif ($sel eq "*" || $sel eq '^') {
1614 my $toggle = 0;
1615 $toggle = 1 if ($sel eq '*');
1616 for (my $i = 0; $i < $count; $i++) {
1617 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001618 }
Joe Perches683c6f82010-10-26 14:22:55 -07001619 } elsif ($sel eq "0") {
1620 for (my $i = 0; $i < $count; $i++) {
1621 $selected{$i} = !$selected{$i};
1622 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001623 } elsif ($sel eq "t") {
1624 if (lc($str) eq "m") {
1625 for (my $i = 0; $i < $count; $i++) {
1626 $selected{$i} = !$selected{$i}
1627 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1628 }
1629 } elsif (lc($str) eq "g") {
1630 for (my $i = 0; $i < $count; $i++) {
1631 $selected{$i} = !$selected{$i}
1632 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1633 }
1634 } elsif (lc($str) eq "l") {
1635 for (my $i = 0; $i < $count; $i++) {
1636 $selected{$i} = !$selected{$i}
1637 if ($list[$i]->[1] =~ /^(open list)/i);
1638 }
1639 } elsif (lc($str) eq "s") {
1640 for (my $i = 0; $i < $count; $i++) {
1641 $selected{$i} = !$selected{$i}
1642 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1643 }
1644 }
Joe Perches683c6f82010-10-26 14:22:55 -07001645 } elsif ($sel eq "a") {
1646 if ($val > 0 && $val <= $count) {
1647 $authored{$val - 1} = !$authored{$val - 1};
1648 } elsif ($str eq '*' || $str eq '^') {
1649 my $toggle = 0;
1650 $toggle = 1 if ($str eq '*');
1651 for (my $i = 0; $i < $count; $i++) {
1652 $authored{$i} = $toggle;
1653 }
1654 }
1655 } elsif ($sel eq "s") {
1656 if ($val > 0 && $val <= $count) {
1657 $signed{$val - 1} = !$signed{$val - 1};
1658 } elsif ($str eq '*' || $str eq '^') {
1659 my $toggle = 0;
1660 $toggle = 1 if ($str eq '*');
1661 for (my $i = 0; $i < $count; $i++) {
1662 $signed{$i} = $toggle;
1663 }
1664 }
1665 } elsif ($sel eq "o") {
1666 $print_options = 1;
1667 $redraw = 1;
1668 } elsif ($sel eq "g") {
1669 if ($str eq "f") {
1670 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001671 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001672 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001673 }
Joe Perches683c6f82010-10-26 14:22:55 -07001674 $rerun = 1;
1675 } elsif ($sel eq "b") {
1676 if ($str eq "s") {
1677 bool_invert(\$email_git_blame_signatures);
1678 } else {
1679 bool_invert(\$email_git_blame);
1680 }
1681 $rerun = 1;
1682 } elsif ($sel eq "c") {
1683 if ($val > 0) {
1684 $email_git_min_signatures = $val;
1685 $rerun = 1;
1686 }
1687 } elsif ($sel eq "x") {
1688 if ($val > 0) {
1689 $email_git_max_maintainers = $val;
1690 $rerun = 1;
1691 }
1692 } elsif ($sel eq "%") {
1693 if ($str ne "" && $val >= 0) {
1694 $email_git_min_percent = $val;
1695 $rerun = 1;
1696 }
1697 } elsif ($sel eq "d") {
1698 if (vcs_is_git()) {
1699 $email_git_since = $str;
1700 } elsif (vcs_is_hg()) {
1701 $email_hg_since = $str;
1702 }
1703 $rerun = 1;
1704 } elsif ($sel eq "t") {
1705 bool_invert(\$email_git_all_signature_types);
1706 $rerun = 1;
1707 } elsif ($sel eq "f") {
1708 bool_invert(\$file_emails);
1709 $rerun = 1;
1710 } elsif ($sel eq "r") {
1711 bool_invert(\$email_remove_duplicates);
1712 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001713 } elsif ($sel eq "m") {
1714 bool_invert(\$email_use_mailmap);
1715 read_mailmap();
1716 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001717 } elsif ($sel eq "k") {
1718 bool_invert(\$keywords);
1719 $rerun = 1;
1720 } elsif ($sel eq "p") {
1721 if ($str ne "" && $val >= 0) {
1722 $pattern_depth = $val;
1723 $rerun = 1;
1724 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001725 } elsif ($sel eq "h" || $sel eq "?") {
1726 print STDERR <<EOT
1727
1728Interactive mode allows you to select the various maintainers, submitters,
1729commit signers and mailing lists that could be CC'd on a patch.
1730
1731Any *'d entry is selected.
1732
Joe Perches47abc722010-10-26 14:22:57 -07001733If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001734history of files in the patch. Also, each line of the current file can
1735be matched to its commit author and that commits signers with blame.
1736
1737Various knobs exist to control the length of time for active commit
1738tracking, the maximum number of commit authors and signers to add,
1739and such.
1740
1741Enter selections at the prompt until you are satisfied that the selected
1742maintainers are appropriate. You may enter multiple selections separated
1743by either commas or spaces.
1744
1745EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001746 } else {
1747 print STDERR "invalid option: '$nr'\n";
1748 $redraw = 0;
1749 }
1750 }
1751 if ($rerun) {
1752 print STDERR "git-blame can be very slow, please have patience..."
1753 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001754 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001755 }
1756 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001757
1758 #drop not selected entries
1759 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001760 my @new_emailto = ();
1761 foreach my $entry (@list) {
1762 if ($selected{$count}) {
1763 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001764 }
1765 $count++;
1766 }
Joe Perches683c6f82010-10-26 14:22:55 -07001767 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001768}
1769
Joe Perches683c6f82010-10-26 14:22:55 -07001770sub bool_invert {
1771 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001772
Joe Perches683c6f82010-10-26 14:22:55 -07001773 if ($$bool_ref) {
1774 $$bool_ref = 0;
1775 } else {
1776 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001777 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001778}
1779
Joe Perchesb9e23312010-10-26 14:22:58 -07001780sub deduplicate_email {
1781 my ($email) = @_;
1782
1783 my $matched = 0;
1784 my ($name, $address) = parse_email($email);
1785 $email = format_email($name, $address, 1);
1786 $email = mailmap_email($email);
1787
1788 return $email if (!$email_remove_duplicates);
1789
1790 ($name, $address) = parse_email($email);
1791
Joe Perchesfae99202010-10-26 14:22:58 -07001792 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001793 $name = $deduplicate_name_hash{lc($name)}->[0];
1794 $address = $deduplicate_name_hash{lc($name)}->[1];
1795 $matched = 1;
1796 } elsif ($deduplicate_address_hash{lc($address)}) {
1797 $name = $deduplicate_address_hash{lc($address)}->[0];
1798 $address = $deduplicate_address_hash{lc($address)}->[1];
1799 $matched = 1;
1800 }
1801 if (!$matched) {
1802 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1803 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1804 }
1805 $email = format_email($name, $address, 1);
1806 $email = mailmap_email($email);
1807 return $email;
1808}
1809
Joe Perches683c6f82010-10-26 14:22:55 -07001810sub save_commits_by_author {
1811 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001812
Joe Perches683c6f82010-10-26 14:22:55 -07001813 my @authors = ();
1814 my @commits = ();
1815 my @subjects = ();
1816
1817 foreach my $line (@lines) {
1818 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1819 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001820 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001821 push(@authors, $author);
1822 }
1823 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1824 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1825 }
1826
1827 for (my $i = 0; $i < @authors; $i++) {
1828 my $exists = 0;
1829 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1830 if (@{$ref}[0] eq $commits[$i] &&
1831 @{$ref}[1] eq $subjects[$i]) {
1832 $exists = 1;
1833 last;
1834 }
1835 }
1836 if (!$exists) {
1837 push(@{$commit_author_hash{$authors[$i]}},
1838 [ ($commits[$i], $subjects[$i]) ]);
1839 }
1840 }
1841}
1842
1843sub save_commits_by_signer {
1844 my (@lines) = @_;
1845
1846 my $commit = "";
1847 my $subject = "";
1848
1849 foreach my $line (@lines) {
1850 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1851 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1852 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1853 my @signatures = ($line);
1854 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1855 my @types = @$types_ref;
1856 my @signers = @$signers_ref;
1857
1858 my $type = $types[0];
1859 my $signer = $signers[0];
1860
Joe Perchesb9e23312010-10-26 14:22:58 -07001861 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001862
Joe Perches683c6f82010-10-26 14:22:55 -07001863 my $exists = 0;
1864 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1865 if (@{$ref}[0] eq $commit &&
1866 @{$ref}[1] eq $subject &&
1867 @{$ref}[2] eq $type) {
1868 $exists = 1;
1869 last;
1870 }
1871 }
1872 if (!$exists) {
1873 push(@{$commit_signer_hash{$signer}},
1874 [ ($commit, $subject, $type) ]);
1875 }
1876 }
1877 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001878}
1879
Joe Perches60db31a2009-12-14 18:00:50 -08001880sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001881 my ($role, $divisor, @lines) = @_;
1882
1883 my %hash;
1884 my $count = 0;
1885
Joe Perchesa8af2432009-12-14 18:00:49 -08001886 return if (@lines <= 0);
1887
1888 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001889 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001890 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001891 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001892
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001893 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001894
Joe Perches63ab52d2010-10-26 14:22:51 -07001895 return if (@lines <= 0);
1896
Joe Perches0e70e832009-09-21 17:04:20 -07001897 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001898
Joe Perches11ecf532009-09-21 17:04:22 -07001899 # uniq -c
1900 $hash{$_}++ for @lines;
1901
1902 # sort -rn
1903 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1904 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001905 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001906
Joe Perchesa8af2432009-12-14 18:00:49 -08001907 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07001908 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07001909 $count++;
1910 last if ($sign_offs < $email_git_min_signatures ||
1911 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001912 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001913 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001914 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001915 my $fmt_percent = sprintf("%.0f", $percent);
1916 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1917 } else {
1918 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001919 }
Joe Perchesf5492662009-09-21 17:04:13 -07001920 }
1921}
1922
Joe Perches60db31a2009-12-14 18:00:50 -08001923sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001924 my ($file) = @_;
1925
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001926 my $authors_ref;
1927 my $signers_ref;
1928 my $stats_ref;
1929 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001930 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001931 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001932 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001933
Joe Perches683c6f82010-10-26 14:22:55 -07001934 $vcs_used = vcs_exists();
1935 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001936
Joe Perches60db31a2009-12-14 18:00:50 -08001937 my $cmd = $VCS_cmds{"find_signers_cmd"};
1938 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1939
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001940 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1941
1942 @signers = @{$signers_ref} if defined $signers_ref;
1943 @authors = @{$authors_ref} if defined $authors_ref;
1944 @stats = @{$stats_ref} if defined $stats_ref;
1945
1946# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07001947
1948 foreach my $signer (@signers) {
1949 $signer = deduplicate_email($signer);
1950 }
1951
Joe Perches60db31a2009-12-14 18:00:50 -08001952 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001953 vcs_assign("authored", $commits, @authors);
1954 if ($#authors == $#stats) {
1955 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1956 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
1957
1958 my $added = 0;
1959 my $deleted = 0;
1960 for (my $i = 0; $i <= $#stats; $i++) {
1961 if ($stats[$i] =~ /$stat_pattern/) {
1962 $added += $1;
1963 $deleted += $2;
1964 }
1965 }
1966 my @tmp_authors = uniq(@authors);
1967 foreach my $author (@tmp_authors) {
1968 $author = deduplicate_email($author);
1969 }
1970 @tmp_authors = uniq(@tmp_authors);
1971 my @list_added = ();
1972 my @list_deleted = ();
1973 foreach my $author (@tmp_authors) {
1974 my $auth_added = 0;
1975 my $auth_deleted = 0;
1976 for (my $i = 0; $i <= $#stats; $i++) {
1977 if ($author eq deduplicate_email($authors[$i]) &&
1978 $stats[$i] =~ /$stat_pattern/) {
1979 $auth_added += $1;
1980 $auth_deleted += $2;
1981 }
1982 }
1983 for (my $i = 0; $i < $auth_added; $i++) {
1984 push(@list_added, $author);
1985 }
1986 for (my $i = 0; $i < $auth_deleted; $i++) {
1987 push(@list_deleted, $author);
1988 }
1989 }
1990 vcs_assign("added_lines", $added, @list_added);
1991 vcs_assign("removed_lines", $deleted, @list_deleted);
1992 }
Joe Perchesa8af2432009-12-14 18:00:49 -08001993}
1994
Joe Perches60db31a2009-12-14 18:00:50 -08001995sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001996 my ($file) = @_;
1997
Joe Perches60db31a2009-12-14 18:00:50 -08001998 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001999 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002000 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002001 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002002 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07002003
Joe Perches683c6f82010-10-26 14:22:55 -07002004 $vcs_used = vcs_exists();
2005 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002006
Joe Perches63ab52d2010-10-26 14:22:51 -07002007 @all_commits = vcs_blame($file);
2008 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002009 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002010 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002011
Joe Perches683c6f82010-10-26 14:22:55 -07002012 if ($email_git_blame_signatures) {
2013 if (vcs_is_hg()) {
2014 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002015 my $commit_authors_ref;
2016 my $commit_signers_ref;
2017 my $stats_ref;
2018 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002019 my @commit_signers = ();
2020 my $commit = join(" -r ", @commits);
2021 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002022
Joe Perches683c6f82010-10-26 14:22:55 -07002023 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2024 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002025
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002026 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2027 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2028 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002029
Joe Perches683c6f82010-10-26 14:22:55 -07002030 push(@signers, @commit_signers);
2031 } else {
2032 foreach my $commit (@commits) {
2033 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002034 my $commit_authors_ref;
2035 my $commit_signers_ref;
2036 my $stats_ref;
2037 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002038 my @commit_signers = ();
2039 my $cmd;
2040
2041 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2042 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2043
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002044 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2045 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2046 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002047
2048 push(@signers, @commit_signers);
2049 }
2050 }
Joe Perchesf5492662009-09-21 17:04:13 -07002051 }
2052
Joe Perchesa8af2432009-12-14 18:00:49 -08002053 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002054 if ($output_rolestats) {
2055 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002056 if (vcs_is_hg()) {{ # Double brace for last exit
2057 my $commit_count;
2058 my @commit_signers = ();
2059 @commits = uniq(@commits);
2060 @commits = sort(@commits);
2061 my $commit = join(" -r ", @commits);
2062 my $cmd;
2063
2064 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2065 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2066
2067 my @lines = ();
2068
2069 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2070
2071 if (!$email_git_penguin_chiefs) {
2072 @lines = grep(!/${penguin_chiefs}/i, @lines);
2073 }
2074
2075 last if !@lines;
2076
2077 my @authors = ();
2078 foreach my $line (@lines) {
2079 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2080 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002081 $author = deduplicate_email($author);
2082 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002083 }
2084 }
2085
2086 save_commits_by_author(@lines) if ($interactive);
2087 save_commits_by_signer(@lines) if ($interactive);
2088
2089 push(@signers, @authors);
2090 }}
2091 else {
2092 foreach my $commit (@commits) {
2093 my $i;
2094 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2095 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2096 my @author = vcs_find_author($cmd);
2097 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002098
2099 my $formatted_author = deduplicate_email($author[0]);
2100
Joe Perches683c6f82010-10-26 14:22:55 -07002101 my $count = grep(/$commit/, @all_commits);
2102 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002103 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002104 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002105 }
2106 }
2107 if (@blame_signers) {
2108 vcs_assign("authored lines", $total_lines, @blame_signers);
2109 }
2110 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002111 foreach my $signer (@signers) {
2112 $signer = deduplicate_email($signer);
2113 }
Joe Perches60db31a2009-12-14 18:00:50 -08002114 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002115 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002116 foreach my $signer (@signers) {
2117 $signer = deduplicate_email($signer);
2118 }
Joe Perches60db31a2009-12-14 18:00:50 -08002119 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002120 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002121}
2122
2123sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002124 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002125
2126 my %saw;
2127 @parms = grep(!$saw{$_}++, @parms);
2128 return @parms;
2129}
2130
2131sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002132 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002133
2134 my %saw;
2135 @parms = sort @parms;
2136 @parms = grep(!$saw{$_}++, @parms);
2137 return @parms;
2138}
2139
Joe Perches03372db2010-03-05 13:43:00 -08002140sub clean_file_emails {
2141 my (@file_emails) = @_;
2142 my @fmt_emails = ();
2143
2144 foreach my $email (@file_emails) {
2145 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2146 my ($name, $address) = parse_email($email);
2147 if ($name eq '"[,\.]"') {
2148 $name = "";
2149 }
2150
2151 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2152 if (@nw > 2) {
2153 my $first = $nw[@nw - 3];
2154 my $middle = $nw[@nw - 2];
2155 my $last = $nw[@nw - 1];
2156
2157 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2158 (length($first) == 2 && substr($first, -1) eq ".")) ||
2159 (length($middle) == 1 ||
2160 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2161 $name = "$first $middle $last";
2162 } else {
2163 $name = "$middle $last";
2164 }
2165 }
2166
2167 if (substr($name, -1) =~ /[,\.]/) {
2168 $name = substr($name, 0, length($name) - 1);
2169 } elsif (substr($name, -2) =~ /[,\.]"/) {
2170 $name = substr($name, 0, length($name) - 2) . '"';
2171 }
2172
2173 if (substr($name, 0, 1) =~ /[,\.]/) {
2174 $name = substr($name, 1, length($name) - 1);
2175 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2176 $name = '"' . substr($name, 2, length($name) - 2);
2177 }
2178
2179 my $fmt_email = format_email($name, $address, $email_usename);
2180 push(@fmt_emails, $fmt_email);
2181 }
2182 return @fmt_emails;
2183}
2184
Joe Perches3c7385b2009-12-14 18:00:46 -08002185sub merge_email {
2186 my @lines;
2187 my %saw;
2188
2189 for (@_) {
2190 my ($address, $role) = @$_;
2191 if (!$saw{$address}) {
2192 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002193 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002194 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002195 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002196 }
2197 $saw{$address} = 1;
2198 }
2199 }
2200
2201 return @lines;
2202}
2203
Joe Perchescb7301c2009-04-07 20:40:12 -07002204sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002205 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002206
2207 if ($output_multiline) {
2208 foreach my $line (@parms) {
2209 print("${line}\n");
2210 }
2211 } else {
2212 print(join($output_separator, @parms));
2213 print("\n");
2214 }
2215}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002216
2217my $rfc822re;
2218
2219sub make_rfc822re {
2220# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2221# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2222# This regexp will only work on addresses which have had comments stripped
2223# and replaced with rfc822_lwsp.
2224
2225 my $specials = '()<>@,;:\\\\".\\[\\]';
2226 my $controls = '\\000-\\037\\177';
2227
2228 my $dtext = "[^\\[\\]\\r\\\\]";
2229 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2230
2231 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2232
2233# Use zero-width assertion to spot the limit of an atom. A simple
2234# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2235 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2236 my $word = "(?:$atom|$quoted_string)";
2237 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2238
2239 my $sub_domain = "(?:$atom|$domain_literal)";
2240 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2241
2242 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2243
2244 my $phrase = "$word*";
2245 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2246 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2247 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2248
2249 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2250 my $address = "(?:$mailbox|$group)";
2251
2252 return "$rfc822_lwsp*$address";
2253}
2254
2255sub rfc822_strip_comments {
2256 my $s = shift;
2257# Recursively remove comments, and replace with a single space. The simpler
2258# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2259# chars in atoms, for example.
2260
2261 while ($s =~ s/^((?:[^"\\]|\\.)*
2262 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2263 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2264 return $s;
2265}
2266
2267# valid: returns true if the parameter is an RFC822 valid address
2268#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002269sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002270 my $s = rfc822_strip_comments(shift);
2271
2272 if (!$rfc822re) {
2273 $rfc822re = make_rfc822re();
2274 }
2275
2276 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2277}
2278
2279# validlist: In scalar context, returns true if the parameter is an RFC822
2280# valid list of addresses.
2281#
2282# In list context, returns an empty list on failure (an invalid
2283# address was found); otherwise a list whose first element is the
2284# number of addresses found and whose remaining elements are the
2285# addresses. This is needed to disambiguate failure (invalid)
2286# from success with no addresses found, because an empty string is
2287# a valid list.
2288
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002289sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002290 my $s = rfc822_strip_comments(shift);
2291
2292 if (!$rfc822re) {
2293 $rfc822re = make_rfc822re();
2294 }
2295 # * null list items are valid according to the RFC
2296 # * the '1' business is to aid in distinguishing failure from no results
2297
2298 my @r;
2299 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2300 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002301 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002302 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002303 }
2304 return wantarray ? (scalar(@r), @r) : 1;
2305 }
Joe Perches60db31a2009-12-14 18:00:50 -08002306 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002307}