blob: 98bae869f6d06d612f32d3bac51e52bd2fc348d5 [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 Perches290603c2009-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
Joe Perchese4d26b02010-05-24 14:33:17 -0700784 --git-since => git history to use (default: $email_git_since)
785 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700786 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700787 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700788 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -0700789 --n => include name 'Full Name <addr\@domain.tld>'
790 --l => include list(s) if any
791 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700792 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800793 --roles => show roles (status:subsystem, git-signer, list, etc...)
794 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800795 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700796 --scm => print SCM tree(s) if any
797 --status => print status if any
798 --subsystem => print subsystem name if any
799 --web => print website(s) if any
800
801Output type options:
802 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700803 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700804 --multiline => print 1 entry per line
805
Joe Perchescb7301c2009-04-07 20:40:12 -0700806Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700807 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700808 --keywords => scan patch for keywords (default: $keywords)
809 --sections => print all of the subsystem sections with pattern matches
810 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700811 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700812 --help => show this help information
813
Joe Perches3fb55652009-09-21 17:04:17 -0700814Default options:
Joe Perches7e1863a2011-01-12 16:59:49 -0800815 [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0
816 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700817
Joe Perches870020f2009-07-29 15:04:28 -0700818Notes:
819 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700820 Used with "--git", git signators for _all_ files in and below
821 directory are examined as git recurses directories.
822 Any specified X: (exclude) pattern matches are _not_ ignored.
823 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800824 no individual file within the directory or subdirectory
825 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700826 Used with "--git-blame", does not iterate all files in directory
827 Using "--git-blame" is slow and may add old committers and authors
828 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800829 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
830 other automated tools that expect only ["name"] <email address>
831 may not work because of additional output after <email address>.
832 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
833 not the percentage of the entire file authored. # of commits is
834 not a good measure of amount of code authored. 1 major commit may
835 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800836 If git is not installed, but mercurial (hg) is installed and an .hg
837 repository exists, the following options apply to mercurial:
838 --git,
839 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
840 --git-blame
841 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700842 File ".get_maintainer.conf", if it exists in the linux kernel source root
843 directory, can change whatever get_maintainer defaults are desired.
844 Entries in this file can be any command line argument.
845 This file is prepended to any additional command line arguments.
846 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700847EOT
848}
849
850sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700851 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700852
Joe Perches47abc722010-10-26 14:22:57 -0700853 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
854 $lk_path .= "/";
855 }
856 if ( (-f "${lk_path}COPYING")
857 && (-f "${lk_path}CREDITS")
858 && (-f "${lk_path}Kbuild")
859 && (-f "${lk_path}MAINTAINERS")
860 && (-f "${lk_path}Makefile")
861 && (-f "${lk_path}README")
862 && (-d "${lk_path}Documentation")
863 && (-d "${lk_path}arch")
864 && (-d "${lk_path}include")
865 && (-d "${lk_path}drivers")
866 && (-d "${lk_path}fs")
867 && (-d "${lk_path}init")
868 && (-d "${lk_path}ipc")
869 && (-d "${lk_path}kernel")
870 && (-d "${lk_path}lib")
871 && (-d "${lk_path}scripts")) {
872 return 1;
873 }
874 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700875}
876
Joe Perches0e70e832009-09-21 17:04:20 -0700877sub parse_email {
878 my ($formatted_email) = @_;
879
880 my $name = "";
881 my $address = "";
882
Joe Perches11ecf532009-09-21 17:04:22 -0700883 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700884 $name = $1;
885 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700886 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700887 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700888 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700889 $address = $1;
890 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700891
892 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700893 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700894 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700895
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800896 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700897 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700898 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700899 }
Joe Perches0e70e832009-09-21 17:04:20 -0700900
901 return ($name, $address);
902}
903
904sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800905 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700906
907 my $formatted_email;
908
909 $name =~ s/^\s+|\s+$//g;
910 $name =~ s/^\"|\"$//g;
911 $address =~ s/^\s+|\s+$//g;
912
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800913 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700914 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
915 $name = "\"$name\"";
916 }
917
Joe Perchesa8af2432009-12-14 18:00:49 -0800918 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700919 if ("$name" eq "") {
920 $formatted_email = "$address";
921 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800922 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700923 }
924 } else {
925 $formatted_email = $address;
926 }
927
Joe Perchescb7301c2009-04-07 20:40:12 -0700928 return $formatted_email;
929}
930
Joe Perches272a8972010-01-08 14:42:48 -0800931sub find_first_section {
932 my $index = 0;
933
934 while ($index < @typevalue) {
935 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700936 if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perches272a8972010-01-08 14:42:48 -0800937 last;
938 }
939 $index++;
940 }
941
942 return $index;
943}
944
Joe Perchesb7816552009-09-21 17:04:24 -0700945sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700946 my ($index) = @_;
947
948 while ($index > 0) {
949 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700950 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -0700951 last;
952 }
953 $index--;
954 }
955
956 return $index;
957}
958
959sub find_ending_index {
960 my ($index) = @_;
961
962 while ($index < @typevalue) {
963 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700964 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -0700965 last;
966 }
967 $index++;
968 }
969
970 return $index;
971}
972
Joe Perches3c7385b2009-12-14 18:00:46 -0800973sub get_maintainer_role {
974 my ($index) = @_;
975
976 my $i;
977 my $start = find_starting_index($index);
978 my $end = find_ending_index($index);
979
Joe Perches0ede2742012-03-23 15:01:56 -0700980 my $role = "unknown";
Joe Perches3c7385b2009-12-14 18:00:46 -0800981 my $subsystem = $typevalue[$start];
Joe Perches364f68d2015-06-25 15:01:52 -0700982 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
983 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
Joe Perches3c7385b2009-12-14 18:00:46 -0800984 $subsystem =~ s/\s*$//;
985 $subsystem = $subsystem . "...";
986 }
987
988 for ($i = $start + 1; $i < $end; $i++) {
989 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700990 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800991 my $ptype = $1;
992 my $pvalue = $2;
993 if ($ptype eq "S") {
994 $role = $pvalue;
995 }
996 }
997 }
998
999 $role = lc($role);
1000 if ($role eq "supported") {
1001 $role = "supporter";
1002 } elsif ($role eq "maintained") {
1003 $role = "maintainer";
1004 } elsif ($role eq "odd fixes") {
1005 $role = "odd fixer";
1006 } elsif ($role eq "orphan") {
1007 $role = "orphan minder";
1008 } elsif ($role eq "obsolete") {
1009 $role = "obsolete minder";
1010 } elsif ($role eq "buried alive in reporters") {
1011 $role = "chief penguin";
1012 }
1013
1014 return $role . ":" . $subsystem;
1015}
1016
1017sub get_list_role {
1018 my ($index) = @_;
1019
1020 my $i;
1021 my $start = find_starting_index($index);
1022 my $end = find_ending_index($index);
1023
1024 my $subsystem = $typevalue[$start];
Joe Perches364f68d2015-06-25 15:01:52 -07001025 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
1026 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
Joe Perches3c7385b2009-12-14 18:00:46 -08001027 $subsystem =~ s/\s*$//;
1028 $subsystem = $subsystem . "...";
1029 }
1030
1031 if ($subsystem eq "THE REST") {
1032 $subsystem = "";
1033 }
1034
1035 return $subsystem;
1036}
1037
Joe Perchescb7301c2009-04-07 20:40:12 -07001038sub add_categories {
1039 my ($index) = @_;
1040
Joe Perchesb7816552009-09-21 17:04:24 -07001041 my $i;
1042 my $start = find_starting_index($index);
1043 my $end = find_ending_index($index);
1044
1045 push(@subsystem, $typevalue[$start]);
1046
1047 for ($i = $start + 1; $i < $end; $i++) {
1048 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001049 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001050 my $ptype = $1;
1051 my $pvalue = $2;
1052 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001053 my $list_address = $pvalue;
1054 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001055 my $list_role = get_list_role($i);
1056
1057 if ($list_role ne "") {
1058 $list_role = ":" . $list_role;
1059 }
Joe Perches290603c2009-06-16 15:33:58 -07001060 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1061 $list_address = $1;
1062 $list_additional = $2;
1063 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001064 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001065 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001066 if (!$hash_list_to{lc($list_address)}) {
1067 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001068 push(@list_to, [$list_address,
1069 "subscriber list${list_role}"]);
1070 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001071 }
1072 } else {
1073 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001074 if (!$hash_list_to{lc($list_address)}) {
1075 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001076 if ($list_additional =~ m/moderated/) {
1077 push(@list_to, [$list_address,
1078 "moderated list${list_role}"]);
1079 } else {
1080 push(@list_to, [$list_address,
1081 "open list${list_role}"]);
1082 }
Joe Perches683c6f82010-10-26 14:22:55 -07001083 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001084 }
1085 }
1086 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001087 my ($name, $address) = parse_email($pvalue);
1088 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001089 if ($i > 0) {
1090 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001091 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001092 if ($1 eq "P") {
1093 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001094 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001095 }
1096 }
1097 }
1098 }
Joe Perches0e70e832009-09-21 17:04:20 -07001099 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001100 my $role = get_maintainer_role($i);
1101 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001102 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001103 } elsif ($ptype eq "R") {
1104 my ($name, $address) = parse_email($pvalue);
1105 if ($name eq "") {
1106 if ($i > 0) {
1107 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001108 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001109 if ($1 eq "P") {
1110 $name = $2;
1111 $pvalue = format_email($name, $address, $email_usename);
1112 }
1113 }
1114 }
1115 }
1116 if ($email_reviewer) {
1117 push_email_addresses($pvalue, 'reviewer');
1118 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001119 } elsif ($ptype eq "T") {
1120 push(@scm, $pvalue);
1121 } elsif ($ptype eq "W") {
1122 push(@web, $pvalue);
1123 } elsif ($ptype eq "S") {
1124 push(@status, $pvalue);
1125 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001126 }
1127 }
1128}
1129
Joe Perches11ecf532009-09-21 17:04:22 -07001130sub email_inuse {
1131 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001132
Joe Perches11ecf532009-09-21 17:04:22 -07001133 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001134 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1135 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001136
Joe Perches0e70e832009-09-21 17:04:20 -07001137 return 0;
1138}
1139
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001140sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001141 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001142
Joe Perches0e70e832009-09-21 17:04:20 -07001143 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001144
Joe Perchesb7816552009-09-21 17:04:24 -07001145 if ($address eq "") {
1146 return 0;
1147 }
1148
Joe Perches11ecf532009-09-21 17:04:22 -07001149 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001150 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001151 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001152 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001153 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001154 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001155 }
Joe Perchesb7816552009-09-21 17:04:24 -07001156
1157 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001158}
1159
1160sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001161 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001162
1163 my @address_list = ();
1164
Joe Perches5f2441e2009-06-16 15:34:02 -07001165 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001166 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001167 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001168 my $array_count = shift(@address_list);
1169 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001170 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001171 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001172 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001173 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001174 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1175 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001176 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001177}
1178
Joe Perches3c7385b2009-12-14 18:00:46 -08001179sub add_role {
1180 my ($line, $role) = @_;
1181
1182 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001183 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001184
1185 foreach my $entry (@email_to) {
1186 if ($email_remove_duplicates) {
1187 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001188 if (($name eq $entry_name || $address eq $entry_address)
1189 && ($role eq "" || !($entry->[1] =~ m/$role/))
1190 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001191 if ($entry->[1] eq "") {
1192 $entry->[1] = "$role";
1193 } else {
1194 $entry->[1] = "$entry->[1],$role";
1195 }
1196 }
1197 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001198 if ($email eq $entry->[0]
1199 && ($role eq "" || !($entry->[1] =~ m/$role/))
1200 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001201 if ($entry->[1] eq "") {
1202 $entry->[1] = "$role";
1203 } else {
1204 $entry->[1] = "$entry->[1],$role";
1205 }
1206 }
1207 }
1208 }
1209}
1210
Joe Perchescb7301c2009-04-07 20:40:12 -07001211sub which {
1212 my ($bin) = @_;
1213
Joe Perchesf5f50782009-06-16 15:34:00 -07001214 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001215 if (-e "$path/$bin") {
1216 return "$path/$bin";
1217 }
1218 }
1219
1220 return "";
1221}
1222
Joe Perchesbcde44e2010-10-26 14:22:53 -07001223sub which_conf {
1224 my ($conf) = @_;
1225
1226 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1227 if (-e "$path/$conf") {
1228 return "$path/$conf";
1229 }
1230 }
1231
1232 return "";
1233}
1234
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001235sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001236 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001237
Joe Perches47abc722010-10-26 14:22:57 -07001238 my ($name, $address) = parse_email($line);
1239 my $email = format_email($name, $address, 1);
1240 my $real_name = $name;
1241 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001242
Joe Perches47abc722010-10-26 14:22:57 -07001243 if (exists $mailmap->{names}->{$email} ||
1244 exists $mailmap->{addresses}->{$email}) {
1245 if (exists $mailmap->{names}->{$email}) {
1246 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001247 }
Joe Perches47abc722010-10-26 14:22:57 -07001248 if (exists $mailmap->{addresses}->{$email}) {
1249 $real_address = $mailmap->{addresses}->{$email};
1250 }
1251 } else {
1252 if (exists $mailmap->{names}->{$address}) {
1253 $real_name = $mailmap->{names}->{$address};
1254 }
1255 if (exists $mailmap->{addresses}->{$address}) {
1256 $real_address = $mailmap->{addresses}->{$address};
1257 }
1258 }
1259 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001260}
1261
1262sub mailmap {
1263 my (@addresses) = @_;
1264
Joe Perchesb9e23312010-10-26 14:22:58 -07001265 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001266 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001267 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001268 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001269 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1270 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001271}
1272
1273sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001274 my %address_map;
1275 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001276
Joe Perches47abc722010-10-26 14:22:57 -07001277 foreach my $email (@emails) {
1278 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001279 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001280 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001281 $email = format_email($name, $address, 1);
1282 } else {
1283 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001284 }
Joe Perches47abc722010-10-26 14:22:57 -07001285 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001286}
1287
Joe Perches60db31a2009-12-14 18:00:50 -08001288sub git_execute_cmd {
1289 my ($cmd) = @_;
1290 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001291
Joe Perches60db31a2009-12-14 18:00:50 -08001292 my $output = `$cmd`;
1293 $output =~ s/^\s*//gm;
1294 @lines = split("\n", $output);
1295
1296 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001297}
1298
Joe Perches60db31a2009-12-14 18:00:50 -08001299sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001300 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001301 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001302
Joe Perches60db31a2009-12-14 18:00:50 -08001303 my $output = `$cmd`;
1304 @lines = split("\n", $output);
1305
1306 return @lines;
1307}
1308
Joe Perches683c6f82010-10-26 14:22:55 -07001309sub extract_formatted_signatures {
1310 my (@signature_lines) = @_;
1311
1312 my @type = @signature_lines;
1313
1314 s/\s*(.*):.*/$1/ for (@type);
1315
1316 # cut -f2- -d":"
1317 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1318
1319## Reformat email addresses (with names) to avoid badly written signatures
1320
1321 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001322 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001323 }
1324
1325 return (\@type, \@signature_lines);
1326}
1327
Joe Perches60db31a2009-12-14 18:00:50 -08001328sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001329 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001330 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001331 my @lines = ();
1332 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001333 my @authors = ();
1334 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001335
Joe Perches60db31a2009-12-14 18:00:50 -08001336 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001337
Joe Perches60db31a2009-12-14 18:00:50 -08001338 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001339 my $author_pattern = $VCS_cmds{"author_pattern"};
1340 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1341
1342 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001343
Joe Perches60db31a2009-12-14 18:00:50 -08001344 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001345
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001346 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001347 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001348 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001349
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001350# print("stats: <@stats>\n");
1351
1352 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001353
1354 save_commits_by_author(@lines) if ($interactive);
1355 save_commits_by_signer(@lines) if ($interactive);
1356
Joe Perches0e70e832009-09-21 17:04:20 -07001357 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001358 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001359 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001360
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001361 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001362 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001363
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001364 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001365}
1366
Joe Perches63ab52d2010-10-26 14:22:51 -07001367sub vcs_find_author {
1368 my ($cmd) = @_;
1369 my @lines = ();
1370
1371 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1372
1373 if (!$email_git_penguin_chiefs) {
1374 @lines = grep(!/${penguin_chiefs}/i, @lines);
1375 }
1376
1377 return @lines if !@lines;
1378
Joe Perches683c6f82010-10-26 14:22:55 -07001379 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001380 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001381 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1382 my $author = $1;
1383 my ($name, $address) = parse_email($author);
1384 $author = format_email($name, $address, 1);
1385 push(@authors, $author);
1386 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001387 }
1388
Joe Perches683c6f82010-10-26 14:22:55 -07001389 save_commits_by_author(@lines) if ($interactive);
1390 save_commits_by_signer(@lines) if ($interactive);
1391
1392 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001393}
1394
Joe Perches60db31a2009-12-14 18:00:50 -08001395sub vcs_save_commits {
1396 my ($cmd) = @_;
1397 my @lines = ();
1398 my @commits = ();
1399
1400 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1401
1402 foreach my $line (@lines) {
1403 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1404 push(@commits, $1);
1405 }
1406 }
1407
1408 return @commits;
1409}
1410
1411sub vcs_blame {
1412 my ($file) = @_;
1413 my $cmd;
1414 my @commits = ();
1415
1416 return @commits if (!(-f $file));
1417
1418 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1419 my @all_commits = ();
1420
1421 $cmd = $VCS_cmds{"blame_file_cmd"};
1422 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1423 @all_commits = vcs_save_commits($cmd);
1424
1425 foreach my $file_range_diff (@range) {
1426 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1427 my $diff_file = $1;
1428 my $diff_start = $2;
1429 my $diff_length = $3;
1430 next if ("$file" ne "$diff_file");
1431 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1432 push(@commits, $all_commits[$i]);
1433 }
1434 }
1435 } elsif (@range) {
1436 foreach my $file_range_diff (@range) {
1437 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1438 my $diff_file = $1;
1439 my $diff_start = $2;
1440 my $diff_length = $3;
1441 next if ("$file" ne "$diff_file");
1442 $cmd = $VCS_cmds{"blame_range_cmd"};
1443 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1444 push(@commits, vcs_save_commits($cmd));
1445 }
1446 } else {
1447 $cmd = $VCS_cmds{"blame_file_cmd"};
1448 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1449 @commits = vcs_save_commits($cmd);
1450 }
1451
Joe Perches63ab52d2010-10-26 14:22:51 -07001452 foreach my $commit (@commits) {
1453 $commit =~ s/^\^//g;
1454 }
1455
Joe Perches60db31a2009-12-14 18:00:50 -08001456 return @commits;
1457}
1458
1459my $printed_novcs = 0;
1460sub vcs_exists {
1461 %VCS_cmds = %VCS_cmds_git;
1462 return 1 if eval $VCS_cmds{"available"};
1463 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001464 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001465 %VCS_cmds = ();
1466 if (!$printed_novcs) {
1467 warn("$P: No supported VCS found. Add --nogit to options?\n");
1468 warn("Using a git repository produces better results.\n");
1469 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001470 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001471 $printed_novcs = 1;
1472 }
1473 return 0;
1474}
1475
Joe Perches683c6f82010-10-26 14:22:55 -07001476sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001477 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001478 return $vcs_used == 1;
1479}
1480
1481sub vcs_is_hg {
1482 return $vcs_used == 2;
1483}
1484
Joe Perches6ef1c522010-10-26 14:22:56 -07001485sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001486 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001487 my @list = @$list_ref;
1488
Joe Perches683c6f82010-10-26 14:22:55 -07001489 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001490
1491 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001492 my %authored;
1493 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001494 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001495 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001496 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001497 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1498 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001499 $authored{$count} = 0;
1500 $signed{$count} = 0;
1501 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001502 }
1503
1504 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001505 my $done = 0;
1506 my $print_options = 0;
1507 my $redraw = 1;
1508 while (!$done) {
1509 $count = 0;
1510 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001511 printf STDERR "\n%1s %2s %-65s",
1512 "*", "#", "email/list and role:stats";
1513 if ($email_git ||
1514 ($email_git_fallback && !$maintained) ||
1515 $email_git_blame) {
1516 print STDERR "auth sign";
1517 }
1518 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001519 foreach my $entry (@list) {
1520 my $email = $entry->[0];
1521 my $role = $entry->[1];
1522 my $sel = "";
1523 $sel = "*" if ($selected{$count});
1524 my $commit_author = $commit_author_hash{$email};
1525 my $commit_signer = $commit_signer_hash{$email};
1526 my $authored = 0;
1527 my $signed = 0;
1528 $authored++ for (@{$commit_author});
1529 $signed++ for (@{$commit_signer});
1530 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1531 printf STDERR "%4d %4d", $authored, $signed
1532 if ($authored > 0 || $signed > 0);
1533 printf STDERR "\n %s\n", $role;
1534 if ($authored{$count}) {
1535 my $commit_author = $commit_author_hash{$email};
1536 foreach my $ref (@{$commit_author}) {
1537 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001538 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001539 }
Joe Perches683c6f82010-10-26 14:22:55 -07001540 if ($signed{$count}) {
1541 my $commit_signer = $commit_signer_hash{$email};
1542 foreach my $ref (@{$commit_signer}) {
1543 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1544 }
1545 }
1546
1547 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001548 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001549 }
Joe Perches683c6f82010-10-26 14:22:55 -07001550 my $date_ref = \$email_git_since;
1551 $date_ref = \$email_hg_since if (vcs_is_hg());
1552 if ($print_options) {
1553 $print_options = 0;
1554 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001555 print STDERR <<EOT
1556
1557Version Control options:
1558g use git history [$email_git]
1559gf use git-fallback [$email_git_fallback]
1560b use git blame [$email_git_blame]
1561bs use blame signatures [$email_git_blame_signatures]
1562c# minimum commits [$email_git_min_signatures]
1563%# min percent [$email_git_min_percent]
1564d# history to use [$$date_ref]
1565x# max maintainers [$email_git_max_maintainers]
1566t all signature types [$email_git_all_signature_types]
1567m use .mailmap [$email_use_mailmap]
1568EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001569 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001570 print STDERR <<EOT
1571
1572Additional options:
15730 toggle all
1574tm toggle maintainers
1575tg toggle git entries
1576tl toggle open list entries
1577ts toggle subscriber list entries
1578f emails in file [$file_emails]
1579k keywords in file [$keywords]
1580r remove duplicates [$email_remove_duplicates]
1581p# pattern match depth [$pattern_depth]
1582EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001583 }
1584 print STDERR
1585"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1586
1587 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001588 chomp($input);
1589
Joe Perches683c6f82010-10-26 14:22:55 -07001590 $redraw = 1;
1591 my $rerun = 0;
1592 my @wish = split(/[, ]+/, $input);
1593 foreach my $nr (@wish) {
1594 $nr = lc($nr);
1595 my $sel = substr($nr, 0, 1);
1596 my $str = substr($nr, 1);
1597 my $val = 0;
1598 $val = $1 if $str =~ /^(\d+)$/;
1599
1600 if ($sel eq "y") {
1601 $interactive = 0;
1602 $done = 1;
1603 $output_rolestats = 0;
1604 $output_roles = 0;
1605 last;
1606 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1607 $selected{$nr - 1} = !$selected{$nr - 1};
1608 } elsif ($sel eq "*" || $sel eq '^') {
1609 my $toggle = 0;
1610 $toggle = 1 if ($sel eq '*');
1611 for (my $i = 0; $i < $count; $i++) {
1612 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001613 }
Joe Perches683c6f82010-10-26 14:22:55 -07001614 } elsif ($sel eq "0") {
1615 for (my $i = 0; $i < $count; $i++) {
1616 $selected{$i} = !$selected{$i};
1617 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001618 } elsif ($sel eq "t") {
1619 if (lc($str) eq "m") {
1620 for (my $i = 0; $i < $count; $i++) {
1621 $selected{$i} = !$selected{$i}
1622 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1623 }
1624 } elsif (lc($str) eq "g") {
1625 for (my $i = 0; $i < $count; $i++) {
1626 $selected{$i} = !$selected{$i}
1627 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1628 }
1629 } elsif (lc($str) eq "l") {
1630 for (my $i = 0; $i < $count; $i++) {
1631 $selected{$i} = !$selected{$i}
1632 if ($list[$i]->[1] =~ /^(open list)/i);
1633 }
1634 } elsif (lc($str) eq "s") {
1635 for (my $i = 0; $i < $count; $i++) {
1636 $selected{$i} = !$selected{$i}
1637 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1638 }
1639 }
Joe Perches683c6f82010-10-26 14:22:55 -07001640 } elsif ($sel eq "a") {
1641 if ($val > 0 && $val <= $count) {
1642 $authored{$val - 1} = !$authored{$val - 1};
1643 } elsif ($str eq '*' || $str eq '^') {
1644 my $toggle = 0;
1645 $toggle = 1 if ($str eq '*');
1646 for (my $i = 0; $i < $count; $i++) {
1647 $authored{$i} = $toggle;
1648 }
1649 }
1650 } elsif ($sel eq "s") {
1651 if ($val > 0 && $val <= $count) {
1652 $signed{$val - 1} = !$signed{$val - 1};
1653 } elsif ($str eq '*' || $str eq '^') {
1654 my $toggle = 0;
1655 $toggle = 1 if ($str eq '*');
1656 for (my $i = 0; $i < $count; $i++) {
1657 $signed{$i} = $toggle;
1658 }
1659 }
1660 } elsif ($sel eq "o") {
1661 $print_options = 1;
1662 $redraw = 1;
1663 } elsif ($sel eq "g") {
1664 if ($str eq "f") {
1665 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001666 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001667 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001668 }
Joe Perches683c6f82010-10-26 14:22:55 -07001669 $rerun = 1;
1670 } elsif ($sel eq "b") {
1671 if ($str eq "s") {
1672 bool_invert(\$email_git_blame_signatures);
1673 } else {
1674 bool_invert(\$email_git_blame);
1675 }
1676 $rerun = 1;
1677 } elsif ($sel eq "c") {
1678 if ($val > 0) {
1679 $email_git_min_signatures = $val;
1680 $rerun = 1;
1681 }
1682 } elsif ($sel eq "x") {
1683 if ($val > 0) {
1684 $email_git_max_maintainers = $val;
1685 $rerun = 1;
1686 }
1687 } elsif ($sel eq "%") {
1688 if ($str ne "" && $val >= 0) {
1689 $email_git_min_percent = $val;
1690 $rerun = 1;
1691 }
1692 } elsif ($sel eq "d") {
1693 if (vcs_is_git()) {
1694 $email_git_since = $str;
1695 } elsif (vcs_is_hg()) {
1696 $email_hg_since = $str;
1697 }
1698 $rerun = 1;
1699 } elsif ($sel eq "t") {
1700 bool_invert(\$email_git_all_signature_types);
1701 $rerun = 1;
1702 } elsif ($sel eq "f") {
1703 bool_invert(\$file_emails);
1704 $rerun = 1;
1705 } elsif ($sel eq "r") {
1706 bool_invert(\$email_remove_duplicates);
1707 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001708 } elsif ($sel eq "m") {
1709 bool_invert(\$email_use_mailmap);
1710 read_mailmap();
1711 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001712 } elsif ($sel eq "k") {
1713 bool_invert(\$keywords);
1714 $rerun = 1;
1715 } elsif ($sel eq "p") {
1716 if ($str ne "" && $val >= 0) {
1717 $pattern_depth = $val;
1718 $rerun = 1;
1719 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001720 } elsif ($sel eq "h" || $sel eq "?") {
1721 print STDERR <<EOT
1722
1723Interactive mode allows you to select the various maintainers, submitters,
1724commit signers and mailing lists that could be CC'd on a patch.
1725
1726Any *'d entry is selected.
1727
Joe Perches47abc722010-10-26 14:22:57 -07001728If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001729history of files in the patch. Also, each line of the current file can
1730be matched to its commit author and that commits signers with blame.
1731
1732Various knobs exist to control the length of time for active commit
1733tracking, the maximum number of commit authors and signers to add,
1734and such.
1735
1736Enter selections at the prompt until you are satisfied that the selected
1737maintainers are appropriate. You may enter multiple selections separated
1738by either commas or spaces.
1739
1740EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001741 } else {
1742 print STDERR "invalid option: '$nr'\n";
1743 $redraw = 0;
1744 }
1745 }
1746 if ($rerun) {
1747 print STDERR "git-blame can be very slow, please have patience..."
1748 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001749 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001750 }
1751 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001752
1753 #drop not selected entries
1754 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001755 my @new_emailto = ();
1756 foreach my $entry (@list) {
1757 if ($selected{$count}) {
1758 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001759 }
1760 $count++;
1761 }
Joe Perches683c6f82010-10-26 14:22:55 -07001762 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001763}
1764
Joe Perches683c6f82010-10-26 14:22:55 -07001765sub bool_invert {
1766 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001767
Joe Perches683c6f82010-10-26 14:22:55 -07001768 if ($$bool_ref) {
1769 $$bool_ref = 0;
1770 } else {
1771 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001772 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001773}
1774
Joe Perchesb9e23312010-10-26 14:22:58 -07001775sub deduplicate_email {
1776 my ($email) = @_;
1777
1778 my $matched = 0;
1779 my ($name, $address) = parse_email($email);
1780 $email = format_email($name, $address, 1);
1781 $email = mailmap_email($email);
1782
1783 return $email if (!$email_remove_duplicates);
1784
1785 ($name, $address) = parse_email($email);
1786
Joe Perchesfae99202010-10-26 14:22:58 -07001787 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001788 $name = $deduplicate_name_hash{lc($name)}->[0];
1789 $address = $deduplicate_name_hash{lc($name)}->[1];
1790 $matched = 1;
1791 } elsif ($deduplicate_address_hash{lc($address)}) {
1792 $name = $deduplicate_address_hash{lc($address)}->[0];
1793 $address = $deduplicate_address_hash{lc($address)}->[1];
1794 $matched = 1;
1795 }
1796 if (!$matched) {
1797 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1798 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1799 }
1800 $email = format_email($name, $address, 1);
1801 $email = mailmap_email($email);
1802 return $email;
1803}
1804
Joe Perches683c6f82010-10-26 14:22:55 -07001805sub save_commits_by_author {
1806 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001807
Joe Perches683c6f82010-10-26 14:22:55 -07001808 my @authors = ();
1809 my @commits = ();
1810 my @subjects = ();
1811
1812 foreach my $line (@lines) {
1813 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1814 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001815 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001816 push(@authors, $author);
1817 }
1818 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1819 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1820 }
1821
1822 for (my $i = 0; $i < @authors; $i++) {
1823 my $exists = 0;
1824 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1825 if (@{$ref}[0] eq $commits[$i] &&
1826 @{$ref}[1] eq $subjects[$i]) {
1827 $exists = 1;
1828 last;
1829 }
1830 }
1831 if (!$exists) {
1832 push(@{$commit_author_hash{$authors[$i]}},
1833 [ ($commits[$i], $subjects[$i]) ]);
1834 }
1835 }
1836}
1837
1838sub save_commits_by_signer {
1839 my (@lines) = @_;
1840
1841 my $commit = "";
1842 my $subject = "";
1843
1844 foreach my $line (@lines) {
1845 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1846 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1847 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1848 my @signatures = ($line);
1849 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1850 my @types = @$types_ref;
1851 my @signers = @$signers_ref;
1852
1853 my $type = $types[0];
1854 my $signer = $signers[0];
1855
Joe Perchesb9e23312010-10-26 14:22:58 -07001856 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001857
Joe Perches683c6f82010-10-26 14:22:55 -07001858 my $exists = 0;
1859 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1860 if (@{$ref}[0] eq $commit &&
1861 @{$ref}[1] eq $subject &&
1862 @{$ref}[2] eq $type) {
1863 $exists = 1;
1864 last;
1865 }
1866 }
1867 if (!$exists) {
1868 push(@{$commit_signer_hash{$signer}},
1869 [ ($commit, $subject, $type) ]);
1870 }
1871 }
1872 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001873}
1874
Joe Perches60db31a2009-12-14 18:00:50 -08001875sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001876 my ($role, $divisor, @lines) = @_;
1877
1878 my %hash;
1879 my $count = 0;
1880
Joe Perchesa8af2432009-12-14 18:00:49 -08001881 return if (@lines <= 0);
1882
1883 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001884 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001885 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001886 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001887
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001888 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001889
Joe Perches63ab52d2010-10-26 14:22:51 -07001890 return if (@lines <= 0);
1891
Joe Perches0e70e832009-09-21 17:04:20 -07001892 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001893
Joe Perches11ecf532009-09-21 17:04:22 -07001894 # uniq -c
1895 $hash{$_}++ for @lines;
1896
1897 # sort -rn
1898 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1899 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001900 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001901
Joe Perchesa8af2432009-12-14 18:00:49 -08001902 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07001903 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07001904 $count++;
1905 last if ($sign_offs < $email_git_min_signatures ||
1906 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001907 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001908 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001909 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001910 my $fmt_percent = sprintf("%.0f", $percent);
1911 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1912 } else {
1913 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001914 }
Joe Perchesf5492662009-09-21 17:04:13 -07001915 }
1916}
1917
Joe Perches60db31a2009-12-14 18:00:50 -08001918sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001919 my ($file) = @_;
1920
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001921 my $authors_ref;
1922 my $signers_ref;
1923 my $stats_ref;
1924 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001925 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001926 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001927 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001928
Joe Perches683c6f82010-10-26 14:22:55 -07001929 $vcs_used = vcs_exists();
1930 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001931
Joe Perches60db31a2009-12-14 18:00:50 -08001932 my $cmd = $VCS_cmds{"find_signers_cmd"};
1933 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1934
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001935 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1936
1937 @signers = @{$signers_ref} if defined $signers_ref;
1938 @authors = @{$authors_ref} if defined $authors_ref;
1939 @stats = @{$stats_ref} if defined $stats_ref;
1940
1941# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07001942
1943 foreach my $signer (@signers) {
1944 $signer = deduplicate_email($signer);
1945 }
1946
Joe Perches60db31a2009-12-14 18:00:50 -08001947 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001948 vcs_assign("authored", $commits, @authors);
1949 if ($#authors == $#stats) {
1950 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1951 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
1952
1953 my $added = 0;
1954 my $deleted = 0;
1955 for (my $i = 0; $i <= $#stats; $i++) {
1956 if ($stats[$i] =~ /$stat_pattern/) {
1957 $added += $1;
1958 $deleted += $2;
1959 }
1960 }
1961 my @tmp_authors = uniq(@authors);
1962 foreach my $author (@tmp_authors) {
1963 $author = deduplicate_email($author);
1964 }
1965 @tmp_authors = uniq(@tmp_authors);
1966 my @list_added = ();
1967 my @list_deleted = ();
1968 foreach my $author (@tmp_authors) {
1969 my $auth_added = 0;
1970 my $auth_deleted = 0;
1971 for (my $i = 0; $i <= $#stats; $i++) {
1972 if ($author eq deduplicate_email($authors[$i]) &&
1973 $stats[$i] =~ /$stat_pattern/) {
1974 $auth_added += $1;
1975 $auth_deleted += $2;
1976 }
1977 }
1978 for (my $i = 0; $i < $auth_added; $i++) {
1979 push(@list_added, $author);
1980 }
1981 for (my $i = 0; $i < $auth_deleted; $i++) {
1982 push(@list_deleted, $author);
1983 }
1984 }
1985 vcs_assign("added_lines", $added, @list_added);
1986 vcs_assign("removed_lines", $deleted, @list_deleted);
1987 }
Joe Perchesa8af2432009-12-14 18:00:49 -08001988}
1989
Joe Perches60db31a2009-12-14 18:00:50 -08001990sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001991 my ($file) = @_;
1992
Joe Perches60db31a2009-12-14 18:00:50 -08001993 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001994 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001995 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001996 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001997 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001998
Joe Perches683c6f82010-10-26 14:22:55 -07001999 $vcs_used = vcs_exists();
2000 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002001
Joe Perches63ab52d2010-10-26 14:22:51 -07002002 @all_commits = vcs_blame($file);
2003 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002004 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002005 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002006
Joe Perches683c6f82010-10-26 14:22:55 -07002007 if ($email_git_blame_signatures) {
2008 if (vcs_is_hg()) {
2009 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002010 my $commit_authors_ref;
2011 my $commit_signers_ref;
2012 my $stats_ref;
2013 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002014 my @commit_signers = ();
2015 my $commit = join(" -r ", @commits);
2016 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002017
Joe Perches683c6f82010-10-26 14:22:55 -07002018 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2019 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002020
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002021 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2022 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2023 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002024
Joe Perches683c6f82010-10-26 14:22:55 -07002025 push(@signers, @commit_signers);
2026 } else {
2027 foreach my $commit (@commits) {
2028 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002029 my $commit_authors_ref;
2030 my $commit_signers_ref;
2031 my $stats_ref;
2032 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002033 my @commit_signers = ();
2034 my $cmd;
2035
2036 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2037 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2038
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002039 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2040 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2041 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002042
2043 push(@signers, @commit_signers);
2044 }
2045 }
Joe Perchesf5492662009-09-21 17:04:13 -07002046 }
2047
Joe Perchesa8af2432009-12-14 18:00:49 -08002048 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002049 if ($output_rolestats) {
2050 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002051 if (vcs_is_hg()) {{ # Double brace for last exit
2052 my $commit_count;
2053 my @commit_signers = ();
2054 @commits = uniq(@commits);
2055 @commits = sort(@commits);
2056 my $commit = join(" -r ", @commits);
2057 my $cmd;
2058
2059 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2060 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2061
2062 my @lines = ();
2063
2064 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2065
2066 if (!$email_git_penguin_chiefs) {
2067 @lines = grep(!/${penguin_chiefs}/i, @lines);
2068 }
2069
2070 last if !@lines;
2071
2072 my @authors = ();
2073 foreach my $line (@lines) {
2074 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2075 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002076 $author = deduplicate_email($author);
2077 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002078 }
2079 }
2080
2081 save_commits_by_author(@lines) if ($interactive);
2082 save_commits_by_signer(@lines) if ($interactive);
2083
2084 push(@signers, @authors);
2085 }}
2086 else {
2087 foreach my $commit (@commits) {
2088 my $i;
2089 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2090 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2091 my @author = vcs_find_author($cmd);
2092 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002093
2094 my $formatted_author = deduplicate_email($author[0]);
2095
Joe Perches683c6f82010-10-26 14:22:55 -07002096 my $count = grep(/$commit/, @all_commits);
2097 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002098 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002099 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002100 }
2101 }
2102 if (@blame_signers) {
2103 vcs_assign("authored lines", $total_lines, @blame_signers);
2104 }
2105 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002106 foreach my $signer (@signers) {
2107 $signer = deduplicate_email($signer);
2108 }
Joe Perches60db31a2009-12-14 18:00:50 -08002109 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002110 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002111 foreach my $signer (@signers) {
2112 $signer = deduplicate_email($signer);
2113 }
Joe Perches60db31a2009-12-14 18:00:50 -08002114 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002115 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002116}
2117
2118sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002119 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002120
2121 my %saw;
2122 @parms = grep(!$saw{$_}++, @parms);
2123 return @parms;
2124}
2125
2126sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002127 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002128
2129 my %saw;
2130 @parms = sort @parms;
2131 @parms = grep(!$saw{$_}++, @parms);
2132 return @parms;
2133}
2134
Joe Perches03372db2010-03-05 13:43:00 -08002135sub clean_file_emails {
2136 my (@file_emails) = @_;
2137 my @fmt_emails = ();
2138
2139 foreach my $email (@file_emails) {
2140 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2141 my ($name, $address) = parse_email($email);
2142 if ($name eq '"[,\.]"') {
2143 $name = "";
2144 }
2145
2146 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2147 if (@nw > 2) {
2148 my $first = $nw[@nw - 3];
2149 my $middle = $nw[@nw - 2];
2150 my $last = $nw[@nw - 1];
2151
2152 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2153 (length($first) == 2 && substr($first, -1) eq ".")) ||
2154 (length($middle) == 1 ||
2155 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2156 $name = "$first $middle $last";
2157 } else {
2158 $name = "$middle $last";
2159 }
2160 }
2161
2162 if (substr($name, -1) =~ /[,\.]/) {
2163 $name = substr($name, 0, length($name) - 1);
2164 } elsif (substr($name, -2) =~ /[,\.]"/) {
2165 $name = substr($name, 0, length($name) - 2) . '"';
2166 }
2167
2168 if (substr($name, 0, 1) =~ /[,\.]/) {
2169 $name = substr($name, 1, length($name) - 1);
2170 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2171 $name = '"' . substr($name, 2, length($name) - 2);
2172 }
2173
2174 my $fmt_email = format_email($name, $address, $email_usename);
2175 push(@fmt_emails, $fmt_email);
2176 }
2177 return @fmt_emails;
2178}
2179
Joe Perches3c7385b2009-12-14 18:00:46 -08002180sub merge_email {
2181 my @lines;
2182 my %saw;
2183
2184 for (@_) {
2185 my ($address, $role) = @$_;
2186 if (!$saw{$address}) {
2187 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002188 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002189 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002190 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002191 }
2192 $saw{$address} = 1;
2193 }
2194 }
2195
2196 return @lines;
2197}
2198
Joe Perchescb7301c2009-04-07 20:40:12 -07002199sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002200 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002201
2202 if ($output_multiline) {
2203 foreach my $line (@parms) {
2204 print("${line}\n");
2205 }
2206 } else {
2207 print(join($output_separator, @parms));
2208 print("\n");
2209 }
2210}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002211
2212my $rfc822re;
2213
2214sub make_rfc822re {
2215# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2216# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2217# This regexp will only work on addresses which have had comments stripped
2218# and replaced with rfc822_lwsp.
2219
2220 my $specials = '()<>@,;:\\\\".\\[\\]';
2221 my $controls = '\\000-\\037\\177';
2222
2223 my $dtext = "[^\\[\\]\\r\\\\]";
2224 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2225
2226 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2227
2228# Use zero-width assertion to spot the limit of an atom. A simple
2229# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2230 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2231 my $word = "(?:$atom|$quoted_string)";
2232 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2233
2234 my $sub_domain = "(?:$atom|$domain_literal)";
2235 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2236
2237 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2238
2239 my $phrase = "$word*";
2240 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2241 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2242 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2243
2244 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2245 my $address = "(?:$mailbox|$group)";
2246
2247 return "$rfc822_lwsp*$address";
2248}
2249
2250sub rfc822_strip_comments {
2251 my $s = shift;
2252# Recursively remove comments, and replace with a single space. The simpler
2253# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2254# chars in atoms, for example.
2255
2256 while ($s =~ s/^((?:[^"\\]|\\.)*
2257 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2258 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2259 return $s;
2260}
2261
2262# valid: returns true if the parameter is an RFC822 valid address
2263#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002264sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002265 my $s = rfc822_strip_comments(shift);
2266
2267 if (!$rfc822re) {
2268 $rfc822re = make_rfc822re();
2269 }
2270
2271 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2272}
2273
2274# validlist: In scalar context, returns true if the parameter is an RFC822
2275# valid list of addresses.
2276#
2277# In list context, returns an empty list on failure (an invalid
2278# address was found); otherwise a list whose first element is the
2279# number of addresses found and whose remaining elements are the
2280# addresses. This is needed to disambiguate failure (invalid)
2281# from success with no addresses found, because an empty string is
2282# a valid list.
2283
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002284sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002285 my $s = rfc822_strip_comments(shift);
2286
2287 if (!$rfc822re) {
2288 $rfc822re = make_rfc822re();
2289 }
2290 # * null list items are valid according to the RFC
2291 # * the '1' business is to aid in distinguishing failure from no results
2292
2293 my @r;
2294 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2295 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002296 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002297 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002298 }
2299 return wantarray ? (scalar(@r), @r) : 1;
2300 }
Joe Perches60db31a2009-12-14 18:00:50 -08002301 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002302}