blob: 122fcdaf42c86cec7a5fbce08cc3b406f692f6c5 [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);
Joe Perchesbe17bdd2016-01-20 14:58:24 -080019use Cwd;
Joe Perchescb7301c2009-04-07 20:40:12 -070020
Joe Perchesbe17bdd2016-01-20 14:58:24 -080021my $cur_path = fastgetcwd() . '/';
Joe Perchescb7301c2009-04-07 20:40:12 -070022my $lk_path = "./";
23my $email = 1;
24my $email_usename = 1;
25my $email_maintainer = 1;
Joe Perchesc1c3f2c2014-06-02 12:05:17 -070026my $email_reviewer = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070027my $email_list = 1;
28my $email_subscriber_list = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070029my $email_git_penguin_chiefs = 0;
Joe Perchese3e9d112010-10-26 14:22:53 -070030my $email_git = 0;
Florian Mickler0fa05592010-05-24 14:33:20 -070031my $email_git_all_signature_types = 0;
Joe Perches60db31a2009-12-14 18:00:50 -080032my $email_git_blame = 0;
Joe Perches683c6f82010-10-26 14:22:55 -070033my $email_git_blame_signatures = 1;
Joe Perchese3e9d112010-10-26 14:22:53 -070034my $email_git_fallback = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070035my $email_git_min_signatures = 1;
36my $email_git_max_maintainers = 5;
Joe Perchesafa81ee2009-07-29 15:04:28 -070037my $email_git_min_percent = 5;
Joe Perchescb7301c2009-04-07 20:40:12 -070038my $email_git_since = "1-year-ago";
Joe Perches60db31a2009-12-14 18:00:50 -080039my $email_hg_since = "-365";
Florian Micklerdace8e32010-10-26 14:22:54 -070040my $interactive = 0;
Joe Perches11ecf532009-09-21 17:04:22 -070041my $email_remove_duplicates = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -070042my $email_use_mailmap = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070043my $output_multiline = 1;
44my $output_separator = ", ";
Joe Perches3c7385b2009-12-14 18:00:46 -080045my $output_roles = 0;
Joe Perches7e1863a2011-01-12 16:59:49 -080046my $output_rolestats = 1;
Joe Perches364f68d2015-06-25 15:01:52 -070047my $output_section_maxlen = 50;
Joe Perchescb7301c2009-04-07 20:40:12 -070048my $scm = 0;
49my $web = 0;
50my $subsystem = 0;
51my $status = 0;
Joe Perchesdcf36a92009-10-26 16:49:47 -070052my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080053my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080054my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070055my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070056my $pattern_depth = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070057my $version = 0;
58my $help = 0;
59
Joe Perches683c6f82010-10-26 14:22:55 -070060my $vcs_used = 0;
61
Joe Perchescb7301c2009-04-07 20:40:12 -070062my $exit = 0;
63
Joe Perches683c6f82010-10-26 14:22:55 -070064my %commit_author_hash;
65my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070066
Joe Perchescb7301c2009-04-07 20:40:12 -070067my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070068push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070069#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070070#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070071
72my @penguin_chief_names = ();
73foreach my $chief (@penguin_chief) {
74 if ($chief =~ m/^(.*):(.*)/) {
75 my $chief_name = $1;
76 my $chief_addr = $2;
77 push(@penguin_chief_names, $chief_name);
78 }
79}
Joe Perchese4d26b02010-05-24 14:33:17 -070080my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
81
82# Signature types of people who are either
83# a) responsible for the code in question, or
84# b) familiar enough with it to give relevant feedback
85my @signature_tags = ();
86push(@signature_tags, "Signed-off-by:");
87push(@signature_tags, "Reviewed-by:");
88push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070089
Joe Perches7dea2682012-06-20 12:53:02 -070090my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
91
Joe Perches5f2441e2009-06-16 15:34:02 -070092# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070093my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070094my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070095
Joe Perches60db31a2009-12-14 18:00:50 -080096# VCS command support: class-like functions and strings
97
98my %VCS_cmds;
99
100my %VCS_cmds_git = (
101 "execute_cmd" => \&git_execute_cmd,
Richard Genoudec83b612014-02-10 14:25:31 -0800102 "available" => '(which("git") ne "") && (-e ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -0700103 "find_signers_cmd" =>
Ian Campbelled128fea2012-01-10 15:08:41 -0800104 "git log --no-color --follow --since=\$email_git_since " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800105 '--numstat --no-merges ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700106 '--format="GitCommit: %H%n' .
107 'GitAuthor: %an <%ae>%n' .
108 'GitDate: %aD%n' .
109 'GitSubject: %s%n' .
110 '%b%n"' .
111 " -- \$file",
112 "find_commit_signers_cmd" =>
113 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800114 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700115 '--format="GitCommit: %H%n' .
116 'GitAuthor: %an <%ae>%n' .
117 'GitDate: %aD%n' .
118 'GitSubject: %s%n' .
119 '%b%n"' .
120 " -1 \$commit",
121 "find_commit_author_cmd" =>
122 "git log --no-color " .
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800123 '--numstat ' .
Joe Perches683c6f82010-10-26 14:22:55 -0700124 '--format="GitCommit: %H%n' .
125 'GitAuthor: %an <%ae>%n' .
126 'GitDate: %aD%n' .
127 'GitSubject: %s%n"' .
128 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800129 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
130 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700131 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700132 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700133 "author_pattern" => "^GitAuthor: (.*)",
134 "subject_pattern" => "^GitSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800135 "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700136 "file_exists_cmd" => "git ls-files \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800137);
138
139my %VCS_cmds_hg = (
140 "execute_cmd" => \&hg_execute_cmd,
141 "available" => '(which("hg") ne "") && (-d ".hg")',
142 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700143 "hg log --date=\$email_hg_since " .
144 "--template='HgCommit: {node}\\n" .
145 "HgAuthor: {author}\\n" .
146 "HgSubject: {desc}\\n'" .
147 " -- \$file",
148 "find_commit_signers_cmd" =>
149 "hg log " .
150 "--template='HgSubject: {desc}\\n'" .
151 " -r \$commit",
152 "find_commit_author_cmd" =>
153 "hg log " .
154 "--template='HgCommit: {node}\\n" .
155 "HgAuthor: {author}\\n" .
156 "HgSubject: {desc|firstline}\\n'" .
157 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800158 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700159 "blame_file_cmd" => "hg blame -n \$file",
160 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
161 "blame_commit_pattern" => "^([ 0-9a-f]+):",
162 "author_pattern" => "^HgAuthor: (.*)",
163 "subject_pattern" => "^HgSubject: (.*)",
Joe Perchesc9ecefe2014-01-23 15:54:20 -0800164 "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
Joe Perches4cad35a2016-08-02 14:04:10 -0700165 "file_exists_cmd" => "hg files \$file",
Joe Perches60db31a2009-12-14 18:00:50 -0800166);
167
Joe Perchesbcde44e2010-10-26 14:22:53 -0700168my $conf = which_conf(".get_maintainer.conf");
169if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700170 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700171 open(my $conffile, '<', "$conf")
172 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
173
Joe Perches368669d2010-05-24 14:33:19 -0700174 while (<$conffile>) {
175 my $line = $_;
176
177 $line =~ s/\s*\n?$//g;
178 $line =~ s/^\s*//g;
179 $line =~ s/\s+/ /g;
180
181 next if ($line =~ m/^\s*#/);
182 next if ($line =~ m/^\s*$/);
183
184 my @words = split(" ", $line);
185 foreach my $word (@words) {
186 last if ($word =~ m/^#/);
187 push (@conf_args, $word);
188 }
189 }
190 close($conffile);
191 unshift(@ARGV, @conf_args) if @conf_args;
192}
193
Joe Perches435de072015-06-25 15:01:50 -0700194my @ignore_emails = ();
195my $ignore_file = which_conf(".get_maintainer.ignore");
196if (-f $ignore_file) {
197 open(my $ignore, '<', "$ignore_file")
198 or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
199 while (<$ignore>) {
200 my $line = $_;
201
202 $line =~ s/\s*\n?$//;
203 $line =~ s/^\s*//;
204 $line =~ s/\s+$//;
205 $line =~ s/#.*$//;
206
207 next if ($line =~ m/^\s*$/);
208 if (rfc822_valid($line)) {
209 push(@ignore_emails, $line);
210 }
211 }
212 close($ignore);
213}
214
Joe Perchescb7301c2009-04-07 20:40:12 -0700215if (!GetOptions(
216 'email!' => \$email,
217 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700218 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800219 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700220 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700221 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700222 'git-chief-penguins!' => \$email_git_penguin_chiefs,
223 'git-min-signatures=i' => \$email_git_min_signatures,
224 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700225 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700226 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800227 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700228 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700229 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchesb9e23312010-10-26 14:22:58 -0700230 'mailmap!' => \$email_use_mailmap,
Joe Perchescb7301c2009-04-07 20:40:12 -0700231 'm!' => \$email_maintainer,
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700232 'r!' => \$email_reviewer,
Joe Perchescb7301c2009-04-07 20:40:12 -0700233 'n!' => \$email_usename,
234 'l!' => \$email_list,
235 's!' => \$email_subscriber_list,
236 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800237 'roles!' => \$output_roles,
238 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700239 'separator=s' => \$output_separator,
240 'subsystem!' => \$subsystem,
241 'status!' => \$status,
242 'scm!' => \$scm,
243 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700244 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700245 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800246 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800247 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700248 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700249 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800250 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700251 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800252 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700253}
254
255if ($help != 0) {
256 usage();
257 exit 0;
258}
259
260if ($version != 0) {
261 print("${P} ${V}\n");
262 exit 0;
263}
264
Joe Perches64f77f32010-03-05 13:43:04 -0800265if (-t STDIN && !@ARGV) {
266 # We're talking to a terminal, but have no command line arguments.
267 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700268}
269
Joe Perches683c6f82010-10-26 14:22:55 -0700270$output_multiline = 0 if ($output_separator ne ", ");
271$output_rolestats = 1 if ($interactive);
272$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800273
Joe Perches4b76c9d2010-03-05 13:43:03 -0800274if ($sections) {
275 $email = 0;
276 $email_list = 0;
277 $scm = 0;
278 $status = 0;
279 $subsystem = 0;
280 $web = 0;
281 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700282 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800283} else {
284 my $selections = $email + $scm + $status + $subsystem + $web;
285 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800286 die "$P: Missing required option: email, scm, status, subsystem or web\n";
287 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700288}
289
Joe Perchesf5492662009-09-21 17:04:13 -0700290if ($email &&
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700291 ($email_maintainer + $email_reviewer +
292 $email_list + $email_subscriber_list +
Joe Perchesf5492662009-09-21 17:04:13 -0700293 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700294 die "$P: Please select at least 1 email option\n";
295}
296
297if (!top_of_kernel_tree($lk_path)) {
298 die "$P: The current directory does not appear to be "
299 . "a linux kernel source tree.\n";
300}
301
302## Read MAINTAINERS for type/value pairs
303
304my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700305my %keyword_hash;
306
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800307open (my $maint, '<', "${lk_path}MAINTAINERS")
308 or die "$P: Can't open MAINTAINERS: $!\n";
309while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700310 my $line = $_;
311
Joe Perchesce8155f2015-06-25 15:01:55 -0700312 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700313 my $type = $1;
314 my $value = $2;
315
316 ##Filename pattern matching
317 if ($type eq "F" || $type eq "X") {
318 $value =~ s@\.@\\\.@g; ##Convert . to \.
319 $value =~ s/\*/\.\*/g; ##Convert * to .*
320 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700321 ##if pattern is a directory and it lacks a trailing slash, add one
322 if ((-d $value)) {
323 $value =~ s@([^/])$@$1/@;
324 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700325 } elsif ($type eq "K") {
326 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700327 }
328 push(@typevalue, "$type:$value");
329 } elsif (!/^(\s)*$/) {
330 $line =~ s/\n$//g;
331 push(@typevalue, $line);
332 }
333}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800334close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700335
Joe Perches8cbb3a72009-09-21 17:04:21 -0700336
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700337#
338# Read mail address map
339#
340
Joe Perchesb9e23312010-10-26 14:22:58 -0700341my $mailmap;
342
343read_mailmap();
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700344
345sub read_mailmap {
Joe Perchesb9e23312010-10-26 14:22:58 -0700346 $mailmap = {
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700347 names => {},
348 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700349 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700350
Joe Perchesb9e23312010-10-26 14:22:58 -0700351 return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700352
353 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800354 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700355
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700356 while (<$mailmap_file>) {
357 s/#.*$//; #strip comments
358 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700359
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700360 next if (/^\s*$/); #skip empty lines
361 #entries have one of the following formats:
362 # name1 <mail1>
363 # <mail1> <mail2>
364 # name1 <mail1> <mail2>
365 # name1 <mail1> name2 <mail2>
366 # (see man git-shortlog)
Joe Perches0334b382011-07-25 17:13:13 -0700367
368 if (/^([^<]+)<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700369 my $real_name = $1;
370 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700371
Joe Perches47abc722010-10-26 14:22:57 -0700372 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700373 ($real_name, $address) = parse_email("$real_name <$address>");
Joe Perches47abc722010-10-26 14:22:57 -0700374 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700375
Joe Perches0334b382011-07-25 17:13:13 -0700376 } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700377 my $real_address = $1;
378 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700379
Joe Perches47abc722010-10-26 14:22:57 -0700380 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700381
Joe Perches0334b382011-07-25 17:13:13 -0700382 } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700383 my $real_name = $1;
Joe Perches47abc722010-10-26 14:22:57 -0700384 my $real_address = $2;
385 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700386
Joe Perches47abc722010-10-26 14:22:57 -0700387 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700388 ($real_name, $real_address) =
389 parse_email("$real_name <$real_address>");
Joe Perches47abc722010-10-26 14:22:57 -0700390 $mailmap->{names}->{$wrong_address} = $real_name;
391 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700392
Joe Perches0334b382011-07-25 17:13:13 -0700393 } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700394 my $real_name = $1;
395 my $real_address = $2;
396 my $wrong_name = $3;
397 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700398
Joe Perches47abc722010-10-26 14:22:57 -0700399 $real_name =~ s/\s+$//;
Joe Perchesb9e23312010-10-26 14:22:58 -0700400 ($real_name, $real_address) =
401 parse_email("$real_name <$real_address>");
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700402
Joe Perchesb9e23312010-10-26 14:22:58 -0700403 $wrong_name =~ s/\s+$//;
404 ($wrong_name, $wrong_address) =
405 parse_email("$wrong_name <$wrong_address>");
406
407 my $wrong_email = format_email($wrong_name, $wrong_address, 1);
408 $mailmap->{names}->{$wrong_email} = $real_name;
409 $mailmap->{addresses}->{$wrong_email} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700410 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700411 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700412 close($mailmap_file);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700413}
414
Joe Perches4a7fdb52009-04-10 12:28:57 -0700415## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700416
417my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700418my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700419my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800420my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700421
Joe Perches64f77f32010-03-05 13:43:04 -0800422if (!@ARGV) {
423 push(@ARGV, "&STDIN");
424}
425
Joe Perches4a7fdb52009-04-10 12:28:57 -0700426foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800427 if ($file ne "&STDIN") {
428 ##if $file is a directory and it lacks a trailing slash, add one
429 if ((-d $file)) {
430 $file =~ s@([^/])$@$1/@;
431 } elsif (!(-f $file)) {
432 die "$P: file '${file}' not found\n";
433 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700434 }
Joe Perches4cad35a2016-08-02 14:04:10 -0700435 if ($from_filename || vcs_file_exists($file)) {
Joe Perchesbe17bdd2016-01-20 14:58:24 -0800436 $file =~ s/^\Q${cur_path}\E//; #strip any absolute path
437 $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree
Joe Perches4a7fdb52009-04-10 12:28:57 -0700438 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700439 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800440 open(my $f, '<', $file)
441 or die "$P: Can't open $file: $!\n";
442 my $text = do { local($/) ; <$f> };
443 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800444 if ($keywords) {
445 foreach my $line (keys %keyword_hash) {
446 if ($text =~ m/$keyword_hash{$line}/x) {
447 push(@keyword_tvi, $line);
448 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700449 }
450 }
Joe Perches03372db2010-03-05 13:43:00 -0800451 if ($file_emails) {
452 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;
453 push(@file_emails, clean_file_emails(@poss_addr));
454 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700455 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700456 } else {
457 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700458 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800459
Wolfram Sang3a4df132010-03-23 13:35:18 -0700460 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800461 or die "$P: Can't open $file: $!\n";
Joe Perches7764dcb2011-03-22 16:34:24 -0700462
463 # We can check arbitrary information before the patch
464 # like the commit message, mail headers, etc...
465 # This allows us to match arbitrary keywords against any part
466 # of a git format-patch generated file (subject tags, etc...)
467
468 my $patch_prefix = ""; #Parsing the intro
469
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800470 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700471 my $patch_line = $_;
Geert Uytterhoeven6be07102013-02-21 16:43:12 -0800472 if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
Joe Perches4a7fdb52009-04-10 12:28:57 -0700473 my $filename = $1;
474 $filename =~ s@^[^/]*/@@;
475 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700476 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700477 push(@files, $filename);
Joe Perches7764dcb2011-03-22 16:34:24 -0700478 $patch_prefix = "^[+-].*"; #Now parsing the actual patch
Joe Perchesf5492662009-09-21 17:04:13 -0700479 } elsif (m/^\@\@ -(\d+),(\d+)/) {
480 if ($email_git_blame) {
481 push(@range, "$lastfile:$1:$2");
482 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700483 } elsif ($keywords) {
484 foreach my $line (keys %keyword_hash) {
Joe Perches7764dcb2011-03-22 16:34:24 -0700485 if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700486 push(@keyword_tvi, $line);
487 }
488 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700489 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700490 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800491 close($patch);
492
Joe Perches4a7fdb52009-04-10 12:28:57 -0700493 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700494 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700495 . "Add -f to options?\n";
496 }
497 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700498 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700499}
500
Joe Perches03372db2010-03-05 13:43:00 -0800501@file_emails = uniq(@file_emails);
502
Joe Perches683c6f82010-10-26 14:22:55 -0700503my %email_hash_name;
504my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700505my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700506my %hash_list_to;
Joe Perches290603c2009-06-16 15:33:58 -0700507my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700508my @scm = ();
509my @web = ();
510my @subsystem = ();
511my @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700512my %deduplicate_name_hash = ();
513my %deduplicate_address_hash = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700514
Joe Perches6ef1c522010-10-26 14:22:56 -0700515my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700516
Joe Perches6ef1c522010-10-26 14:22:56 -0700517if (@maintainers) {
518 @maintainers = merge_email(@maintainers);
519 output(@maintainers);
520}
Joe Perchescb7301c2009-04-07 20:40:12 -0700521
522if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700523 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700524 output(@scm);
525}
Joe Perches683c6f82010-10-26 14:22:55 -0700526
Joe Perchescb7301c2009-04-07 20:40:12 -0700527if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700528 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700529 output(@status);
530}
531
532if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700533 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700534 output(@subsystem);
535}
536
537if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700538 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700539 output(@web);
540}
541
542exit($exit);
543
Joe Perches435de072015-06-25 15:01:50 -0700544sub ignore_email_address {
545 my ($address) = @_;
546
547 foreach my $ignore (@ignore_emails) {
548 return 1 if ($ignore eq $address);
549 }
550
551 return 0;
552}
553
Joe Perchesab6c9372011-01-12 16:59:50 -0800554sub range_is_maintained {
555 my ($start, $end) = @_;
556
557 for (my $i = $start; $i < $end; $i++) {
558 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700559 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800560 my $type = $1;
561 my $value = $2;
562 if ($type eq 'S') {
563 if ($value =~ /(maintain|support)/i) {
564 return 1;
565 }
566 }
567 }
568 }
569 return 0;
570}
571
572sub range_has_maintainer {
573 my ($start, $end) = @_;
574
575 for (my $i = $start; $i < $end; $i++) {
576 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700577 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesab6c9372011-01-12 16:59:50 -0800578 my $type = $1;
579 my $value = $2;
580 if ($type eq 'M') {
581 return 1;
582 }
583 }
584 }
585 return 0;
586}
587
Joe Perches6ef1c522010-10-26 14:22:56 -0700588sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700589 %email_hash_name = ();
590 %email_hash_address = ();
591 %commit_author_hash = ();
592 %commit_signer_hash = ();
593 @email_to = ();
594 %hash_list_to = ();
595 @list_to = ();
596 @scm = ();
597 @web = ();
598 @subsystem = ();
599 @status = ();
Joe Perchesb9e23312010-10-26 14:22:58 -0700600 %deduplicate_name_hash = ();
601 %deduplicate_address_hash = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700602 if ($email_git_all_signature_types) {
603 $signature_pattern = "(.+?)[Bb][Yy]:";
604 } else {
605 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
606 }
607
608 # Find responsible parties
609
Joe Perchesb9e23312010-10-26 14:22:58 -0700610 my %exact_pattern_match_hash = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700611
Joe Perches683c6f82010-10-26 14:22:55 -0700612 foreach my $file (@files) {
613
614 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700615 my $tvi = find_first_section();
616 while ($tvi < @typevalue) {
617 my $start = find_starting_index($tvi);
618 my $end = find_ending_index($tvi);
619 my $exclude = 0;
620 my $i;
621
622 #Do not match excluded file patterns
623
624 for ($i = $start; $i < $end; $i++) {
625 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700626 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700627 my $type = $1;
628 my $value = $2;
629 if ($type eq 'X') {
630 if (file_match_pattern($file, $value)) {
631 $exclude = 1;
632 last;
633 }
634 }
635 }
636 }
637
638 if (!$exclude) {
639 for ($i = $start; $i < $end; $i++) {
640 my $line = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -0700641 if ($line =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches683c6f82010-10-26 14:22:55 -0700642 my $type = $1;
643 my $value = $2;
644 if ($type eq 'F') {
645 if (file_match_pattern($file, $value)) {
646 my $value_pd = ($value =~ tr@/@@);
647 my $file_pd = ($file =~ tr@/@@);
648 $value_pd++ if (substr($value,-1,1) ne "/");
649 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perchesab6c9372011-01-12 16:59:50 -0800650 if ($value_pd >= $file_pd &&
651 range_is_maintained($start, $end) &&
652 range_has_maintainer($start, $end)) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700653 $exact_pattern_match_hash{$file} = 1;
654 }
Joe Perches683c6f82010-10-26 14:22:55 -0700655 if ($pattern_depth == 0 ||
656 (($file_pd - $value_pd) < $pattern_depth)) {
657 $hash{$tvi} = $value_pd;
658 }
659 }
Stephen Warrenbbbe96e2013-04-29 16:17:23 -0700660 } elsif ($type eq 'N') {
Stephen Warreneb90d082013-02-27 17:02:53 -0800661 if ($file =~ m/$value/x) {
662 $hash{$tvi} = 0;
663 }
Joe Perches683c6f82010-10-26 14:22:55 -0700664 }
665 }
666 }
667 }
668 $tvi = $end + 1;
669 }
670
671 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
672 add_categories($line);
673 if ($sections) {
674 my $i;
675 my $start = find_starting_index($line);
676 my $end = find_ending_index($line);
677 for ($i = $start; $i < $end; $i++) {
678 my $line = $typevalue[$i];
679 if ($line =~ /^[FX]:/) { ##Restore file patterns
680 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
681 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
682 $line =~ s/\\\./\./g; ##Convert \. to .
683 $line =~ s/\.\*/\*/g; ##Convert .* to *
684 }
685 $line =~ s/^([A-Z]):/$1:\t/g;
686 print("$line\n");
687 }
688 print("\n");
689 }
690 }
Joe Perches683c6f82010-10-26 14:22:55 -0700691 }
692
693 if ($keywords) {
694 @keyword_tvi = sort_and_uniq(@keyword_tvi);
695 foreach my $line (@keyword_tvi) {
696 add_categories($line);
697 }
698 }
699
Joe Perchesb9e23312010-10-26 14:22:58 -0700700 foreach my $email (@email_to, @list_to) {
701 $email->[0] = deduplicate_email($email->[0]);
702 }
Joe Perches6ef1c522010-10-26 14:22:56 -0700703
704 foreach my $file (@files) {
705 if ($email &&
706 ($email_git || ($email_git_fallback &&
707 !$exact_pattern_match_hash{$file}))) {
708 vcs_file_signoffs($file);
709 }
710 if ($email && $email_git_blame) {
711 vcs_file_blame($file);
712 }
713 }
714
Joe Perches683c6f82010-10-26 14:22:55 -0700715 if ($email) {
716 foreach my $chief (@penguin_chief) {
717 if ($chief =~ m/^(.*):(.*)/) {
718 my $email_address;
719
720 $email_address = format_email($1, $2, $email_usename);
721 if ($email_git_penguin_chiefs) {
722 push(@email_to, [$email_address, 'chief penguin']);
723 } else {
724 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
725 }
726 }
727 }
728
729 foreach my $email (@file_emails) {
730 my ($name, $address) = parse_email($email);
731
732 my $tmp_email = format_email($name, $address, $email_usename);
733 push_email_address($tmp_email, '');
734 add_role($tmp_email, 'in file');
735 }
736 }
737
738 my @to = ();
739 if ($email || $email_list) {
740 if ($email) {
741 @to = (@to, @email_to);
742 }
743 if ($email_list) {
744 @to = (@to, @list_to);
745 }
746 }
747
Joe Perches6ef1c522010-10-26 14:22:56 -0700748 if ($interactive) {
Joe Perchesb9e23312010-10-26 14:22:58 -0700749 @to = interactive_get_maintainers(\@to);
Joe Perches6ef1c522010-10-26 14:22:56 -0700750 }
Joe Perches683c6f82010-10-26 14:22:55 -0700751
752 return @to;
753}
754
Joe Perchescb7301c2009-04-07 20:40:12 -0700755sub file_match_pattern {
756 my ($file, $pattern) = @_;
757 if (substr($pattern, -1) eq "/") {
758 if ($file =~ m@^$pattern@) {
759 return 1;
760 }
761 } else {
762 if ($file =~ m@^$pattern@) {
763 my $s1 = ($file =~ tr@/@@);
764 my $s2 = ($pattern =~ tr@/@@);
765 if ($s1 == $s2) {
766 return 1;
767 }
768 }
769 }
770 return 0;
771}
772
773sub usage {
774 print <<EOT;
775usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700776 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700777version: $V
778
779MAINTAINER field selection options:
780 --email => print email address(es) if any
781 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700782 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700783 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700784 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700785 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700786 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
787 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
788 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700789 --git-blame => use git blame to find modified commits for patch or file
Brian Norris3cbcca82015-11-06 16:30:41 -0800790 --git-blame-signatures => when used with --git-blame, also include all commit signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700791 --git-since => git history to use (default: $email_git_since)
792 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700793 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700794 --m => include maintainer(s) if any
Joe Perchesc1c3f2c2014-06-02 12:05:17 -0700795 --r => include reviewer(s) if any
Joe Perchescb7301c2009-04-07 20:40:12 -0700796 --n => include name 'Full Name <addr\@domain.tld>'
797 --l => include list(s) if any
798 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700799 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800800 --roles => show roles (status:subsystem, git-signer, list, etc...)
801 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800802 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700803 --scm => print SCM tree(s) if any
804 --status => print status if any
805 --subsystem => print subsystem name if any
806 --web => print website(s) if any
807
808Output type options:
809 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700810 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700811 --multiline => print 1 entry per line
812
Joe Perchescb7301c2009-04-07 20:40:12 -0700813Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700814 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesb9e23312010-10-26 14:22:58 -0700815 --keywords => scan patch for keywords (default: $keywords)
816 --sections => print all of the subsystem sections with pattern matches
817 --mailmap => use .mailmap file (default: $email_use_mailmap)
Joe Perchesf5f50782009-06-16 15:34:00 -0700818 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700819 --help => show this help information
820
Joe Perches3fb55652009-09-21 17:04:17 -0700821Default options:
Brian Norris4f075102015-11-06 16:30:49 -0800822 [--email --nogit --git-fallback --m --r --n --l --multiline --pattern-depth=0
Joe Perches7e1863a2011-01-12 16:59:49 -0800823 --remove-duplicates --rolestats]
Joe Perches3fb55652009-09-21 17:04:17 -0700824
Joe Perches870020f2009-07-29 15:04:28 -0700825Notes:
826 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700827 Used with "--git", git signators for _all_ files in and below
828 directory are examined as git recurses directories.
829 Any specified X: (exclude) pattern matches are _not_ ignored.
830 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800831 no individual file within the directory or subdirectory
832 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700833 Used with "--git-blame", does not iterate all files in directory
834 Using "--git-blame" is slow and may add old committers and authors
835 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800836 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
837 other automated tools that expect only ["name"] <email address>
838 may not work because of additional output after <email address>.
839 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
840 not the percentage of the entire file authored. # of commits is
841 not a good measure of amount of code authored. 1 major commit may
842 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800843 If git is not installed, but mercurial (hg) is installed and an .hg
844 repository exists, the following options apply to mercurial:
845 --git,
846 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
847 --git-blame
848 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700849 File ".get_maintainer.conf", if it exists in the linux kernel source root
850 directory, can change whatever get_maintainer defaults are desired.
851 Entries in this file can be any command line argument.
852 This file is prepended to any additional command line arguments.
853 Multiple lines and # comments are allowed.
Brian Norrisb1312bfe2015-11-06 16:30:46 -0800854 Most options have both positive and negative forms.
855 The negative forms for --<foo> are --no<foo> and --no-<foo>.
856
Joe Perchescb7301c2009-04-07 20:40:12 -0700857EOT
858}
859
860sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700861 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700862
Joe Perches47abc722010-10-26 14:22:57 -0700863 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
864 $lk_path .= "/";
865 }
866 if ( (-f "${lk_path}COPYING")
867 && (-f "${lk_path}CREDITS")
868 && (-f "${lk_path}Kbuild")
869 && (-f "${lk_path}MAINTAINERS")
870 && (-f "${lk_path}Makefile")
871 && (-f "${lk_path}README")
872 && (-d "${lk_path}Documentation")
873 && (-d "${lk_path}arch")
874 && (-d "${lk_path}include")
875 && (-d "${lk_path}drivers")
876 && (-d "${lk_path}fs")
877 && (-d "${lk_path}init")
878 && (-d "${lk_path}ipc")
879 && (-d "${lk_path}kernel")
880 && (-d "${lk_path}lib")
881 && (-d "${lk_path}scripts")) {
882 return 1;
883 }
884 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700885}
886
Joe Perches0e70e832009-09-21 17:04:20 -0700887sub parse_email {
888 my ($formatted_email) = @_;
889
890 my $name = "";
891 my $address = "";
892
Joe Perches11ecf532009-09-21 17:04:22 -0700893 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700894 $name = $1;
895 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700896 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700897 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700898 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700899 $address = $1;
900 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700901
902 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700903 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700904 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700905
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800906 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700907 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700908 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700909 }
Joe Perches0e70e832009-09-21 17:04:20 -0700910
911 return ($name, $address);
912}
913
914sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800915 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700916
917 my $formatted_email;
918
919 $name =~ s/^\s+|\s+$//g;
920 $name =~ s/^\"|\"$//g;
921 $address =~ s/^\s+|\s+$//g;
922
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800923 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700924 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
925 $name = "\"$name\"";
926 }
927
Joe Perchesa8af2432009-12-14 18:00:49 -0800928 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700929 if ("$name" eq "") {
930 $formatted_email = "$address";
931 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800932 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700933 }
934 } else {
935 $formatted_email = $address;
936 }
937
Joe Perchescb7301c2009-04-07 20:40:12 -0700938 return $formatted_email;
939}
940
Joe Perches272a8972010-01-08 14:42:48 -0800941sub find_first_section {
942 my $index = 0;
943
944 while ($index < @typevalue) {
945 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700946 if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perches272a8972010-01-08 14:42:48 -0800947 last;
948 }
949 $index++;
950 }
951
952 return $index;
953}
954
Joe Perchesb7816552009-09-21 17:04:24 -0700955sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700956 my ($index) = @_;
957
958 while ($index > 0) {
959 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700960 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -0700961 last;
962 }
963 $index--;
964 }
965
966 return $index;
967}
968
969sub find_ending_index {
970 my ($index) = @_;
971
972 while ($index < @typevalue) {
973 my $tv = $typevalue[$index];
Joe Perchesce8155f2015-06-25 15:01:55 -0700974 if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
Joe Perchesb7816552009-09-21 17:04:24 -0700975 last;
976 }
977 $index++;
978 }
979
980 return $index;
981}
982
Joe Perches2a7cb1d2015-11-06 16:30:52 -0800983sub get_subsystem_name {
984 my ($index) = @_;
985
986 my $start = find_starting_index($index);
987
988 my $subsystem = $typevalue[$start];
989 if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
990 $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
991 $subsystem =~ s/\s*$//;
992 $subsystem = $subsystem . "...";
993 }
994 return $subsystem;
995}
996
Joe Perches3c7385b2009-12-14 18:00:46 -0800997sub get_maintainer_role {
998 my ($index) = @_;
999
1000 my $i;
1001 my $start = find_starting_index($index);
1002 my $end = find_ending_index($index);
1003
Joe Perches0ede2742012-03-23 15:01:56 -07001004 my $role = "unknown";
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001005 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001006
1007 for ($i = $start + 1; $i < $end; $i++) {
1008 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001009 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001010 my $ptype = $1;
1011 my $pvalue = $2;
1012 if ($ptype eq "S") {
1013 $role = $pvalue;
1014 }
1015 }
1016 }
1017
1018 $role = lc($role);
1019 if ($role eq "supported") {
1020 $role = "supporter";
1021 } elsif ($role eq "maintained") {
1022 $role = "maintainer";
1023 } elsif ($role eq "odd fixes") {
1024 $role = "odd fixer";
1025 } elsif ($role eq "orphan") {
1026 $role = "orphan minder";
1027 } elsif ($role eq "obsolete") {
1028 $role = "obsolete minder";
1029 } elsif ($role eq "buried alive in reporters") {
1030 $role = "chief penguin";
1031 }
1032
1033 return $role . ":" . $subsystem;
1034}
1035
1036sub get_list_role {
1037 my ($index) = @_;
1038
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001039 my $subsystem = get_subsystem_name($index);
Joe Perches3c7385b2009-12-14 18:00:46 -08001040
1041 if ($subsystem eq "THE REST") {
1042 $subsystem = "";
1043 }
1044
1045 return $subsystem;
1046}
1047
Joe Perchescb7301c2009-04-07 20:40:12 -07001048sub add_categories {
1049 my ($index) = @_;
1050
Joe Perchesb7816552009-09-21 17:04:24 -07001051 my $i;
1052 my $start = find_starting_index($index);
1053 my $end = find_ending_index($index);
1054
1055 push(@subsystem, $typevalue[$start]);
1056
1057 for ($i = $start + 1; $i < $end; $i++) {
1058 my $tv = $typevalue[$i];
Joe Perchesce8155f2015-06-25 15:01:55 -07001059 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001060 my $ptype = $1;
1061 my $pvalue = $2;
1062 if ($ptype eq "L") {
Joe Perches290603c2009-06-16 15:33:58 -07001063 my $list_address = $pvalue;
1064 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -08001065 my $list_role = get_list_role($i);
1066
1067 if ($list_role ne "") {
1068 $list_role = ":" . $list_role;
1069 }
Joe Perches290603c2009-06-16 15:33:58 -07001070 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
1071 $list_address = $1;
1072 $list_additional = $2;
1073 }
Joe Perchesbdf7c682009-06-16 15:33:59 -07001074 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001075 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001076 if (!$hash_list_to{lc($list_address)}) {
1077 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001078 push(@list_to, [$list_address,
1079 "subscriber list${list_role}"]);
1080 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001081 }
1082 } else {
1083 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001084 if (!$hash_list_to{lc($list_address)}) {
1085 $hash_list_to{lc($list_address)} = 1;
Richard Weinberger728f5a92012-03-23 15:01:56 -07001086 if ($list_additional =~ m/moderated/) {
1087 push(@list_to, [$list_address,
1088 "moderated list${list_role}"]);
1089 } else {
1090 push(@list_to, [$list_address,
1091 "open list${list_role}"]);
1092 }
Joe Perches683c6f82010-10-26 14:22:55 -07001093 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001094 }
1095 }
1096 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -07001097 my ($name, $address) = parse_email($pvalue);
1098 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -07001099 if ($i > 0) {
1100 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001101 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perches0e70e832009-09-21 17:04:20 -07001102 if ($1 eq "P") {
1103 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -08001104 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -07001105 }
1106 }
1107 }
1108 }
Joe Perches0e70e832009-09-21 17:04:20 -07001109 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001110 my $role = get_maintainer_role($i);
1111 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -07001112 }
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001113 } elsif ($ptype eq "R") {
1114 my ($name, $address) = parse_email($pvalue);
1115 if ($name eq "") {
1116 if ($i > 0) {
1117 my $tv = $typevalue[$i - 1];
Joe Perchesce8155f2015-06-25 15:01:55 -07001118 if ($tv =~ m/^([A-Z]):\s*(.*)/) {
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001119 if ($1 eq "P") {
1120 $name = $2;
1121 $pvalue = format_email($name, $address, $email_usename);
1122 }
1123 }
1124 }
1125 }
1126 if ($email_reviewer) {
Joe Perches2a7cb1d2015-11-06 16:30:52 -08001127 my $subsystem = get_subsystem_name($i);
1128 push_email_addresses($pvalue, "reviewer:$subsystem");
Joe Perchesc1c3f2c2014-06-02 12:05:17 -07001129 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001130 } elsif ($ptype eq "T") {
1131 push(@scm, $pvalue);
1132 } elsif ($ptype eq "W") {
1133 push(@web, $pvalue);
1134 } elsif ($ptype eq "S") {
1135 push(@status, $pvalue);
1136 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001137 }
1138 }
1139}
1140
Joe Perches11ecf532009-09-21 17:04:22 -07001141sub email_inuse {
1142 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001143
Joe Perches11ecf532009-09-21 17:04:22 -07001144 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001145 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1146 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001147
Joe Perches0e70e832009-09-21 17:04:20 -07001148 return 0;
1149}
1150
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001151sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001152 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001153
Joe Perches0e70e832009-09-21 17:04:20 -07001154 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001155
Joe Perchesb7816552009-09-21 17:04:24 -07001156 if ($address eq "") {
1157 return 0;
1158 }
1159
Joe Perches11ecf532009-09-21 17:04:22 -07001160 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001161 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001162 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001163 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perchesfae99202010-10-26 14:22:58 -07001164 $email_hash_name{lc($name)}++ if ($name ne "");
Joe Perches6ef1c522010-10-26 14:22:56 -07001165 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001166 }
Joe Perchesb7816552009-09-21 17:04:24 -07001167
1168 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001169}
1170
1171sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001172 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001173
1174 my @address_list = ();
1175
Joe Perches5f2441e2009-06-16 15:34:02 -07001176 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001177 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001178 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001179 my $array_count = shift(@address_list);
1180 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001181 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001182 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001183 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001184 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001185 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1186 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001187 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001188}
1189
Joe Perches3c7385b2009-12-14 18:00:46 -08001190sub add_role {
1191 my ($line, $role) = @_;
1192
1193 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001194 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001195
1196 foreach my $entry (@email_to) {
1197 if ($email_remove_duplicates) {
1198 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001199 if (($name eq $entry_name || $address eq $entry_address)
1200 && ($role eq "" || !($entry->[1] =~ m/$role/))
1201 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001202 if ($entry->[1] eq "") {
1203 $entry->[1] = "$role";
1204 } else {
1205 $entry->[1] = "$entry->[1],$role";
1206 }
1207 }
1208 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001209 if ($email eq $entry->[0]
1210 && ($role eq "" || !($entry->[1] =~ m/$role/))
1211 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001212 if ($entry->[1] eq "") {
1213 $entry->[1] = "$role";
1214 } else {
1215 $entry->[1] = "$entry->[1],$role";
1216 }
1217 }
1218 }
1219 }
1220}
1221
Joe Perchescb7301c2009-04-07 20:40:12 -07001222sub which {
1223 my ($bin) = @_;
1224
Joe Perchesf5f50782009-06-16 15:34:00 -07001225 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001226 if (-e "$path/$bin") {
1227 return "$path/$bin";
1228 }
1229 }
1230
1231 return "";
1232}
1233
Joe Perchesbcde44e2010-10-26 14:22:53 -07001234sub which_conf {
1235 my ($conf) = @_;
1236
1237 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1238 if (-e "$path/$conf") {
1239 return "$path/$conf";
1240 }
1241 }
1242
1243 return "";
1244}
1245
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001246sub mailmap_email {
Joe Perchesb9e23312010-10-26 14:22:58 -07001247 my ($line) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001248
Joe Perches47abc722010-10-26 14:22:57 -07001249 my ($name, $address) = parse_email($line);
1250 my $email = format_email($name, $address, 1);
1251 my $real_name = $name;
1252 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001253
Joe Perches47abc722010-10-26 14:22:57 -07001254 if (exists $mailmap->{names}->{$email} ||
1255 exists $mailmap->{addresses}->{$email}) {
1256 if (exists $mailmap->{names}->{$email}) {
1257 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001258 }
Joe Perches47abc722010-10-26 14:22:57 -07001259 if (exists $mailmap->{addresses}->{$email}) {
1260 $real_address = $mailmap->{addresses}->{$email};
1261 }
1262 } else {
1263 if (exists $mailmap->{names}->{$address}) {
1264 $real_name = $mailmap->{names}->{$address};
1265 }
1266 if (exists $mailmap->{addresses}->{$address}) {
1267 $real_address = $mailmap->{addresses}->{$address};
1268 }
1269 }
1270 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001271}
1272
1273sub mailmap {
1274 my (@addresses) = @_;
1275
Joe Perchesb9e23312010-10-26 14:22:58 -07001276 my @mapped_emails = ();
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001277 foreach my $line (@addresses) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001278 push(@mapped_emails, mailmap_email($line));
Joe Perches8cbb3a72009-09-21 17:04:21 -07001279 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001280 merge_by_realname(@mapped_emails) if ($email_use_mailmap);
1281 return @mapped_emails;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001282}
1283
1284sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001285 my %address_map;
1286 my (@emails) = @_;
Joe Perchesb9e23312010-10-26 14:22:58 -07001287
Joe Perches47abc722010-10-26 14:22:57 -07001288 foreach my $email (@emails) {
1289 my ($name, $address) = parse_email($email);
Joe Perchesb9e23312010-10-26 14:22:58 -07001290 if (exists $address_map{$name}) {
Joe Perches47abc722010-10-26 14:22:57 -07001291 $address = $address_map{$name};
Joe Perchesb9e23312010-10-26 14:22:58 -07001292 $email = format_email($name, $address, 1);
1293 } else {
1294 $address_map{$name} = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001295 }
Joe Perches47abc722010-10-26 14:22:57 -07001296 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001297}
1298
Joe Perches60db31a2009-12-14 18:00:50 -08001299sub git_execute_cmd {
1300 my ($cmd) = @_;
1301 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001302
Joe Perches60db31a2009-12-14 18:00:50 -08001303 my $output = `$cmd`;
1304 $output =~ s/^\s*//gm;
1305 @lines = split("\n", $output);
1306
1307 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001308}
1309
Joe Perches60db31a2009-12-14 18:00:50 -08001310sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001311 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001312 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001313
Joe Perches60db31a2009-12-14 18:00:50 -08001314 my $output = `$cmd`;
1315 @lines = split("\n", $output);
1316
1317 return @lines;
1318}
1319
Joe Perches683c6f82010-10-26 14:22:55 -07001320sub extract_formatted_signatures {
1321 my (@signature_lines) = @_;
1322
1323 my @type = @signature_lines;
1324
1325 s/\s*(.*):.*/$1/ for (@type);
1326
1327 # cut -f2- -d":"
1328 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1329
1330## Reformat email addresses (with names) to avoid badly written signatures
1331
1332 foreach my $signer (@signature_lines) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001333 $signer = deduplicate_email($signer);
Joe Perches683c6f82010-10-26 14:22:55 -07001334 }
1335
1336 return (\@type, \@signature_lines);
1337}
1338
Joe Perches60db31a2009-12-14 18:00:50 -08001339sub vcs_find_signers {
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001340 my ($cmd, $file) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001341 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001342 my @lines = ();
1343 my @signatures = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001344 my @authors = ();
1345 my @stats = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001346
Joe Perches60db31a2009-12-14 18:00:50 -08001347 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001348
Joe Perches60db31a2009-12-14 18:00:50 -08001349 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001350 my $author_pattern = $VCS_cmds{"author_pattern"};
1351 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1352
1353 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
Joe Perchescb7301c2009-04-07 20:40:12 -07001354
Joe Perches60db31a2009-12-14 18:00:50 -08001355 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001356
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001357 @authors = grep(/$author_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001358 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001359 @stats = grep(/$stat_pattern/, @lines);
Joe Perches683c6f82010-10-26 14:22:55 -07001360
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001361# print("stats: <@stats>\n");
1362
1363 return (0, \@signatures, \@authors, \@stats) if !@signatures;
Joe Perches683c6f82010-10-26 14:22:55 -07001364
1365 save_commits_by_author(@lines) if ($interactive);
1366 save_commits_by_signer(@lines) if ($interactive);
1367
Joe Perches0e70e832009-09-21 17:04:20 -07001368 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001369 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001370 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001371
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001372 my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
Joe Perches683c6f82010-10-26 14:22:55 -07001373 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001374
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001375 return ($commits, $signers_ref, $authors_ref, \@stats);
Joe Perchesa8af2432009-12-14 18:00:49 -08001376}
1377
Joe Perches63ab52d2010-10-26 14:22:51 -07001378sub vcs_find_author {
1379 my ($cmd) = @_;
1380 my @lines = ();
1381
1382 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1383
1384 if (!$email_git_penguin_chiefs) {
1385 @lines = grep(!/${penguin_chiefs}/i, @lines);
1386 }
1387
1388 return @lines if !@lines;
1389
Joe Perches683c6f82010-10-26 14:22:55 -07001390 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001391 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001392 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1393 my $author = $1;
1394 my ($name, $address) = parse_email($author);
1395 $author = format_email($name, $address, 1);
1396 push(@authors, $author);
1397 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001398 }
1399
Joe Perches683c6f82010-10-26 14:22:55 -07001400 save_commits_by_author(@lines) if ($interactive);
1401 save_commits_by_signer(@lines) if ($interactive);
1402
1403 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001404}
1405
Joe Perches60db31a2009-12-14 18:00:50 -08001406sub vcs_save_commits {
1407 my ($cmd) = @_;
1408 my @lines = ();
1409 my @commits = ();
1410
1411 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1412
1413 foreach my $line (@lines) {
1414 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1415 push(@commits, $1);
1416 }
1417 }
1418
1419 return @commits;
1420}
1421
1422sub vcs_blame {
1423 my ($file) = @_;
1424 my $cmd;
1425 my @commits = ();
1426
1427 return @commits if (!(-f $file));
1428
1429 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1430 my @all_commits = ();
1431
1432 $cmd = $VCS_cmds{"blame_file_cmd"};
1433 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1434 @all_commits = vcs_save_commits($cmd);
1435
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 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1443 push(@commits, $all_commits[$i]);
1444 }
1445 }
1446 } elsif (@range) {
1447 foreach my $file_range_diff (@range) {
1448 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1449 my $diff_file = $1;
1450 my $diff_start = $2;
1451 my $diff_length = $3;
1452 next if ("$file" ne "$diff_file");
1453 $cmd = $VCS_cmds{"blame_range_cmd"};
1454 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1455 push(@commits, vcs_save_commits($cmd));
1456 }
1457 } else {
1458 $cmd = $VCS_cmds{"blame_file_cmd"};
1459 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1460 @commits = vcs_save_commits($cmd);
1461 }
1462
Joe Perches63ab52d2010-10-26 14:22:51 -07001463 foreach my $commit (@commits) {
1464 $commit =~ s/^\^//g;
1465 }
1466
Joe Perches60db31a2009-12-14 18:00:50 -08001467 return @commits;
1468}
1469
1470my $printed_novcs = 0;
1471sub vcs_exists {
1472 %VCS_cmds = %VCS_cmds_git;
1473 return 1 if eval $VCS_cmds{"available"};
1474 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001475 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001476 %VCS_cmds = ();
1477 if (!$printed_novcs) {
1478 warn("$P: No supported VCS found. Add --nogit to options?\n");
1479 warn("Using a git repository produces better results.\n");
1480 warn("Try Linus Torvalds' latest git repository using:\n");
Ralf Thielow3d1c2f72011-08-25 15:59:07 -07001481 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n");
Joe Perches60db31a2009-12-14 18:00:50 -08001482 $printed_novcs = 1;
1483 }
1484 return 0;
1485}
1486
Joe Perches683c6f82010-10-26 14:22:55 -07001487sub vcs_is_git {
Joe Perchesb9e23312010-10-26 14:22:58 -07001488 vcs_exists();
Joe Perches683c6f82010-10-26 14:22:55 -07001489 return $vcs_used == 1;
1490}
1491
1492sub vcs_is_hg {
1493 return $vcs_used == 2;
1494}
1495
Joe Perches6ef1c522010-10-26 14:22:56 -07001496sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001497 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001498 my @list = @$list_ref;
1499
Joe Perches683c6f82010-10-26 14:22:55 -07001500 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001501
1502 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001503 my %authored;
1504 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001505 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001506 my $maintained = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001507 foreach my $entry (@list) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001508 $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
1509 $selected{$count} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001510 $authored{$count} = 0;
1511 $signed{$count} = 0;
1512 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001513 }
1514
1515 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001516 my $done = 0;
1517 my $print_options = 0;
1518 my $redraw = 1;
1519 while (!$done) {
1520 $count = 0;
1521 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001522 printf STDERR "\n%1s %2s %-65s",
1523 "*", "#", "email/list and role:stats";
1524 if ($email_git ||
1525 ($email_git_fallback && !$maintained) ||
1526 $email_git_blame) {
1527 print STDERR "auth sign";
1528 }
1529 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001530 foreach my $entry (@list) {
1531 my $email = $entry->[0];
1532 my $role = $entry->[1];
1533 my $sel = "";
1534 $sel = "*" if ($selected{$count});
1535 my $commit_author = $commit_author_hash{$email};
1536 my $commit_signer = $commit_signer_hash{$email};
1537 my $authored = 0;
1538 my $signed = 0;
1539 $authored++ for (@{$commit_author});
1540 $signed++ for (@{$commit_signer});
1541 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1542 printf STDERR "%4d %4d", $authored, $signed
1543 if ($authored > 0 || $signed > 0);
1544 printf STDERR "\n %s\n", $role;
1545 if ($authored{$count}) {
1546 my $commit_author = $commit_author_hash{$email};
1547 foreach my $ref (@{$commit_author}) {
1548 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001549 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001550 }
Joe Perches683c6f82010-10-26 14:22:55 -07001551 if ($signed{$count}) {
1552 my $commit_signer = $commit_signer_hash{$email};
1553 foreach my $ref (@{$commit_signer}) {
1554 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1555 }
1556 }
1557
1558 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001559 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001560 }
Joe Perches683c6f82010-10-26 14:22:55 -07001561 my $date_ref = \$email_git_since;
1562 $date_ref = \$email_hg_since if (vcs_is_hg());
1563 if ($print_options) {
1564 $print_options = 0;
1565 if (vcs_exists()) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001566 print STDERR <<EOT
1567
1568Version Control options:
1569g use git history [$email_git]
1570gf use git-fallback [$email_git_fallback]
1571b use git blame [$email_git_blame]
1572bs use blame signatures [$email_git_blame_signatures]
1573c# minimum commits [$email_git_min_signatures]
1574%# min percent [$email_git_min_percent]
1575d# history to use [$$date_ref]
1576x# max maintainers [$email_git_max_maintainers]
1577t all signature types [$email_git_all_signature_types]
1578m use .mailmap [$email_use_mailmap]
1579EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001580 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001581 print STDERR <<EOT
1582
1583Additional options:
15840 toggle all
1585tm toggle maintainers
1586tg toggle git entries
1587tl toggle open list entries
1588ts toggle subscriber list entries
1589f emails in file [$file_emails]
1590k keywords in file [$keywords]
1591r remove duplicates [$email_remove_duplicates]
1592p# pattern match depth [$pattern_depth]
1593EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001594 }
1595 print STDERR
1596"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1597
1598 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001599 chomp($input);
1600
Joe Perches683c6f82010-10-26 14:22:55 -07001601 $redraw = 1;
1602 my $rerun = 0;
1603 my @wish = split(/[, ]+/, $input);
1604 foreach my $nr (@wish) {
1605 $nr = lc($nr);
1606 my $sel = substr($nr, 0, 1);
1607 my $str = substr($nr, 1);
1608 my $val = 0;
1609 $val = $1 if $str =~ /^(\d+)$/;
1610
1611 if ($sel eq "y") {
1612 $interactive = 0;
1613 $done = 1;
1614 $output_rolestats = 0;
1615 $output_roles = 0;
1616 last;
1617 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1618 $selected{$nr - 1} = !$selected{$nr - 1};
1619 } elsif ($sel eq "*" || $sel eq '^') {
1620 my $toggle = 0;
1621 $toggle = 1 if ($sel eq '*');
1622 for (my $i = 0; $i < $count; $i++) {
1623 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001624 }
Joe Perches683c6f82010-10-26 14:22:55 -07001625 } elsif ($sel eq "0") {
1626 for (my $i = 0; $i < $count; $i++) {
1627 $selected{$i} = !$selected{$i};
1628 }
Joe Perchesb9e23312010-10-26 14:22:58 -07001629 } elsif ($sel eq "t") {
1630 if (lc($str) eq "m") {
1631 for (my $i = 0; $i < $count; $i++) {
1632 $selected{$i} = !$selected{$i}
1633 if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
1634 }
1635 } elsif (lc($str) eq "g") {
1636 for (my $i = 0; $i < $count; $i++) {
1637 $selected{$i} = !$selected{$i}
1638 if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
1639 }
1640 } elsif (lc($str) eq "l") {
1641 for (my $i = 0; $i < $count; $i++) {
1642 $selected{$i} = !$selected{$i}
1643 if ($list[$i]->[1] =~ /^(open list)/i);
1644 }
1645 } elsif (lc($str) eq "s") {
1646 for (my $i = 0; $i < $count; $i++) {
1647 $selected{$i} = !$selected{$i}
1648 if ($list[$i]->[1] =~ /^(subscriber list)/i);
1649 }
1650 }
Joe Perches683c6f82010-10-26 14:22:55 -07001651 } elsif ($sel eq "a") {
1652 if ($val > 0 && $val <= $count) {
1653 $authored{$val - 1} = !$authored{$val - 1};
1654 } elsif ($str eq '*' || $str eq '^') {
1655 my $toggle = 0;
1656 $toggle = 1 if ($str eq '*');
1657 for (my $i = 0; $i < $count; $i++) {
1658 $authored{$i} = $toggle;
1659 }
1660 }
1661 } elsif ($sel eq "s") {
1662 if ($val > 0 && $val <= $count) {
1663 $signed{$val - 1} = !$signed{$val - 1};
1664 } elsif ($str eq '*' || $str eq '^') {
1665 my $toggle = 0;
1666 $toggle = 1 if ($str eq '*');
1667 for (my $i = 0; $i < $count; $i++) {
1668 $signed{$i} = $toggle;
1669 }
1670 }
1671 } elsif ($sel eq "o") {
1672 $print_options = 1;
1673 $redraw = 1;
1674 } elsif ($sel eq "g") {
1675 if ($str eq "f") {
1676 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001677 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001678 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001679 }
Joe Perches683c6f82010-10-26 14:22:55 -07001680 $rerun = 1;
1681 } elsif ($sel eq "b") {
1682 if ($str eq "s") {
1683 bool_invert(\$email_git_blame_signatures);
1684 } else {
1685 bool_invert(\$email_git_blame);
1686 }
1687 $rerun = 1;
1688 } elsif ($sel eq "c") {
1689 if ($val > 0) {
1690 $email_git_min_signatures = $val;
1691 $rerun = 1;
1692 }
1693 } elsif ($sel eq "x") {
1694 if ($val > 0) {
1695 $email_git_max_maintainers = $val;
1696 $rerun = 1;
1697 }
1698 } elsif ($sel eq "%") {
1699 if ($str ne "" && $val >= 0) {
1700 $email_git_min_percent = $val;
1701 $rerun = 1;
1702 }
1703 } elsif ($sel eq "d") {
1704 if (vcs_is_git()) {
1705 $email_git_since = $str;
1706 } elsif (vcs_is_hg()) {
1707 $email_hg_since = $str;
1708 }
1709 $rerun = 1;
1710 } elsif ($sel eq "t") {
1711 bool_invert(\$email_git_all_signature_types);
1712 $rerun = 1;
1713 } elsif ($sel eq "f") {
1714 bool_invert(\$file_emails);
1715 $rerun = 1;
1716 } elsif ($sel eq "r") {
1717 bool_invert(\$email_remove_duplicates);
1718 $rerun = 1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001719 } elsif ($sel eq "m") {
1720 bool_invert(\$email_use_mailmap);
1721 read_mailmap();
1722 $rerun = 1;
Joe Perches683c6f82010-10-26 14:22:55 -07001723 } elsif ($sel eq "k") {
1724 bool_invert(\$keywords);
1725 $rerun = 1;
1726 } elsif ($sel eq "p") {
1727 if ($str ne "" && $val >= 0) {
1728 $pattern_depth = $val;
1729 $rerun = 1;
1730 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001731 } elsif ($sel eq "h" || $sel eq "?") {
1732 print STDERR <<EOT
1733
1734Interactive mode allows you to select the various maintainers, submitters,
1735commit signers and mailing lists that could be CC'd on a patch.
1736
1737Any *'d entry is selected.
1738
Joe Perches47abc722010-10-26 14:22:57 -07001739If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001740history of files in the patch. Also, each line of the current file can
1741be matched to its commit author and that commits signers with blame.
1742
1743Various knobs exist to control the length of time for active commit
1744tracking, the maximum number of commit authors and signers to add,
1745and such.
1746
1747Enter selections at the prompt until you are satisfied that the selected
1748maintainers are appropriate. You may enter multiple selections separated
1749by either commas or spaces.
1750
1751EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001752 } else {
1753 print STDERR "invalid option: '$nr'\n";
1754 $redraw = 0;
1755 }
1756 }
1757 if ($rerun) {
1758 print STDERR "git-blame can be very slow, please have patience..."
1759 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001760 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001761 }
1762 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001763
1764 #drop not selected entries
1765 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001766 my @new_emailto = ();
1767 foreach my $entry (@list) {
1768 if ($selected{$count}) {
1769 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001770 }
1771 $count++;
1772 }
Joe Perches683c6f82010-10-26 14:22:55 -07001773 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001774}
1775
Joe Perches683c6f82010-10-26 14:22:55 -07001776sub bool_invert {
1777 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001778
Joe Perches683c6f82010-10-26 14:22:55 -07001779 if ($$bool_ref) {
1780 $$bool_ref = 0;
1781 } else {
1782 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001783 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001784}
1785
Joe Perchesb9e23312010-10-26 14:22:58 -07001786sub deduplicate_email {
1787 my ($email) = @_;
1788
1789 my $matched = 0;
1790 my ($name, $address) = parse_email($email);
1791 $email = format_email($name, $address, 1);
1792 $email = mailmap_email($email);
1793
1794 return $email if (!$email_remove_duplicates);
1795
1796 ($name, $address) = parse_email($email);
1797
Joe Perchesfae99202010-10-26 14:22:58 -07001798 if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
Joe Perchesb9e23312010-10-26 14:22:58 -07001799 $name = $deduplicate_name_hash{lc($name)}->[0];
1800 $address = $deduplicate_name_hash{lc($name)}->[1];
1801 $matched = 1;
1802 } elsif ($deduplicate_address_hash{lc($address)}) {
1803 $name = $deduplicate_address_hash{lc($address)}->[0];
1804 $address = $deduplicate_address_hash{lc($address)}->[1];
1805 $matched = 1;
1806 }
1807 if (!$matched) {
1808 $deduplicate_name_hash{lc($name)} = [ $name, $address ];
1809 $deduplicate_address_hash{lc($address)} = [ $name, $address ];
1810 }
1811 $email = format_email($name, $address, 1);
1812 $email = mailmap_email($email);
1813 return $email;
1814}
1815
Joe Perches683c6f82010-10-26 14:22:55 -07001816sub save_commits_by_author {
1817 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001818
Joe Perches683c6f82010-10-26 14:22:55 -07001819 my @authors = ();
1820 my @commits = ();
1821 my @subjects = ();
1822
1823 foreach my $line (@lines) {
1824 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1825 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07001826 $author = deduplicate_email($author);
Joe Perches683c6f82010-10-26 14:22:55 -07001827 push(@authors, $author);
1828 }
1829 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1830 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1831 }
1832
1833 for (my $i = 0; $i < @authors; $i++) {
1834 my $exists = 0;
1835 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1836 if (@{$ref}[0] eq $commits[$i] &&
1837 @{$ref}[1] eq $subjects[$i]) {
1838 $exists = 1;
1839 last;
1840 }
1841 }
1842 if (!$exists) {
1843 push(@{$commit_author_hash{$authors[$i]}},
1844 [ ($commits[$i], $subjects[$i]) ]);
1845 }
1846 }
1847}
1848
1849sub save_commits_by_signer {
1850 my (@lines) = @_;
1851
1852 my $commit = "";
1853 my $subject = "";
1854
1855 foreach my $line (@lines) {
1856 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1857 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1858 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1859 my @signatures = ($line);
1860 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1861 my @types = @$types_ref;
1862 my @signers = @$signers_ref;
1863
1864 my $type = $types[0];
1865 my $signer = $signers[0];
1866
Joe Perchesb9e23312010-10-26 14:22:58 -07001867 $signer = deduplicate_email($signer);
Joe Perches6ef1c522010-10-26 14:22:56 -07001868
Joe Perches683c6f82010-10-26 14:22:55 -07001869 my $exists = 0;
1870 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1871 if (@{$ref}[0] eq $commit &&
1872 @{$ref}[1] eq $subject &&
1873 @{$ref}[2] eq $type) {
1874 $exists = 1;
1875 last;
1876 }
1877 }
1878 if (!$exists) {
1879 push(@{$commit_signer_hash{$signer}},
1880 [ ($commit, $subject, $type) ]);
1881 }
1882 }
1883 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001884}
1885
Joe Perches60db31a2009-12-14 18:00:50 -08001886sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001887 my ($role, $divisor, @lines) = @_;
1888
1889 my %hash;
1890 my $count = 0;
1891
Joe Perchesa8af2432009-12-14 18:00:49 -08001892 return if (@lines <= 0);
1893
1894 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001895 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001896 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001897 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001898
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001899 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001900
Joe Perches63ab52d2010-10-26 14:22:51 -07001901 return if (@lines <= 0);
1902
Joe Perches0e70e832009-09-21 17:04:20 -07001903 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001904
Joe Perches11ecf532009-09-21 17:04:22 -07001905 # uniq -c
1906 $hash{$_}++ for @lines;
1907
1908 # sort -rn
1909 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1910 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001911 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001912
Joe Perchesa8af2432009-12-14 18:00:49 -08001913 $percent = 100 if ($percent > 100);
Joe Perches435de072015-06-25 15:01:50 -07001914 next if (ignore_email_address($line));
Joe Perches11ecf532009-09-21 17:04:22 -07001915 $count++;
1916 last if ($sign_offs < $email_git_min_signatures ||
1917 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001918 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001919 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001920 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001921 my $fmt_percent = sprintf("%.0f", $percent);
1922 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1923 } else {
1924 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001925 }
Joe Perchesf5492662009-09-21 17:04:13 -07001926 }
1927}
1928
Joe Perches60db31a2009-12-14 18:00:50 -08001929sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001930 my ($file) = @_;
1931
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001932 my $authors_ref;
1933 my $signers_ref;
1934 my $stats_ref;
1935 my @authors = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001936 my @signers = ();
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001937 my @stats = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001938 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001939
Joe Perches683c6f82010-10-26 14:22:55 -07001940 $vcs_used = vcs_exists();
1941 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001942
Joe Perches60db31a2009-12-14 18:00:50 -08001943 my $cmd = $VCS_cmds{"find_signers_cmd"};
1944 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1945
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001946 ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
1947
1948 @signers = @{$signers_ref} if defined $signers_ref;
1949 @authors = @{$authors_ref} if defined $authors_ref;
1950 @stats = @{$stats_ref} if defined $stats_ref;
1951
1952# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
Joe Perchesb9e23312010-10-26 14:22:58 -07001953
1954 foreach my $signer (@signers) {
1955 $signer = deduplicate_email($signer);
1956 }
1957
Joe Perches60db31a2009-12-14 18:00:50 -08001958 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesc9ecefe2014-01-23 15:54:20 -08001959 vcs_assign("authored", $commits, @authors);
1960 if ($#authors == $#stats) {
1961 my $stat_pattern = $VCS_cmds{"stat_pattern"};
1962 $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
1963
1964 my $added = 0;
1965 my $deleted = 0;
1966 for (my $i = 0; $i <= $#stats; $i++) {
1967 if ($stats[$i] =~ /$stat_pattern/) {
1968 $added += $1;
1969 $deleted += $2;
1970 }
1971 }
1972 my @tmp_authors = uniq(@authors);
1973 foreach my $author (@tmp_authors) {
1974 $author = deduplicate_email($author);
1975 }
1976 @tmp_authors = uniq(@tmp_authors);
1977 my @list_added = ();
1978 my @list_deleted = ();
1979 foreach my $author (@tmp_authors) {
1980 my $auth_added = 0;
1981 my $auth_deleted = 0;
1982 for (my $i = 0; $i <= $#stats; $i++) {
1983 if ($author eq deduplicate_email($authors[$i]) &&
1984 $stats[$i] =~ /$stat_pattern/) {
1985 $auth_added += $1;
1986 $auth_deleted += $2;
1987 }
1988 }
1989 for (my $i = 0; $i < $auth_added; $i++) {
1990 push(@list_added, $author);
1991 }
1992 for (my $i = 0; $i < $auth_deleted; $i++) {
1993 push(@list_deleted, $author);
1994 }
1995 }
1996 vcs_assign("added_lines", $added, @list_added);
1997 vcs_assign("removed_lines", $deleted, @list_deleted);
1998 }
Joe Perchesa8af2432009-12-14 18:00:49 -08001999}
2000
Joe Perches60db31a2009-12-14 18:00:50 -08002001sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07002002 my ($file) = @_;
2003
Joe Perches60db31a2009-12-14 18:00:50 -08002004 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07002005 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002006 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08002007 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002008 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07002009
Joe Perches683c6f82010-10-26 14:22:55 -07002010 $vcs_used = vcs_exists();
2011 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07002012
Joe Perches63ab52d2010-10-26 14:22:51 -07002013 @all_commits = vcs_blame($file);
2014 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08002015 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07002016 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08002017
Joe Perches683c6f82010-10-26 14:22:55 -07002018 if ($email_git_blame_signatures) {
2019 if (vcs_is_hg()) {
2020 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002021 my $commit_authors_ref;
2022 my $commit_signers_ref;
2023 my $stats_ref;
2024 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002025 my @commit_signers = ();
2026 my $commit = join(" -r ", @commits);
2027 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07002028
Joe Perches683c6f82010-10-26 14:22:55 -07002029 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2030 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08002031
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002032 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2033 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2034 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches63ab52d2010-10-26 14:22:51 -07002035
Joe Perches683c6f82010-10-26 14:22:55 -07002036 push(@signers, @commit_signers);
2037 } else {
2038 foreach my $commit (@commits) {
2039 my $commit_count;
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002040 my $commit_authors_ref;
2041 my $commit_signers_ref;
2042 my $stats_ref;
2043 my @commit_authors = ();
Joe Perches683c6f82010-10-26 14:22:55 -07002044 my @commit_signers = ();
2045 my $cmd;
2046
2047 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
2048 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2049
Joe Perchesc9ecefe2014-01-23 15:54:20 -08002050 ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
2051 @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
2052 @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
Joe Perches683c6f82010-10-26 14:22:55 -07002053
2054 push(@signers, @commit_signers);
2055 }
2056 }
Joe Perchesf5492662009-09-21 17:04:13 -07002057 }
2058
Joe Perchesa8af2432009-12-14 18:00:49 -08002059 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07002060 if ($output_rolestats) {
2061 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07002062 if (vcs_is_hg()) {{ # Double brace for last exit
2063 my $commit_count;
2064 my @commit_signers = ();
2065 @commits = uniq(@commits);
2066 @commits = sort(@commits);
2067 my $commit = join(" -r ", @commits);
2068 my $cmd;
2069
2070 $cmd = $VCS_cmds{"find_commit_author_cmd"};
2071 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
2072
2073 my @lines = ();
2074
2075 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
2076
2077 if (!$email_git_penguin_chiefs) {
2078 @lines = grep(!/${penguin_chiefs}/i, @lines);
2079 }
2080
2081 last if !@lines;
2082
2083 my @authors = ();
2084 foreach my $line (@lines) {
2085 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
2086 my $author = $1;
Joe Perchesb9e23312010-10-26 14:22:58 -07002087 $author = deduplicate_email($author);
2088 push(@authors, $author);
Joe Perches683c6f82010-10-26 14:22:55 -07002089 }
2090 }
2091
2092 save_commits_by_author(@lines) if ($interactive);
2093 save_commits_by_signer(@lines) if ($interactive);
2094
2095 push(@signers, @authors);
2096 }}
2097 else {
2098 foreach my $commit (@commits) {
2099 my $i;
2100 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
2101 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
2102 my @author = vcs_find_author($cmd);
2103 next if !@author;
Joe Perchesb9e23312010-10-26 14:22:58 -07002104
2105 my $formatted_author = deduplicate_email($author[0]);
2106
Joe Perches683c6f82010-10-26 14:22:55 -07002107 my $count = grep(/$commit/, @all_commits);
2108 for ($i = 0; $i < $count ; $i++) {
Joe Perchesb9e23312010-10-26 14:22:58 -07002109 push(@blame_signers, $formatted_author);
Joe Perches683c6f82010-10-26 14:22:55 -07002110 }
Joe Perches63ab52d2010-10-26 14:22:51 -07002111 }
2112 }
2113 if (@blame_signers) {
2114 vcs_assign("authored lines", $total_lines, @blame_signers);
2115 }
2116 }
Joe Perchesb9e23312010-10-26 14:22:58 -07002117 foreach my $signer (@signers) {
2118 $signer = deduplicate_email($signer);
2119 }
Joe Perches60db31a2009-12-14 18:00:50 -08002120 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08002121 } else {
Joe Perchesb9e23312010-10-26 14:22:58 -07002122 foreach my $signer (@signers) {
2123 $signer = deduplicate_email($signer);
2124 }
Joe Perches60db31a2009-12-14 18:00:50 -08002125 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07002126 }
Joe Perchescb7301c2009-04-07 20:40:12 -07002127}
2128
Joe Perches4cad35a2016-08-02 14:04:10 -07002129sub vcs_file_exists {
2130 my ($file) = @_;
2131
2132 my $exists;
2133
2134 my $vcs_used = vcs_exists();
2135 return 0 if (!$vcs_used);
2136
2137 my $cmd = $VCS_cmds{"file_exists_cmd"};
2138 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
2139
2140 $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
2141
2142 return $exists;
2143}
2144
Joe Perchescb7301c2009-04-07 20:40:12 -07002145sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002146 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002147
2148 my %saw;
2149 @parms = grep(!$saw{$_}++, @parms);
2150 return @parms;
2151}
2152
2153sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08002154 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002155
2156 my %saw;
2157 @parms = sort @parms;
2158 @parms = grep(!$saw{$_}++, @parms);
2159 return @parms;
2160}
2161
Joe Perches03372db2010-03-05 13:43:00 -08002162sub clean_file_emails {
2163 my (@file_emails) = @_;
2164 my @fmt_emails = ();
2165
2166 foreach my $email (@file_emails) {
2167 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
2168 my ($name, $address) = parse_email($email);
2169 if ($name eq '"[,\.]"') {
2170 $name = "";
2171 }
2172
2173 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
2174 if (@nw > 2) {
2175 my $first = $nw[@nw - 3];
2176 my $middle = $nw[@nw - 2];
2177 my $last = $nw[@nw - 1];
2178
2179 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
2180 (length($first) == 2 && substr($first, -1) eq ".")) ||
2181 (length($middle) == 1 ||
2182 (length($middle) == 2 && substr($middle, -1) eq "."))) {
2183 $name = "$first $middle $last";
2184 } else {
2185 $name = "$middle $last";
2186 }
2187 }
2188
2189 if (substr($name, -1) =~ /[,\.]/) {
2190 $name = substr($name, 0, length($name) - 1);
2191 } elsif (substr($name, -2) =~ /[,\.]"/) {
2192 $name = substr($name, 0, length($name) - 2) . '"';
2193 }
2194
2195 if (substr($name, 0, 1) =~ /[,\.]/) {
2196 $name = substr($name, 1, length($name) - 1);
2197 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
2198 $name = '"' . substr($name, 2, length($name) - 2);
2199 }
2200
2201 my $fmt_email = format_email($name, $address, $email_usename);
2202 push(@fmt_emails, $fmt_email);
2203 }
2204 return @fmt_emails;
2205}
2206
Joe Perches3c7385b2009-12-14 18:00:46 -08002207sub merge_email {
2208 my @lines;
2209 my %saw;
2210
2211 for (@_) {
2212 my ($address, $role) = @$_;
2213 if (!$saw{$address}) {
2214 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08002215 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08002216 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08002217 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08002218 }
2219 $saw{$address} = 1;
2220 }
2221 }
2222
2223 return @lines;
2224}
2225
Joe Perchescb7301c2009-04-07 20:40:12 -07002226sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08002227 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07002228
2229 if ($output_multiline) {
2230 foreach my $line (@parms) {
2231 print("${line}\n");
2232 }
2233 } else {
2234 print(join($output_separator, @parms));
2235 print("\n");
2236 }
2237}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002238
2239my $rfc822re;
2240
2241sub make_rfc822re {
2242# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
2243# comment. We must allow for rfc822_lwsp (or comments) after each of these.
2244# This regexp will only work on addresses which have had comments stripped
2245# and replaced with rfc822_lwsp.
2246
2247 my $specials = '()<>@,;:\\\\".\\[\\]';
2248 my $controls = '\\000-\\037\\177';
2249
2250 my $dtext = "[^\\[\\]\\r\\\\]";
2251 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
2252
2253 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
2254
2255# Use zero-width assertion to spot the limit of an atom. A simple
2256# $rfc822_lwsp* causes the regexp engine to hang occasionally.
2257 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
2258 my $word = "(?:$atom|$quoted_string)";
2259 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
2260
2261 my $sub_domain = "(?:$atom|$domain_literal)";
2262 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
2263
2264 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
2265
2266 my $phrase = "$word*";
2267 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
2268 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
2269 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
2270
2271 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
2272 my $address = "(?:$mailbox|$group)";
2273
2274 return "$rfc822_lwsp*$address";
2275}
2276
2277sub rfc822_strip_comments {
2278 my $s = shift;
2279# Recursively remove comments, and replace with a single space. The simpler
2280# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2281# chars in atoms, for example.
2282
2283 while ($s =~ s/^((?:[^"\\]|\\.)*
2284 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2285 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2286 return $s;
2287}
2288
2289# valid: returns true if the parameter is an RFC822 valid address
2290#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002291sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002292 my $s = rfc822_strip_comments(shift);
2293
2294 if (!$rfc822re) {
2295 $rfc822re = make_rfc822re();
2296 }
2297
2298 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2299}
2300
2301# validlist: In scalar context, returns true if the parameter is an RFC822
2302# valid list of addresses.
2303#
2304# In list context, returns an empty list on failure (an invalid
2305# address was found); otherwise a list whose first element is the
2306# number of addresses found and whose remaining elements are the
2307# addresses. This is needed to disambiguate failure (invalid)
2308# from success with no addresses found, because an empty string is
2309# a valid list.
2310
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002311sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002312 my $s = rfc822_strip_comments(shift);
2313
2314 if (!$rfc822re) {
2315 $rfc822re = make_rfc822re();
2316 }
2317 # * null list items are valid according to the RFC
2318 # * the '1' business is to aid in distinguishing failure from no results
2319
2320 my @r;
2321 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2322 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002323 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002324 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002325 }
2326 return wantarray ? (scalar(@r), @r) : 1;
2327 }
Joe Perches60db31a2009-12-14 18:00:50 -08002328 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002329}