blob: 145f1bf6472e07fe1439b27d4fedd13feeaf83aa [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 Norriscc7ff0e2015-11-06 16:30:43 -0800816 [--email --nogit --git-fallback --m --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 Perches3c7385b2009-12-14 18:00:46 -0800977sub get_maintainer_role {
978 my ($index) = @_;
979
980 my $i;
981 my $start = find_starting_index($index);
982 my $end = find_ending_index($index);
983
Joe Perches0ede2742012-03-23 15:01:56 -0700984 my $role = "unknown";
Joe Perches3c7385b2009-12-14 18:00:46 -0800985 my $subsystem = $typevalue[$start];
Joe Perches364f68d2015-06-25 15:01:52 -0700986 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
987 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
Joe Perches3c7385b2009-12-14 18:00:46 -0800988 $subsystem =~ s/\s*$//;
989 $subsystem = $subsystem . "...";
990 }
991
992 for ($i = $start + 1; $i < $end; $i++) {
993 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700994 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800995 my $ptype = $1;
996 my $pvalue = $2;
997 if ($ptype eq "S") {
998 $role = $pvalue;
999 }
1000 }
1001 }
1002
1003 $role = lc($role);
1004 if ($role eq "supported") {
1005 $role = "supporter";
1006 } elsif ($role eq "maintained") {
1007 $role = "maintainer";
1008 } elsif ($role eq "odd fixes") {
1009 $role = "odd fixer";
1010 } elsif ($role eq "orphan") {
1011 $role = "orphan minder";
1012 } elsif ($role eq "obsolete") {
1013 $role = "obsolete minder";
1014 } elsif ($role eq "buried alive in reporters") {
1015 $role = "chief penguin";
1016 }
1017
1018 return $role . ":" . $subsystem;
1019}
1020
1021sub get_list_role {
1022 my ($index) = @_;
1023
1024 my $i;
1025 my $start = find_starting_index($index);
1026 my $end = find_ending_index($index);
1027
1028 my $subsystem = $typevalue[$start];
Joe Perches364f68d2015-06-25 15:01:52 -07001029 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
1030 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
Joe Perches3c7385b2009-12-14 18:00:46 -08001031 $subsystem =~ s/\s*$//;
1032 $subsystem = $subsystem . "...";
1033 }
1034
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) {
1121 push_email_addresses($pvalue, 'reviewer');
1122 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001123 } elsif ($ptype eq "T") {
1124 push(@scm, $pvalue);
1125 } elsif ($ptype eq "W") {
1126 push(@web, $pvalue);
1127 } elsif ($ptype eq "S") {
1128 push(@status, $pvalue);
1129 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001130 }
1131 }
1132}
1133
Joe Perches11ecf532009-09-21 17:04:22 -07001134sub email_inuse {
1135 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001136
Joe Perches11ecf532009-09-21 17:04:22 -07001137 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001138 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1139 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001140
Joe Perches0e70e832009-09-21 17:04:20 -07001141 return 0;
1142}
1143
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001144sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001145 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001146
Joe Perches0e70e832009-09-21 17:04:20 -07001147 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001148
Joe Perchesb7816552009-09-21 17:04:24 -07001149 if ($address eq "") {
1150 return 0;
1151 }
1152
Joe Perches11ecf532009-09-21 17:04:22 -07001153 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001154 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001155 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001156 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001157 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001158 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001159 }
Joe Perchesb7816552009-09-21 17:04:24 -07001160
1161 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001162}
1163
1164sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001165 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001166
1167 my @address_list = ();
1168
Joe Perches5f2441e2009-06-16 15:34:02 -07001169 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001170 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001171 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001172 my $array_count = shift(@address_list);
1173 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001174 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001175 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001176 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001177 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001178 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1179 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001180 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001181}
1182
Joe Perches3c7385b2009-12-14 18:00:46 -08001183sub add_role {
1184 my ($line, $role) = @_;
1185
1186 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001187 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001188
1189 foreach my $entry (@email_to) {
1190 if ($email_remove_duplicates) {
1191 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001192 if (($name eq $entry_name || $address eq $entry_address)
1193 && ($role eq "" || !($entry->[1] =~ m/$role/))
1194 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001195 if ($entry->[1] eq "") {
1196 $entry->[1] = "$role";
1197 } else {
1198 $entry->[1] = "$entry->[1],$role";
1199 }
1200 }
1201 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001202 if ($email eq $entry->[0]
1203 && ($role eq "" || !($entry->[1] =~ m/$role/))
1204 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001205 if ($entry->[1] eq "") {
1206 $entry->[1] = "$role";
1207 } else {
1208 $entry->[1] = "$entry->[1],$role";
1209 }
1210 }
1211 }
1212 }
1213}
1214
Joe Perchescb7301c2009-04-07 20:40:12 -07001215sub which {
1216 my ($bin) = @_;
1217
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001218 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001219 if (-e "$path/$bin") {
1220 return "$path/$bin";
1221 }
1222 }
1223
1224 return "";
1225}
1226
Joe Perchesbcde44e2010-10-26 14:22:53 -07001227sub which_conf {
1228 my ($conf) = @_;
1229
1230 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1231 if (-e "$path/$conf") {
1232 return "$path/$conf";
1233 }
1234 }
1235
1236 return "";
1237}
1238
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001239sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001240 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001241
Joe Perches47abc722010-10-26 14:22:57 -07001242 my ($name, $address) = parse_email($line);
1243 my $email = format_email($name, $address, 1);
1244 my $real_name = $name;
1245 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001246
Joe Perches47abc722010-10-26 14:22:57 -07001247 if (exists $mailmap->{names}->{$email} ||
1248 exists $mailmap->{addresses}->{$email}) {
1249 if (exists $mailmap->{names}->{$email}) {
1250 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001251 }
Joe Perches47abc722010-10-26 14:22:57 -07001252 if (exists $mailmap->{addresses}->{$email}) {
1253 $real_address = $mailmap->{addresses}->{$email};
1254 }
1255 } else {
1256 if (exists $mailmap->{names}->{$address}) {
1257 $real_name = $mailmap->{names}->{$address};
1258 }
1259 if (exists $mailmap->{addresses}->{$address}) {
1260 $real_address = $mailmap->{addresses}->{$address};
1261 }
1262 }
1263 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001264}
1265
1266sub mailmap {
1267 my (@addresses) = @_;
1268
Joe Perchesb9e23312010-10-26 14:22:58 -07001269 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001270 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001271 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001272 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001273 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1274 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001275}
1276
1277sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001278 my %address_map;
1279 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001280
Joe Perches47abc722010-10-26 14:22:57 -07001281 foreach my $email (@emails) {
1282 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001283 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001284 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001285 $email = format_email($name, $address, 1);
1286 } else {
1287 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001288 }
Joe Perches47abc722010-10-26 14:22:57 -07001289 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001290}
1291
Joe Perches60db31a2009-12-14 18:00:50 -08001292sub git_execute_cmd {
1293 my ($cmd) = @_;
1294 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001295
Joe Perches60db31a2009-12-14 18:00:50 -08001296 my $output = `$cmd`;
1297 $output =~ s/^\s*//gm;
1298 @lines = split("\n", $output);
1299
1300 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001301}
1302
Joe Perches60db31a2009-12-14 18:00:50 -08001303sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001304 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001305 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001306
Joe Perches60db31a2009-12-14 18:00:50 -08001307 my $output = `$cmd`;
1308 @lines = split("\n", $output);
1309
1310 return @lines;
1311}
1312
Joe Perches683c6f82010-10-26 14:22:55 -07001313sub extract_formatted_signatures {
1314 my (@signature_lines) = @_;
1315
1316 my @type = @signature_lines;
1317
1318 s/\s*(.*):.*/$1/ for (@type);
1319
1320 # cut -f2- -d":"
1321 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1322
1323## Reformat email addresses (with names) to avoid badly written signatures
1324
1325 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001326 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001327 }
1328
1329 return (\@type, \@signature_lines);
1330}
1331
Joe Perches60db31a2009-12-14 18:00:50 -08001332sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001333 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001334 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001335 my @lines = ();
1336 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001337 my @authors = ();
1338 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001339
Joe Perches60db31a2009-12-14 18:00:50 -08001340 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001341
Joe Perches60db31a2009-12-14 18:00:50 -08001342 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001343 my $author_pattern = $VCS_cmds{"author_pattern"};
1344 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1345
1346 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001347
Joe Perches60db31a2009-12-14 18:00:50 -08001348 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001349
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001350 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001351 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001352 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001353
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001354# print("stats: <@stats>\n");
1355
1356 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001357
1358 save_commits_by_author(@lines) if ($interactive);
1359 save_commits_by_signer(@lines) if ($interactive);
1360
Joe Perches0e70e832009-09-21 17:04:20 -07001361 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001362 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001363 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001364
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001365 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001366 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001367
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001368 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001369}
1370
Joe Perches63ab52d2010-10-26 14:22:51 -07001371sub vcs_find_author {
1372 my ($cmd) = @_;
1373 my @lines = ();
1374
1375 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1376
1377 if (!$email_git_penguin_chiefs) {
1378 @lines = grep(!/${penguin_chiefs}/i, @lines);
1379 }
1380
1381 return @lines if !@lines;
1382
Joe Perches683c6f82010-10-26 14:22:55 -07001383 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001384 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001385 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1386 my $author = $1;
1387 my ($name, $address) = parse_email($author);
1388 $author = format_email($name, $address, 1);
1389 push(@authors, $author);
1390 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001391 }
1392
Joe Perches683c6f82010-10-26 14:22:55 -07001393 save_commits_by_author(@lines) if ($interactive);
1394 save_commits_by_signer(@lines) if ($interactive);
1395
1396 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001397}
1398
Joe Perches60db31a2009-12-14 18:00:50 -08001399sub vcs_save_commits {
1400 my ($cmd) = @_;
1401 my @lines = ();
1402 my @commits = ();
1403
1404 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1405
1406 foreach my $line (@lines) {
1407 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1408 push(@commits, $1);
1409 }
1410 }
1411
1412 return @commits;
1413}
1414
1415sub vcs_blame {
1416 my ($file) = @_;
1417 my $cmd;
1418 my @commits = ();
1419
1420 return @commits if (!(-f $file));
1421
1422 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1423 my @all_commits = ();
1424
1425 $cmd = $VCS_cmds{"blame_file_cmd"};
1426 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1427 @all_commits = vcs_save_commits($cmd);
1428
1429 foreach my $file_range_diff (@range) {
1430 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1431 my $diff_file = $1;
1432 my $diff_start = $2;
1433 my $diff_length = $3;
1434 next if ("$file" ne "$diff_file");
1435 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1436 push(@commits, $all_commits[$i]);
1437 }
1438 }
1439 } elsif (@range) {
1440 foreach my $file_range_diff (@range) {
1441 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1442 my $diff_file = $1;
1443 my $diff_start = $2;
1444 my $diff_length = $3;
1445 next if ("$file" ne "$diff_file");
1446 $cmd = $VCS_cmds{"blame_range_cmd"};
1447 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1448 push(@commits, vcs_save_commits($cmd));
1449 }
1450 } else {
1451 $cmd = $VCS_cmds{"blame_file_cmd"};
1452 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1453 @commits = vcs_save_commits($cmd);
1454 }
1455
Joe Perches63ab52d2010-10-26 14:22:51 -07001456 foreach my $commit (@commits) {
1457 $commit =~ s/^\^//g;
1458 }
1459
Joe Perches60db31a2009-12-14 18:00:50 -08001460 return @commits;
1461}
1462
1463my $printed_novcs = 0;
1464sub vcs_exists {
1465 %VCS_cmds = %VCS_cmds_git;
1466 return 1 if eval $VCS_cmds{"available"};
1467 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001468 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001469 %VCS_cmds = ();
1470 if (!$printed_novcs) {
1471 warn("$P: No supported VCS found. Add --nogit to options?\n");
1472 warn("Using a git repository produces better results.\n");
1473 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001474 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001475 $printed_novcs = 1;
1476 }
1477 return 0;
1478}
1479
Joe Perches683c6f82010-10-26 14:22:55 -07001480sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001481 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001482 return $vcs_used == 1;
1483}
1484
1485sub vcs_is_hg {
1486 return $vcs_used == 2;
1487}
1488
Joe Perches6ef1c522010-10-26 14:22:56 -07001489sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001490 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001491 my @list = @$list_ref;
1492
Joe Perches683c6f82010-10-26 14:22:55 -07001493 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001494
1495 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001496 my %authored;
1497 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001498 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001499 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001500 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001501 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1502 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001503 $authored{$count} = 0;
1504 $signed{$count} = 0;
1505 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001506 }
1507
1508 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001509 my $done = 0;
1510 my $print_options = 0;
1511 my $redraw = 1;
1512 while (!$done) {
1513 $count = 0;
1514 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001515 printf STDERR "\n%1s %2s %-65s",
1516 "*", "#", "email/list and role:stats";
1517 if ($email_git ||
1518 ($email_git_fallback && !$maintained) ||
1519 $email_git_blame) {
1520 print STDERR "auth sign";
1521 }
1522 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001523 foreach my $entry (@list) {
1524 my $email = $entry->[0];
1525 my $role = $entry->[1];
1526 my $sel = "";
1527 $sel = "*" if ($selected{$count});
1528 my $commit_author = $commit_author_hash{$email};
1529 my $commit_signer = $commit_signer_hash{$email};
1530 my $authored = 0;
1531 my $signed = 0;
1532 $authored++ for (@{$commit_author});
1533 $signed++ for (@{$commit_signer});
1534 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1535 printf STDERR "%4d %4d", $authored, $signed
1536 if ($authored > 0 || $signed > 0);
1537 printf STDERR "\n %s\n", $role;
1538 if ($authored{$count}) {
1539 my $commit_author = $commit_author_hash{$email};
1540 foreach my $ref (@{$commit_author}) {
1541 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001542 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001543 }
Joe Perches683c6f82010-10-26 14:22:55 -07001544 if ($signed{$count}) {
1545 my $commit_signer = $commit_signer_hash{$email};
1546 foreach my $ref (@{$commit_signer}) {
1547 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1548 }
1549 }
1550
1551 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001552 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001553 }
Joe Perches683c6f82010-10-26 14:22:55 -07001554 my $date_ref = \$email_git_since;
1555 $date_ref = \$email_hg_since if (vcs_is_hg());
1556 if ($print_options) {
1557 $print_options = 0;
1558 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001559 print STDERR <<EOT
1560
1561Version Control options:
1562g use git history [$email_git]
1563gf use git-fallback [$email_git_fallback]
1564b use git blame [$email_git_blame]
1565bs use blame signatures [$email_git_blame_signatures]
1566c# minimum commits [$email_git_min_signatures]
1567%# min percent [$email_git_min_percent]
1568d# history to use [$$date_ref]
1569x# max maintainers [$email_git_max_maintainers]
1570t all signature types [$email_git_all_signature_types]
1571m use .mailmap [$email_use_mailmap]
1572EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001573 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001574 print STDERR <<EOT
1575
1576Additional options:
15770 toggle all
1578tm toggle maintainers
1579tg toggle git entries
1580tl toggle open list entries
1581ts toggle subscriber list entries
1582f emails in file [$file_emails]
1583k keywords in file [$keywords]
1584r remove duplicates [$email_remove_duplicates]
1585p# pattern match depth [$pattern_depth]
1586EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001587 }
1588 print STDERR
1589"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1590
1591 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001592 chomp($input);
1593
Joe Perches683c6f82010-10-26 14:22:55 -07001594 $redraw = 1;
1595 my $rerun = 0;
1596 my @wish = split(/[, ]+/, $input);
1597 foreach my $nr (@wish) {
1598 $nr = lc($nr);
1599 my $sel = substr($nr, 0, 1);
1600 my $str = substr($nr, 1);
1601 my $val = 0;
1602 $val = $1 if $str =~ /^(\d+)$/;
1603
1604 if ($sel eq "y") {
1605 $interactive = 0;
1606 $done = 1;
1607 $output_rolestats = 0;
1608 $output_roles = 0;
1609 last;
1610 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1611 $selected{$nr - 1} = !$selected{$nr - 1};
1612 } elsif ($sel eq "*" || $sel eq '^') {
1613 my $toggle = 0;
1614 $toggle = 1 if ($sel eq '*');
1615 for (my $i = 0; $i < $count; $i++) {
1616 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001617 }
Joe Perches683c6f82010-10-26 14:22:55 -07001618 } elsif ($sel eq "0") {
1619 for (my $i = 0; $i < $count; $i++) {
1620 $selected{$i} = !$selected{$i};
1621 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001622 } elsif ($sel eq "t") {
1623 if (lc($str) eq "m") {
1624 for (my $i = 0; $i < $count; $i++) {
1625 $selected{$i} = !$selected{$i}
1626 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1627 }
1628 } elsif (lc($str) eq "g") {
1629 for (my $i = 0; $i < $count; $i++) {
1630 $selected{$i} = !$selected{$i}
1631 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1632 }
1633 } elsif (lc($str) eq "l") {
1634 for (my $i = 0; $i < $count; $i++) {
1635 $selected{$i} = !$selected{$i}
1636 if ($list[$i]->[1] =~ /^(open list)/i);
1637 }
1638 } elsif (lc($str) eq "s") {
1639 for (my $i = 0; $i < $count; $i++) {
1640 $selected{$i} = !$selected{$i}
1641 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1642 }
1643 }
Joe Perches683c6f82010-10-26 14:22:55 -07001644 } elsif ($sel eq "a") {
1645 if ($val > 0 && $val <= $count) {
1646 $authored{$val - 1} = !$authored{$val - 1};
1647 } elsif ($str eq '*' || $str eq '^') {
1648 my $toggle = 0;
1649 $toggle = 1 if ($str eq '*');
1650 for (my $i = 0; $i < $count; $i++) {
1651 $authored{$i} = $toggle;
1652 }
1653 }
1654 } elsif ($sel eq "s") {
1655 if ($val > 0 && $val <= $count) {
1656 $signed{$val - 1} = !$signed{$val - 1};
1657 } elsif ($str eq '*' || $str eq '^') {
1658 my $toggle = 0;
1659 $toggle = 1 if ($str eq '*');
1660 for (my $i = 0; $i < $count; $i++) {
1661 $signed{$i} = $toggle;
1662 }
1663 }
1664 } elsif ($sel eq "o") {
1665 $print_options = 1;
1666 $redraw = 1;
1667 } elsif ($sel eq "g") {
1668 if ($str eq "f") {
1669 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001670 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001671 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001672 }
Joe Perches683c6f82010-10-26 14:22:55 -07001673 $rerun = 1;
1674 } elsif ($sel eq "b") {
1675 if ($str eq "s") {
1676 bool_invert(\$email_git_blame_signatures);
1677 } else {
1678 bool_invert(\$email_git_blame);
1679 }
1680 $rerun = 1;
1681 } elsif ($sel eq "c") {
1682 if ($val > 0) {
1683 $email_git_min_signatures = $val;
1684 $rerun = 1;
1685 }
1686 } elsif ($sel eq "x") {
1687 if ($val > 0) {
1688 $email_git_max_maintainers = $val;
1689 $rerun = 1;
1690 }
1691 } elsif ($sel eq "%") {
1692 if ($str ne "" && $val >= 0) {
1693 $email_git_min_percent = $val;
1694 $rerun = 1;
1695 }
1696 } elsif ($sel eq "d") {
1697 if (vcs_is_git()) {
1698 $email_git_since = $str;
1699 } elsif (vcs_is_hg()) {
1700 $email_hg_since = $str;
1701 }
1702 $rerun = 1;
1703 } elsif ($sel eq "t") {
1704 bool_invert(\$email_git_all_signature_types);
1705 $rerun = 1;
1706 } elsif ($sel eq "f") {
1707 bool_invert(\$file_emails);
1708 $rerun = 1;
1709 } elsif ($sel eq "r") {
1710 bool_invert(\$email_remove_duplicates);
1711 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001712 } elsif ($sel eq "m") {
1713 bool_invert(\$email_use_mailmap);
1714 read_mailmap();
1715 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001716 } elsif ($sel eq "k") {
1717 bool_invert(\$keywords);
1718 $rerun = 1;
1719 } elsif ($sel eq "p") {
1720 if ($str ne "" && $val >= 0) {
1721 $pattern_depth = $val;
1722 $rerun = 1;
1723 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001724 } elsif ($sel eq "h" || $sel eq "?") {
1725 print STDERR <<EOT
1726
1727Interactive mode allows you to select the various maintainers, submitters,
1728commit signers and mailing lists that could be CC'd on a patch.
1729
1730Any *'d entry is selected.
1731
Joe Perches47abc722010-10-26 14:22:57 -07001732If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001733history of files in the patch. Also, each line of the current file can
1734be matched to its commit author and that commits signers with blame.
1735
1736Various knobs exist to control the length of time for active commit
1737tracking, the maximum number of commit authors and signers to add,
1738and such.
1739
1740Enter selections at the prompt until you are satisfied that the selected
1741maintainers are appropriate. You may enter multiple selections separated
1742by either commas or spaces.
1743
1744EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001745 } else {
1746 print STDERR "invalid option: '$nr'\n";
1747 $redraw = 0;
1748 }
1749 }
1750 if ($rerun) {
1751 print STDERR "git-blame can be very slow, please have patience..."
1752 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001753 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001754 }
1755 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001756
1757 #drop not selected entries
1758 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001759 my @new_emailto = ();
1760 foreach my $entry (@list) {
1761 if ($selected{$count}) {
1762 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001763 }
1764 $count++;
1765 }
Joe Perches683c6f82010-10-26 14:22:55 -07001766 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001767}
1768
Joe Perches683c6f82010-10-26 14:22:55 -07001769sub bool_invert {
1770 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001771
Joe Perches683c6f82010-10-26 14:22:55 -07001772 if ($$bool_ref) {
1773 $$bool_ref = 0;
1774 } else {
1775 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001776 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001777}
1778
Joe Perchesb9e23312010-10-26 14:22:58 -07001779sub deduplicate_email {
1780 my ($email) = @_;
1781
1782 my $matched = 0;
1783 my ($name, $address) = parse_email($email);
1784 $email = format_email($name, $address, 1);
1785 $email = mailmap_email($email);
1786
1787 return $email if (!$email_remove_duplicates);
1788
1789 ($name, $address) = parse_email($email);
1790
Joe Perchesfae99202010-10-26 14:22:58 -07001791 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001792 $name = $deduplicate_name_hash{lc($name)}->[0];
1793 $address = $deduplicate_name_hash{lc($name)}->[1];
1794 $matched = 1;
1795 } elsif ($deduplicate_address_hash{lc($address)}) {
1796 $name = $deduplicate_address_hash{lc($address)}->[0];
1797 $address = $deduplicate_address_hash{lc($address)}->[1];
1798 $matched = 1;
1799 }
1800 if (!$matched) {
1801 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1802 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1803 }
1804 $email = format_email($name, $address, 1);
1805 $email = mailmap_email($email);
1806 return $email;
1807}
1808
Joe Perches683c6f82010-10-26 14:22:55 -07001809sub save_commits_by_author {
1810 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001811
Joe Perches683c6f82010-10-26 14:22:55 -07001812 my @authors = ();
1813 my @commits = ();
1814 my @subjects = ();
1815
1816 foreach my $line (@lines) {
1817 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1818 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001819 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001820 push(@authors, $author);
1821 }
1822 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1823 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1824 }
1825
1826 for (my $i = 0; $i < @authors; $i++) {
1827 my $exists = 0;
1828 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1829 if (@{$ref}[0] eq $commits[$i] &&
1830 @{$ref}[1] eq $subjects[$i]) {
1831 $exists = 1;
1832 last;
1833 }
1834 }
1835 if (!$exists) {
1836 push(@{$commit_author_hash{$authors[$i]}},
1837 [ ($commits[$i], $subjects[$i]) ]);
1838 }
1839 }
1840}
1841
1842sub save_commits_by_signer {
1843 my (@lines) = @_;
1844
1845 my $commit = "";
1846 my $subject = "";
1847
1848 foreach my $line (@lines) {
1849 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1850 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1851 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1852 my @signatures = ($line);
1853 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1854 my @types = @$types_ref;
1855 my @signers = @$signers_ref;
1856
1857 my $type = $types[0];
1858 my $signer = $signers[0];
1859
Joe Perchesb9e23312010-10-26 14:22:58 -07001860 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001861
Joe Perches683c6f82010-10-26 14:22:55 -07001862 my $exists = 0;
1863 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1864 if (@{$ref}[0] eq $commit &&
1865 @{$ref}[1] eq $subject &&
1866 @{$ref}[2] eq $type) {
1867 $exists = 1;
1868 last;
1869 }
1870 }
1871 if (!$exists) {
1872 push(@{$commit_signer_hash{$signer}},
1873 [ ($commit, $subject, $type) ]);
1874 }
1875 }
1876 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001877}
1878
Joe Perches60db31a2009-12-14 18:00:50 -08001879sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001880 my ($role, $divisor, @lines) = @_;
1881
1882 my %hash;
1883 my $count = 0;
1884
Joe Perchesa8af2432009-12-14 18:00:49 -08001885 return if (@lines <= 0);
1886
1887 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001888 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001889 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001890 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001891
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001892 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001893
Joe Perches63ab52d2010-10-26 14:22:51 -07001894 return if (@lines <= 0);
1895
Joe Perches0e70e832009-09-21 17:04:20 -07001896 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001897
Joe Perches11ecf532009-09-21 17:04:22 -07001898 # uniq -c
1899 $hash{$_}++ for @lines;
1900
1901 # sort -rn
1902 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1903 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001904 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001905
Joe Perchesa8af2432009-12-14 18:00:49 -08001906 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07001907 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07001908 $count++;
1909 last if ($sign_offs < $email_git_min_signatures ||
1910 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001911 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001912 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001913 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001914 my $fmt_percent = sprintf("%.0f", $percent);
1915 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1916 } else {
1917 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001918 }
Joe Perchesf5492662009-09-21 17:04:13 -07001919 }
1920}
1921
Joe Perches60db31a2009-12-14 18:00:50 -08001922sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001923 my ($file) = @_;
1924
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001925 my $authors_ref;
1926 my $signers_ref;
1927 my $stats_ref;
1928 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001929 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001930 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001931 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001932
Joe Perches683c6f82010-10-26 14:22:55 -07001933 $vcs_used = vcs_exists();
1934 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001935
Joe Perches60db31a2009-12-14 18:00:50 -08001936 my $cmd = $VCS_cmds{"find_signers_cmd"};
1937 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1938
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001939 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1940
1941 @signers = @{$signers_ref} if defined $signers_ref;
1942 @authors = @{$authors_ref} if defined $authors_ref;
1943 @stats = @{$stats_ref} if defined $stats_ref;
1944
1945# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07001946
1947 foreach my $signer (@signers) {
1948 $signer = deduplicate_email($signer);
1949 }
1950
Joe Perches60db31a2009-12-14 18:00:50 -08001951 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001952 vcs_assign("authored", $commits, @authors);
1953 if ($#authors == $#stats) {
1954 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1955 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
1956
1957 my $added = 0;
1958 my $deleted = 0;
1959 for (my $i = 0; $i <= $#stats; $i++) {
1960 if ($stats[$i] =~ /$stat_pattern/) {
1961 $added += $1;
1962 $deleted += $2;
1963 }
1964 }
1965 my @tmp_authors = uniq(@authors);
1966 foreach my $author (@tmp_authors) {
1967 $author = deduplicate_email($author);
1968 }
1969 @tmp_authors = uniq(@tmp_authors);
1970 my @list_added = ();
1971 my @list_deleted = ();
1972 foreach my $author (@tmp_authors) {
1973 my $auth_added = 0;
1974 my $auth_deleted = 0;
1975 for (my $i = 0; $i <= $#stats; $i++) {
1976 if ($author eq deduplicate_email($authors[$i]) &&
1977 $stats[$i] =~ /$stat_pattern/) {
1978 $auth_added += $1;
1979 $auth_deleted += $2;
1980 }
1981 }
1982 for (my $i = 0; $i < $auth_added; $i++) {
1983 push(@list_added, $author);
1984 }
1985 for (my $i = 0; $i < $auth_deleted; $i++) {
1986 push(@list_deleted, $author);
1987 }
1988 }
1989 vcs_assign("added_lines", $added, @list_added);
1990 vcs_assign("removed_lines", $deleted, @list_deleted);
1991 }
Joe Perchesa8af2432009-12-14 18:00:49 -08001992}
1993
Joe Perches60db31a2009-12-14 18:00:50 -08001994sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001995 my ($file) = @_;
1996
Joe Perches60db31a2009-12-14 18:00:50 -08001997 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001998 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001999 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002000 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002001 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07002002
Joe Perches683c6f82010-10-26 14:22:55 -07002003 $vcs_used = vcs_exists();
2004 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002005
Joe Perches63ab52d2010-10-26 14:22:51 -07002006 @all_commits = vcs_blame($file);
2007 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002008 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002009 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002010
Joe Perches683c6f82010-10-26 14:22:55 -07002011 if ($email_git_blame_signatures) {
2012 if (vcs_is_hg()) {
2013 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002014 my $commit_authors_ref;
2015 my $commit_signers_ref;
2016 my $stats_ref;
2017 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002018 my @commit_signers = ();
2019 my $commit = join(" -r ", @commits);
2020 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002021
Joe Perches683c6f82010-10-26 14:22:55 -07002022 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2023 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002024
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002025 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2026 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2027 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002028
Joe Perches683c6f82010-10-26 14:22:55 -07002029 push(@signers, @commit_signers);
2030 } else {
2031 foreach my $commit (@commits) {
2032 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002033 my $commit_authors_ref;
2034 my $commit_signers_ref;
2035 my $stats_ref;
2036 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002037 my @commit_signers = ();
2038 my $cmd;
2039
2040 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2041 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2042
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002043 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2044 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2045 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002046
2047 push(@signers, @commit_signers);
2048 }
2049 }
Joe Perchesf5492662009-09-21 17:04:13 -07002050 }
2051
Joe Perchesa8af2432009-12-14 18:00:49 -08002052 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002053 if ($output_rolestats) {
2054 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002055 if (vcs_is_hg()) {{ # Double brace for last exit
2056 my $commit_count;
2057 my @commit_signers = ();
2058 @commits = uniq(@commits);
2059 @commits = sort(@commits);
2060 my $commit = join(" -r ", @commits);
2061 my $cmd;
2062
2063 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2064 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2065
2066 my @lines = ();
2067
2068 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2069
2070 if (!$email_git_penguin_chiefs) {
2071 @lines = grep(!/${penguin_chiefs}/i, @lines);
2072 }
2073
2074 last if !@lines;
2075
2076 my @authors = ();
2077 foreach my $line (@lines) {
2078 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2079 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002080 $author = deduplicate_email($author);
2081 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002082 }
2083 }
2084
2085 save_commits_by_author(@lines) if ($interactive);
2086 save_commits_by_signer(@lines) if ($interactive);
2087
2088 push(@signers, @authors);
2089 }}
2090 else {
2091 foreach my $commit (@commits) {
2092 my $i;
2093 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2094 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2095 my @author = vcs_find_author($cmd);
2096 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002097
2098 my $formatted_author = deduplicate_email($author[0]);
2099
Joe Perches683c6f82010-10-26 14:22:55 -07002100 my $count = grep(/$commit/, @all_commits);
2101 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002102 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002103 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002104 }
2105 }
2106 if (@blame_signers) {
2107 vcs_assign("authored lines", $total_lines, @blame_signers);
2108 }
2109 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002110 foreach my $signer (@signers) {
2111 $signer = deduplicate_email($signer);
2112 }
Joe Perches60db31a2009-12-14 18:00:50 -08002113 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002114 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002115 foreach my $signer (@signers) {
2116 $signer = deduplicate_email($signer);
2117 }
Joe Perches60db31a2009-12-14 18:00:50 -08002118 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002119 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002120}
2121
2122sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002123 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002124
2125 my %saw;
2126 @parms = grep(!$saw{$_}++, @parms);
2127 return @parms;
2128}
2129
2130sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002131 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002132
2133 my %saw;
2134 @parms = sort @parms;
2135 @parms = grep(!$saw{$_}++, @parms);
2136 return @parms;
2137}
2138
Joe Perches03372db2010-03-05 13:43:00 -08002139sub clean_file_emails {
2140 my (@file_emails) = @_;
2141 my @fmt_emails = ();
2142
2143 foreach my $email (@file_emails) {
2144 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2145 my ($name, $address) = parse_email($email);
2146 if ($name eq '"[,\.]"') {
2147 $name = "";
2148 }
2149
2150 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2151 if (@nw > 2) {
2152 my $first = $nw[@nw - 3];
2153 my $middle = $nw[@nw - 2];
2154 my $last = $nw[@nw - 1];
2155
2156 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2157 (length($first) == 2 && substr($first, -1) eq ".")) ||
2158 (length($middle) == 1 ||
2159 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2160 $name = "$first $middle $last";
2161 } else {
2162 $name = "$middle $last";
2163 }
2164 }
2165
2166 if (substr($name, -1) =~ /[,\.]/) {
2167 $name = substr($name, 0, length($name) - 1);
2168 } elsif (substr($name, -2) =~ /[,\.]"/) {
2169 $name = substr($name, 0, length($name) - 2) . '"';
2170 }
2171
2172 if (substr($name, 0, 1) =~ /[,\.]/) {
2173 $name = substr($name, 1, length($name) - 1);
2174 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2175 $name = '"' . substr($name, 2, length($name) - 2);
2176 }
2177
2178 my $fmt_email = format_email($name, $address, $email_usename);
2179 push(@fmt_emails, $fmt_email);
2180 }
2181 return @fmt_emails;
2182}
2183
Joe Perches3c7385b2009-12-14 18:00:46 -08002184sub merge_email {
2185 my @lines;
2186 my %saw;
2187
2188 for (@_) {
2189 my ($address, $role) = @$_;
2190 if (!$saw{$address}) {
2191 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002192 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002193 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002194 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002195 }
2196 $saw{$address} = 1;
2197 }
2198 }
2199
2200 return @lines;
2201}
2202
Joe Perchescb7301c2009-04-07 20:40:12 -07002203sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002204 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002205
2206 if ($output_multiline) {
2207 foreach my $line (@parms) {
2208 print("${line}\n");
2209 }
2210 } else {
2211 print(join($output_separator, @parms));
2212 print("\n");
2213 }
2214}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002215
2216my $rfc822re;
2217
2218sub make_rfc822re {
2219# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2220# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2221# This regexp will only work on addresses which have had comments stripped
2222# and replaced with rfc822_lwsp.
2223
2224 my $specials = '()<>@,;:\\\\".\\[\\]';
2225 my $controls = '\\000-\\037\\177';
2226
2227 my $dtext = "[^\\[\\]\\r\\\\]";
2228 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2229
2230 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2231
2232# Use zero-width assertion to spot the limit of an atom. A simple
2233# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2234 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2235 my $word = "(?:$atom|$quoted_string)";
2236 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2237
2238 my $sub_domain = "(?:$atom|$domain_literal)";
2239 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2240
2241 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2242
2243 my $phrase = "$word*";
2244 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2245 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2246 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2247
2248 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2249 my $address = "(?:$mailbox|$group)";
2250
2251 return "$rfc822_lwsp*$address";
2252}
2253
2254sub rfc822_strip_comments {
2255 my $s = shift;
2256# Recursively remove comments, and replace with a single space. The simpler
2257# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2258# chars in atoms, for example.
2259
2260 while ($s =~ s/^((?:[^"\\]|\\.)*
2261 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2262 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2263 return $s;
2264}
2265
2266# valid: returns true if the parameter is an RFC822 valid address
2267#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002268sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002269 my $s = rfc822_strip_comments(shift);
2270
2271 if (!$rfc822re) {
2272 $rfc822re = make_rfc822re();
2273 }
2274
2275 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2276}
2277
2278# validlist: In scalar context, returns true if the parameter is an RFC822
2279# valid list of addresses.
2280#
2281# In list context, returns an empty list on failure (an invalid
2282# address was found); otherwise a list whose first element is the
2283# number of addresses found and whose remaining elements are the
2284# addresses. This is needed to disambiguate failure (invalid)
2285# from success with no addresses found, because an empty string is
2286# a valid list.
2287
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002288sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002289 my $s = rfc822_strip_comments(shift);
2290
2291 if (!$rfc822re) {
2292 $rfc822re = make_rfc822re();
2293 }
2294 # * null list items are valid according to the RFC
2295 # * the '1' business is to aid in distinguishing failure from no results
2296
2297 my @r;
2298 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2299 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002300 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002301 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002302 }
2303 return wantarray ? (scalar(@r), @r) : 1;
2304 }
Joe Perches60db31a2009-12-14 18:00:50 -08002305 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002306}