blob: 61d3bb51bddf571d0c7a1dd3edb4bfe36b25860b [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 Perches6ef1c522010-10-26 14:22:56 -070016my $V = '0.26-beta4';
Joe Perchescb7301c2009-04-07 20:40:12 -070017
18use Getopt::Long qw(:config no_auto_abbrev);
19
20my $lk_path = "./";
21my $email = 1;
22my $email_usename = 1;
23my $email_maintainer = 1;
24my $email_list = 1;
25my $email_subscriber_list = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070026my $email_git_penguin_chiefs = 0;
Joe Perchese3e9d112010-10-26 14:22:53 -070027my $email_git = 0;
Florian Mickler0fa05592010-05-24 14:33:20 -070028my $email_git_all_signature_types = 0;
Joe Perches60db31a2009-12-14 18:00:50 -080029my $email_git_blame = 0;
Joe Perches683c6f82010-10-26 14:22:55 -070030my $email_git_blame_signatures = 1;
Joe Perchese3e9d112010-10-26 14:22:53 -070031my $email_git_fallback = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070032my $email_git_min_signatures = 1;
33my $email_git_max_maintainers = 5;
Joe Perchesafa81ee2009-07-29 15:04:28 -070034my $email_git_min_percent = 5;
Joe Perchescb7301c2009-04-07 20:40:12 -070035my $email_git_since = "1-year-ago";
Joe Perches60db31a2009-12-14 18:00:50 -080036my $email_hg_since = "-365";
Florian Micklerdace8e32010-10-26 14:22:54 -070037my $interactive = 0;
Joe Perches11ecf532009-09-21 17:04:22 -070038my $email_remove_duplicates = 1;
Joe Perchescb7301c2009-04-07 20:40:12 -070039my $output_multiline = 1;
40my $output_separator = ", ";
Joe Perches3c7385b2009-12-14 18:00:46 -080041my $output_roles = 0;
42my $output_rolestats = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070043my $scm = 0;
44my $web = 0;
45my $subsystem = 0;
46my $status = 0;
Joe Perchesdcf36a92009-10-26 16:49:47 -070047my $keywords = 1;
Joe Perches4b76c9d2010-03-05 13:43:03 -080048my $sections = 0;
Joe Perches03372db2010-03-05 13:43:00 -080049my $file_emails = 0;
Joe Perches4a7fdb52009-04-10 12:28:57 -070050my $from_filename = 0;
Joe Perches3fb55652009-09-21 17:04:17 -070051my $pattern_depth = 0;
Joe Perchescb7301c2009-04-07 20:40:12 -070052my $version = 0;
53my $help = 0;
54
Joe Perches683c6f82010-10-26 14:22:55 -070055my $vcs_used = 0;
56
Joe Perchescb7301c2009-04-07 20:40:12 -070057my $exit = 0;
58
Joe Perches683c6f82010-10-26 14:22:55 -070059my %commit_author_hash;
60my %commit_signer_hash;
Florian Micklerdace8e32010-10-26 14:22:54 -070061
Joe Perchescb7301c2009-04-07 20:40:12 -070062my @penguin_chief = ();
Joe Perchese4d26b02010-05-24 14:33:17 -070063push(@penguin_chief, "Linus Torvalds:torvalds\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070064#Andrew wants in on most everything - 2009/01/14
Joe Perchese4d26b02010-05-24 14:33:17 -070065#push(@penguin_chief, "Andrew Morton:akpm\@linux-foundation.org");
Joe Perchescb7301c2009-04-07 20:40:12 -070066
67my @penguin_chief_names = ();
68foreach my $chief (@penguin_chief) {
69 if ($chief =~ m/^(.*):(.*)/) {
70 my $chief_name = $1;
71 my $chief_addr = $2;
72 push(@penguin_chief_names, $chief_name);
73 }
74}
Joe Perchese4d26b02010-05-24 14:33:17 -070075my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)";
76
77# Signature types of people who are either
78# a) responsible for the code in question, or
79# b) familiar enough with it to give relevant feedback
80my @signature_tags = ();
81push(@signature_tags, "Signed-off-by:");
82push(@signature_tags, "Reviewed-by:");
83push(@signature_tags, "Acked-by:");
Joe Perchescb7301c2009-04-07 20:40:12 -070084
Joe Perches5f2441e2009-06-16 15:34:02 -070085# rfc822 email address - preloaded methods go here.
Joe Perches1b5e1cf2009-06-16 15:34:01 -070086my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
Joe Perchesdf4cc032009-06-16 15:34:04 -070087my $rfc822_char = '[\\000-\\377]';
Joe Perches1b5e1cf2009-06-16 15:34:01 -070088
Joe Perches60db31a2009-12-14 18:00:50 -080089# VCS command support: class-like functions and strings
90
91my %VCS_cmds;
92
93my %VCS_cmds_git = (
94 "execute_cmd" => \&git_execute_cmd,
95 "available" => '(which("git") ne "") && (-d ".git")',
Joe Perches683c6f82010-10-26 14:22:55 -070096 "find_signers_cmd" =>
97 "git log --no-color --since=\$email_git_since " .
98 '--format="GitCommit: %H%n' .
99 'GitAuthor: %an <%ae>%n' .
100 'GitDate: %aD%n' .
101 'GitSubject: %s%n' .
102 '%b%n"' .
103 " -- \$file",
104 "find_commit_signers_cmd" =>
105 "git log --no-color " .
106 '--format="GitCommit: %H%n' .
107 'GitAuthor: %an <%ae>%n' .
108 'GitDate: %aD%n' .
109 'GitSubject: %s%n' .
110 '%b%n"' .
111 " -1 \$commit",
112 "find_commit_author_cmd" =>
113 "git log --no-color " .
114 '--format="GitCommit: %H%n' .
115 'GitAuthor: %an <%ae>%n' .
116 'GitDate: %aD%n' .
117 'GitSubject: %s%n"' .
118 " -1 \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800119 "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
120 "blame_file_cmd" => "git blame -l \$file",
Joe Perches683c6f82010-10-26 14:22:55 -0700121 "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
Florian Micklerdace8e32010-10-26 14:22:54 -0700122 "blame_commit_pattern" => "^([0-9a-f]+) ",
Joe Perches683c6f82010-10-26 14:22:55 -0700123 "author_pattern" => "^GitAuthor: (.*)",
124 "subject_pattern" => "^GitSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800125);
126
127my %VCS_cmds_hg = (
128 "execute_cmd" => \&hg_execute_cmd,
129 "available" => '(which("hg") ne "") && (-d ".hg")',
130 "find_signers_cmd" =>
Joe Perches683c6f82010-10-26 14:22:55 -0700131 "hg log --date=\$email_hg_since " .
132 "--template='HgCommit: {node}\\n" .
133 "HgAuthor: {author}\\n" .
134 "HgSubject: {desc}\\n'" .
135 " -- \$file",
136 "find_commit_signers_cmd" =>
137 "hg log " .
138 "--template='HgSubject: {desc}\\n'" .
139 " -r \$commit",
140 "find_commit_author_cmd" =>
141 "hg log " .
142 "--template='HgCommit: {node}\\n" .
143 "HgAuthor: {author}\\n" .
144 "HgSubject: {desc|firstline}\\n'" .
145 " -r \$commit",
Joe Perches60db31a2009-12-14 18:00:50 -0800146 "blame_range_cmd" => "", # not supported
Joe Perches683c6f82010-10-26 14:22:55 -0700147 "blame_file_cmd" => "hg blame -n \$file",
148 "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
149 "blame_commit_pattern" => "^([ 0-9a-f]+):",
150 "author_pattern" => "^HgAuthor: (.*)",
151 "subject_pattern" => "^HgSubject: (.*)",
Joe Perches60db31a2009-12-14 18:00:50 -0800152);
153
Joe Perchesbcde44e2010-10-26 14:22:53 -0700154my $conf = which_conf(".get_maintainer.conf");
155if (-f $conf) {
Joe Perches368669d2010-05-24 14:33:19 -0700156 my @conf_args;
Joe Perchesbcde44e2010-10-26 14:22:53 -0700157 open(my $conffile, '<', "$conf")
158 or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
159
Joe Perches368669d2010-05-24 14:33:19 -0700160 while (<$conffile>) {
161 my $line = $_;
162
163 $line =~ s/\s*\n?$//g;
164 $line =~ s/^\s*//g;
165 $line =~ s/\s+/ /g;
166
167 next if ($line =~ m/^\s*#/);
168 next if ($line =~ m/^\s*$/);
169
170 my @words = split(" ", $line);
171 foreach my $word (@words) {
172 last if ($word =~ m/^#/);
173 push (@conf_args, $word);
174 }
175 }
176 close($conffile);
177 unshift(@ARGV, @conf_args) if @conf_args;
178}
179
Joe Perchescb7301c2009-04-07 20:40:12 -0700180if (!GetOptions(
181 'email!' => \$email,
182 'git!' => \$email_git,
Joe Perchese4d26b02010-05-24 14:33:17 -0700183 'git-all-signature-types!' => \$email_git_all_signature_types,
Joe Perches60db31a2009-12-14 18:00:50 -0800184 'git-blame!' => \$email_git_blame,
Joe Perches683c6f82010-10-26 14:22:55 -0700185 'git-blame-signatures!' => \$email_git_blame_signatures,
Joe Perchese3e9d112010-10-26 14:22:53 -0700186 'git-fallback!' => \$email_git_fallback,
Joe Perchescb7301c2009-04-07 20:40:12 -0700187 'git-chief-penguins!' => \$email_git_penguin_chiefs,
188 'git-min-signatures=i' => \$email_git_min_signatures,
189 'git-max-maintainers=i' => \$email_git_max_maintainers,
Joe Perchesafa81ee2009-07-29 15:04:28 -0700190 'git-min-percent=i' => \$email_git_min_percent,
Joe Perchescb7301c2009-04-07 20:40:12 -0700191 'git-since=s' => \$email_git_since,
Joe Perches60db31a2009-12-14 18:00:50 -0800192 'hg-since=s' => \$email_hg_since,
Florian Micklerdace8e32010-10-26 14:22:54 -0700193 'i|interactive!' => \$interactive,
Joe Perches11ecf532009-09-21 17:04:22 -0700194 'remove-duplicates!' => \$email_remove_duplicates,
Joe Perchescb7301c2009-04-07 20:40:12 -0700195 'm!' => \$email_maintainer,
196 'n!' => \$email_usename,
197 'l!' => \$email_list,
198 's!' => \$email_subscriber_list,
199 'multiline!' => \$output_multiline,
Joe Perches3c7385b2009-12-14 18:00:46 -0800200 'roles!' => \$output_roles,
201 'rolestats!' => \$output_rolestats,
Joe Perchescb7301c2009-04-07 20:40:12 -0700202 'separator=s' => \$output_separator,
203 'subsystem!' => \$subsystem,
204 'status!' => \$status,
205 'scm!' => \$scm,
206 'web!' => \$web,
Joe Perches3fb55652009-09-21 17:04:17 -0700207 'pattern-depth=i' => \$pattern_depth,
Joe Perchesdcf36a92009-10-26 16:49:47 -0700208 'k|keywords!' => \$keywords,
Joe Perches4b76c9d2010-03-05 13:43:03 -0800209 'sections!' => \$sections,
Joe Perches03372db2010-03-05 13:43:00 -0800210 'fe|file-emails!' => \$file_emails,
Joe Perches4a7fdb52009-04-10 12:28:57 -0700211 'f|file' => \$from_filename,
Joe Perchescb7301c2009-04-07 20:40:12 -0700212 'v|version' => \$version,
Joe Perches64f77f32010-03-05 13:43:04 -0800213 'h|help|usage' => \$help,
Joe Perchescb7301c2009-04-07 20:40:12 -0700214 )) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800215 die "$P: invalid argument - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700216}
217
218if ($help != 0) {
219 usage();
220 exit 0;
221}
222
223if ($version != 0) {
224 print("${P} ${V}\n");
225 exit 0;
226}
227
Joe Perches64f77f32010-03-05 13:43:04 -0800228if (-t STDIN && !@ARGV) {
229 # We're talking to a terminal, but have no command line arguments.
230 die "$P: missing patchfile or -f file - use --help if necessary\n";
Joe Perchescb7301c2009-04-07 20:40:12 -0700231}
232
Joe Perches683c6f82010-10-26 14:22:55 -0700233$output_multiline = 0 if ($output_separator ne ", ");
234$output_rolestats = 1 if ($interactive);
235$output_roles = 1 if ($output_rolestats);
Joe Perches3c7385b2009-12-14 18:00:46 -0800236
Joe Perches4b76c9d2010-03-05 13:43:03 -0800237if ($sections) {
238 $email = 0;
239 $email_list = 0;
240 $scm = 0;
241 $status = 0;
242 $subsystem = 0;
243 $web = 0;
244 $keywords = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -0700245 $interactive = 0;
Joe Perches4b76c9d2010-03-05 13:43:03 -0800246} else {
247 my $selections = $email + $scm + $status + $subsystem + $web;
248 if ($selections == 0) {
Joe Perches4b76c9d2010-03-05 13:43:03 -0800249 die "$P: Missing required option: email, scm, status, subsystem or web\n";
250 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700251}
252
Joe Perchesf5492662009-09-21 17:04:13 -0700253if ($email &&
254 ($email_maintainer + $email_list + $email_subscriber_list +
255 $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700256 die "$P: Please select at least 1 email option\n";
257}
258
259if (!top_of_kernel_tree($lk_path)) {
260 die "$P: The current directory does not appear to be "
261 . "a linux kernel source tree.\n";
262}
263
264## Read MAINTAINERS for type/value pairs
265
266my @typevalue = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700267my %keyword_hash;
268
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800269open (my $maint, '<', "${lk_path}MAINTAINERS")
270 or die "$P: Can't open MAINTAINERS: $!\n";
271while (<$maint>) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700272 my $line = $_;
273
274 if ($line =~ m/^(\C):\s*(.*)/) {
275 my $type = $1;
276 my $value = $2;
277
278 ##Filename pattern matching
279 if ($type eq "F" || $type eq "X") {
280 $value =~ s@\.@\\\.@g; ##Convert . to \.
281 $value =~ s/\*/\.\*/g; ##Convert * to .*
282 $value =~ s/\?/\./g; ##Convert ? to .
Joe Perches870020f2009-07-29 15:04:28 -0700283 ##if pattern is a directory and it lacks a trailing slash, add one
284 if ((-d $value)) {
285 $value =~ s@([^/])$@$1/@;
286 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700287 } elsif ($type eq "K") {
288 $keyword_hash{@typevalue} = $value;
Joe Perchescb7301c2009-04-07 20:40:12 -0700289 }
290 push(@typevalue, "$type:$value");
291 } elsif (!/^(\s)*$/) {
292 $line =~ s/\n$//g;
293 push(@typevalue, $line);
294 }
295}
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800296close($maint);
Joe Perchescb7301c2009-04-07 20:40:12 -0700297
Joe Perches8cbb3a72009-09-21 17:04:21 -0700298my %mailmap;
299
Joe Perches11ecf532009-09-21 17:04:22 -0700300if ($email_remove_duplicates) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800301 open(my $mailmap, '<', "${lk_path}.mailmap")
302 or warn "$P: Can't open .mailmap: $!\n";
303 while (<$mailmap>) {
Joe Perches11ecf532009-09-21 17:04:22 -0700304 my $line = $_;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700305
Joe Perches11ecf532009-09-21 17:04:22 -0700306 next if ($line =~ m/^\s*#/);
307 next if ($line =~ m/^\s*$/);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700308
Joe Perches11ecf532009-09-21 17:04:22 -0700309 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -0800310 $line = format_email($name, $address, $email_usename);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700311
Joe Perches11ecf532009-09-21 17:04:22 -0700312 next if ($line =~ m/^\s*$/);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700313
Joe Perches11ecf532009-09-21 17:04:22 -0700314 if (exists($mailmap{$name})) {
315 my $obj = $mailmap{$name};
316 push(@$obj, $address);
317 } else {
318 my @arr = ($address);
319 $mailmap{$name} = \@arr;
320 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700321 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800322 close($mailmap);
Joe Perches8cbb3a72009-09-21 17:04:21 -0700323}
324
Joe Perches4a7fdb52009-04-10 12:28:57 -0700325## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700326
327my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700328my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700329my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800330my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700331
Joe Perches64f77f32010-03-05 13:43:04 -0800332if (!@ARGV) {
333 push(@ARGV, "&STDIN");
334}
335
Joe Perches4a7fdb52009-04-10 12:28:57 -0700336foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800337 if ($file ne "&STDIN") {
338 ##if $file is a directory and it lacks a trailing slash, add one
339 if ((-d $file)) {
340 $file =~ s@([^/])$@$1/@;
341 } elsif (!(-f $file)) {
342 die "$P: file '${file}' not found\n";
343 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700344 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700345 if ($from_filename) {
346 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700347 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800348 open(my $f, '<', $file)
349 or die "$P: Can't open $file: $!\n";
350 my $text = do { local($/) ; <$f> };
351 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800352 if ($keywords) {
353 foreach my $line (keys %keyword_hash) {
354 if ($text =~ m/$keyword_hash{$line}/x) {
355 push(@keyword_tvi, $line);
356 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700357 }
358 }
Joe Perches03372db2010-03-05 13:43:00 -0800359 if ($file_emails) {
360 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;
361 push(@file_emails, clean_file_emails(@poss_addr));
362 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700363 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700364 } else {
365 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700366 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800367
Wolfram Sang3a4df132010-03-23 13:35:18 -0700368 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800369 or die "$P: Can't open $file: $!\n";
370 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700371 my $patch_line = $_;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700372 if (m/^\+\+\+\s+(\S+)/) {
373 my $filename = $1;
374 $filename =~ s@^[^/]*/@@;
375 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700376 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700377 push(@files, $filename);
Joe Perchesf5492662009-09-21 17:04:13 -0700378 } elsif (m/^\@\@ -(\d+),(\d+)/) {
379 if ($email_git_blame) {
380 push(@range, "$lastfile:$1:$2");
381 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700382 } elsif ($keywords) {
383 foreach my $line (keys %keyword_hash) {
384 if ($patch_line =~ m/^[+-].*$keyword_hash{$line}/x) {
385 push(@keyword_tvi, $line);
386 }
387 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700388 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700389 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800390 close($patch);
391
Joe Perches4a7fdb52009-04-10 12:28:57 -0700392 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700393 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700394 . "Add -f to options?\n";
395 }
396 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700397 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700398}
399
Joe Perches03372db2010-03-05 13:43:00 -0800400@file_emails = uniq(@file_emails);
401
Joe Perches683c6f82010-10-26 14:22:55 -0700402my %email_hash_name;
403my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700404my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700405my %hash_list_to;
Joe Perches290603c12009-06-16 15:33:58 -0700406my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700407my @scm = ();
408my @web = ();
409my @subsystem = ();
410my @status = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700411my @interactive_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700412my $signature_pattern;
Joe Perchescb7301c2009-04-07 20:40:12 -0700413
Joe Perches6ef1c522010-10-26 14:22:56 -0700414my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700415
Joe Perches6ef1c522010-10-26 14:22:56 -0700416if (@maintainers) {
417 @maintainers = merge_email(@maintainers);
418 output(@maintainers);
419}
Joe Perchescb7301c2009-04-07 20:40:12 -0700420
421if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700422 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700423 output(@scm);
424}
Joe Perches683c6f82010-10-26 14:22:55 -0700425
Joe Perchescb7301c2009-04-07 20:40:12 -0700426if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700427 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700428 output(@status);
429}
430
431if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700432 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700433 output(@subsystem);
434}
435
436if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700437 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700438 output(@web);
439}
440
441exit($exit);
442
Joe Perches6ef1c522010-10-26 14:22:56 -0700443sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700444 %email_hash_name = ();
445 %email_hash_address = ();
446 %commit_author_hash = ();
447 %commit_signer_hash = ();
448 @email_to = ();
449 %hash_list_to = ();
450 @list_to = ();
451 @scm = ();
452 @web = ();
453 @subsystem = ();
454 @status = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700455 @interactive_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700456 if ($email_git_all_signature_types) {
457 $signature_pattern = "(.+?)[Bb][Yy]:";
458 } else {
459 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
460 }
461
462 # Find responsible parties
463
Joe Perches6ef1c522010-10-26 14:22:56 -0700464 my %exact_pattern_match_hash;
465
Joe Perches683c6f82010-10-26 14:22:55 -0700466 foreach my $file (@files) {
467
468 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700469 my $tvi = find_first_section();
470 while ($tvi < @typevalue) {
471 my $start = find_starting_index($tvi);
472 my $end = find_ending_index($tvi);
473 my $exclude = 0;
474 my $i;
475
476 #Do not match excluded file patterns
477
478 for ($i = $start; $i < $end; $i++) {
479 my $line = $typevalue[$i];
480 if ($line =~ m/^(\C):\s*(.*)/) {
481 my $type = $1;
482 my $value = $2;
483 if ($type eq 'X') {
484 if (file_match_pattern($file, $value)) {
485 $exclude = 1;
486 last;
487 }
488 }
489 }
490 }
491
492 if (!$exclude) {
493 for ($i = $start; $i < $end; $i++) {
494 my $line = $typevalue[$i];
495 if ($line =~ m/^(\C):\s*(.*)/) {
496 my $type = $1;
497 my $value = $2;
498 if ($type eq 'F') {
499 if (file_match_pattern($file, $value)) {
500 my $value_pd = ($value =~ tr@/@@);
501 my $file_pd = ($file =~ tr@/@@);
502 $value_pd++ if (substr($value,-1,1) ne "/");
503 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perches6ef1c522010-10-26 14:22:56 -0700504 if ($value_pd >= $file_pd) {
505 $exact_pattern_match_hash{$file} = 1;
506 }
Joe Perches683c6f82010-10-26 14:22:55 -0700507 if ($pattern_depth == 0 ||
508 (($file_pd - $value_pd) < $pattern_depth)) {
509 $hash{$tvi} = $value_pd;
510 }
511 }
512 }
513 }
514 }
515 }
516 $tvi = $end + 1;
517 }
518
519 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
520 add_categories($line);
521 if ($sections) {
522 my $i;
523 my $start = find_starting_index($line);
524 my $end = find_ending_index($line);
525 for ($i = $start; $i < $end; $i++) {
526 my $line = $typevalue[$i];
527 if ($line =~ /^[FX]:/) { ##Restore file patterns
528 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
529 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
530 $line =~ s/\\\./\./g; ##Convert \. to .
531 $line =~ s/\.\*/\*/g; ##Convert .* to *
532 }
533 $line =~ s/^([A-Z]):/$1:\t/g;
534 print("$line\n");
535 }
536 print("\n");
537 }
538 }
Joe Perches683c6f82010-10-26 14:22:55 -0700539 }
540
541 if ($keywords) {
542 @keyword_tvi = sort_and_uniq(@keyword_tvi);
543 foreach my $line (@keyword_tvi) {
544 add_categories($line);
545 }
546 }
547
Joe Perches6ef1c522010-10-26 14:22:56 -0700548 @interactive_to = (@email_to, @list_to);
549
550 foreach my $file (@files) {
551 if ($email &&
552 ($email_git || ($email_git_fallback &&
553 !$exact_pattern_match_hash{$file}))) {
554 vcs_file_signoffs($file);
555 }
556 if ($email && $email_git_blame) {
557 vcs_file_blame($file);
558 }
559 }
560
Joe Perches683c6f82010-10-26 14:22:55 -0700561 if ($email) {
562 foreach my $chief (@penguin_chief) {
563 if ($chief =~ m/^(.*):(.*)/) {
564 my $email_address;
565
566 $email_address = format_email($1, $2, $email_usename);
567 if ($email_git_penguin_chiefs) {
568 push(@email_to, [$email_address, 'chief penguin']);
569 } else {
570 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
571 }
572 }
573 }
574
575 foreach my $email (@file_emails) {
576 my ($name, $address) = parse_email($email);
577
578 my $tmp_email = format_email($name, $address, $email_usename);
579 push_email_address($tmp_email, '');
580 add_role($tmp_email, 'in file');
581 }
582 }
583
584 my @to = ();
585 if ($email || $email_list) {
586 if ($email) {
587 @to = (@to, @email_to);
588 }
589 if ($email_list) {
590 @to = (@to, @list_to);
591 }
592 }
593
Joe Perches6ef1c522010-10-26 14:22:56 -0700594 if ($interactive) {
595 @interactive_to = @to;
596 @to = interactive_get_maintainers(\@interactive_to);
597 }
Joe Perches683c6f82010-10-26 14:22:55 -0700598
599 return @to;
600}
601
Joe Perchescb7301c2009-04-07 20:40:12 -0700602sub file_match_pattern {
603 my ($file, $pattern) = @_;
604 if (substr($pattern, -1) eq "/") {
605 if ($file =~ m@^$pattern@) {
606 return 1;
607 }
608 } else {
609 if ($file =~ m@^$pattern@) {
610 my $s1 = ($file =~ tr@/@@);
611 my $s2 = ($pattern =~ tr@/@@);
612 if ($s1 == $s2) {
613 return 1;
614 }
615 }
616 }
617 return 0;
618}
619
620sub usage {
621 print <<EOT;
622usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700623 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700624version: $V
625
626MAINTAINER field selection options:
627 --email => print email address(es) if any
628 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700629 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700630 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700631 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700632 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700633 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
634 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
635 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700636 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700637 --git-since => git history to use (default: $email_git_since)
638 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700639 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700640 --m => include maintainer(s) if any
641 --n => include name 'Full Name <addr\@domain.tld>'
642 --l => include list(s) if any
643 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700644 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800645 --roles => show roles (status:subsystem, git-signer, list, etc...)
646 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800647 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700648 --scm => print SCM tree(s) if any
649 --status => print status if any
650 --subsystem => print subsystem name if any
651 --web => print website(s) if any
652
653Output type options:
654 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700655 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700656 --multiline => print 1 entry per line
657
Joe Perchescb7301c2009-04-07 20:40:12 -0700658Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700659 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesdcf36a92009-10-26 16:49:47 -0700660 --keywords => scan patch for keywords (default: 1 (on))
Joe Perches4b76c9d2010-03-05 13:43:03 -0800661 --sections => print the entire subsystem sections with pattern matches
Joe Perchesf5f5078d2009-06-16 15:34:00 -0700662 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700663 --help => show this help information
664
Joe Perches3fb55652009-09-21 17:04:17 -0700665Default options:
Joe Perches11ecf532009-09-21 17:04:22 -0700666 [--email --git --m --n --l --multiline --pattern-depth=0 --remove-duplicates]
Joe Perches3fb55652009-09-21 17:04:17 -0700667
Joe Perches870020f2009-07-29 15:04:28 -0700668Notes:
669 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700670 Used with "--git", git signators for _all_ files in and below
671 directory are examined as git recurses directories.
672 Any specified X: (exclude) pattern matches are _not_ ignored.
673 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800674 no individual file within the directory or subdirectory
675 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700676 Used with "--git-blame", does not iterate all files in directory
677 Using "--git-blame" is slow and may add old committers and authors
678 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800679 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
680 other automated tools that expect only ["name"] <email address>
681 may not work because of additional output after <email address>.
682 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
683 not the percentage of the entire file authored. # of commits is
684 not a good measure of amount of code authored. 1 major commit may
685 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800686 If git is not installed, but mercurial (hg) is installed and an .hg
687 repository exists, the following options apply to mercurial:
688 --git,
689 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
690 --git-blame
691 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700692 File ".get_maintainer.conf", if it exists in the linux kernel source root
693 directory, can change whatever get_maintainer defaults are desired.
694 Entries in this file can be any command line argument.
695 This file is prepended to any additional command line arguments.
696 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700697EOT
698}
699
700sub top_of_kernel_tree {
701 my ($lk_path) = @_;
702
703 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
704 $lk_path .= "/";
705 }
706 if ( (-f "${lk_path}COPYING")
707 && (-f "${lk_path}CREDITS")
708 && (-f "${lk_path}Kbuild")
709 && (-f "${lk_path}MAINTAINERS")
710 && (-f "${lk_path}Makefile")
711 && (-f "${lk_path}README")
712 && (-d "${lk_path}Documentation")
713 && (-d "${lk_path}arch")
714 && (-d "${lk_path}include")
715 && (-d "${lk_path}drivers")
716 && (-d "${lk_path}fs")
717 && (-d "${lk_path}init")
718 && (-d "${lk_path}ipc")
719 && (-d "${lk_path}kernel")
720 && (-d "${lk_path}lib")
721 && (-d "${lk_path}scripts")) {
722 return 1;
723 }
724 return 0;
725}
726
Joe Perches0e70e832009-09-21 17:04:20 -0700727sub parse_email {
728 my ($formatted_email) = @_;
729
730 my $name = "";
731 my $address = "";
732
Joe Perches11ecf532009-09-21 17:04:22 -0700733 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700734 $name = $1;
735 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700736 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700737 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700738 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700739 $address = $1;
740 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700741
742 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700743 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700744 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700745
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800746 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700747 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700748 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700749 }
Joe Perches0e70e832009-09-21 17:04:20 -0700750
751 return ($name, $address);
752}
753
754sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800755 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700756
757 my $formatted_email;
758
759 $name =~ s/^\s+|\s+$//g;
760 $name =~ s/^\"|\"$//g;
761 $address =~ s/^\s+|\s+$//g;
762
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800763 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700764 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
765 $name = "\"$name\"";
766 }
767
Joe Perchesa8af2432009-12-14 18:00:49 -0800768 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700769 if ("$name" eq "") {
770 $formatted_email = "$address";
771 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800772 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700773 }
774 } else {
775 $formatted_email = $address;
776 }
777
Joe Perchescb7301c2009-04-07 20:40:12 -0700778 return $formatted_email;
779}
780
Joe Perches272a8972010-01-08 14:42:48 -0800781sub find_first_section {
782 my $index = 0;
783
784 while ($index < @typevalue) {
785 my $tv = $typevalue[$index];
786 if (($tv =~ m/^(\C):\s*(.*)/)) {
787 last;
788 }
789 $index++;
790 }
791
792 return $index;
793}
794
Joe Perchesb7816552009-09-21 17:04:24 -0700795sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700796 my ($index) = @_;
797
798 while ($index > 0) {
799 my $tv = $typevalue[$index];
800 if (!($tv =~ m/^(\C):\s*(.*)/)) {
801 last;
802 }
803 $index--;
804 }
805
806 return $index;
807}
808
809sub find_ending_index {
810 my ($index) = @_;
811
812 while ($index < @typevalue) {
813 my $tv = $typevalue[$index];
814 if (!($tv =~ m/^(\C):\s*(.*)/)) {
815 last;
816 }
817 $index++;
818 }
819
820 return $index;
821}
822
Joe Perches3c7385b2009-12-14 18:00:46 -0800823sub get_maintainer_role {
824 my ($index) = @_;
825
826 my $i;
827 my $start = find_starting_index($index);
828 my $end = find_ending_index($index);
829
830 my $role;
831 my $subsystem = $typevalue[$start];
832 if (length($subsystem) > 20) {
833 $subsystem = substr($subsystem, 0, 17);
834 $subsystem =~ s/\s*$//;
835 $subsystem = $subsystem . "...";
836 }
837
838 for ($i = $start + 1; $i < $end; $i++) {
839 my $tv = $typevalue[$i];
840 if ($tv =~ m/^(\C):\s*(.*)/) {
841 my $ptype = $1;
842 my $pvalue = $2;
843 if ($ptype eq "S") {
844 $role = $pvalue;
845 }
846 }
847 }
848
849 $role = lc($role);
850 if ($role eq "supported") {
851 $role = "supporter";
852 } elsif ($role eq "maintained") {
853 $role = "maintainer";
854 } elsif ($role eq "odd fixes") {
855 $role = "odd fixer";
856 } elsif ($role eq "orphan") {
857 $role = "orphan minder";
858 } elsif ($role eq "obsolete") {
859 $role = "obsolete minder";
860 } elsif ($role eq "buried alive in reporters") {
861 $role = "chief penguin";
862 }
863
864 return $role . ":" . $subsystem;
865}
866
867sub get_list_role {
868 my ($index) = @_;
869
870 my $i;
871 my $start = find_starting_index($index);
872 my $end = find_ending_index($index);
873
874 my $subsystem = $typevalue[$start];
875 if (length($subsystem) > 20) {
876 $subsystem = substr($subsystem, 0, 17);
877 $subsystem =~ s/\s*$//;
878 $subsystem = $subsystem . "...";
879 }
880
881 if ($subsystem eq "THE REST") {
882 $subsystem = "";
883 }
884
885 return $subsystem;
886}
887
Joe Perchescb7301c2009-04-07 20:40:12 -0700888sub add_categories {
889 my ($index) = @_;
890
Joe Perchesb7816552009-09-21 17:04:24 -0700891 my $i;
892 my $start = find_starting_index($index);
893 my $end = find_ending_index($index);
894
895 push(@subsystem, $typevalue[$start]);
896
897 for ($i = $start + 1; $i < $end; $i++) {
898 my $tv = $typevalue[$i];
Joe Perches290603c12009-06-16 15:33:58 -0700899 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700900 my $ptype = $1;
901 my $pvalue = $2;
902 if ($ptype eq "L") {
Joe Perches290603c12009-06-16 15:33:58 -0700903 my $list_address = $pvalue;
904 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -0800905 my $list_role = get_list_role($i);
906
907 if ($list_role ne "") {
908 $list_role = ":" . $list_role;
909 }
Joe Perches290603c12009-06-16 15:33:58 -0700910 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
911 $list_address = $1;
912 $list_additional = $2;
913 }
Joe Perchesbdf7c682009-06-16 15:33:59 -0700914 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700915 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700916 if (!$hash_list_to{lc($list_address)}) {
917 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -0700918 push(@list_to, [$list_address,
919 "subscriber list${list_role}"]);
920 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700921 }
922 } else {
923 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700924 if (!$hash_list_to{lc($list_address)}) {
925 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -0700926 push(@list_to, [$list_address,
927 "open list${list_role}"]);
928 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700929 }
930 }
931 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -0700932 my ($name, $address) = parse_email($pvalue);
933 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -0700934 if ($i > 0) {
935 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -0700936 if ($tv =~ m/^(\C):\s*(.*)/) {
937 if ($1 eq "P") {
938 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -0800939 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -0700940 }
941 }
942 }
943 }
Joe Perches0e70e832009-09-21 17:04:20 -0700944 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800945 my $role = get_maintainer_role($i);
946 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -0700947 }
948 } elsif ($ptype eq "T") {
949 push(@scm, $pvalue);
950 } elsif ($ptype eq "W") {
951 push(@web, $pvalue);
952 } elsif ($ptype eq "S") {
953 push(@status, $pvalue);
954 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700955 }
956 }
957}
958
Joe Perches11ecf532009-09-21 17:04:22 -0700959sub email_inuse {
960 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700961
Joe Perches11ecf532009-09-21 17:04:22 -0700962 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -0700963 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
964 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -0700965
Joe Perches0e70e832009-09-21 17:04:20 -0700966 return 0;
967}
968
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700969sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -0800970 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700971
Joe Perches0e70e832009-09-21 17:04:20 -0700972 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700973
Joe Perchesb7816552009-09-21 17:04:24 -0700974 if ($address eq "") {
975 return 0;
976 }
977
Joe Perches11ecf532009-09-21 17:04:22 -0700978 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -0800979 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -0700980 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -0800981 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches6ef1c522010-10-26 14:22:56 -0700982 $email_hash_name{lc($name)}++;
983 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700984 }
Joe Perchesb7816552009-09-21 17:04:24 -0700985
986 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700987}
988
989sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -0800990 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700991
992 my @address_list = ();
993
Joe Perches5f2441e2009-06-16 15:34:02 -0700994 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800995 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -0700996 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -0700997 my $array_count = shift(@address_list);
998 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800999 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001000 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001001 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001002 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001003 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1004 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001005 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001006}
1007
Joe Perches3c7385b2009-12-14 18:00:46 -08001008sub add_role {
1009 my ($line, $role) = @_;
1010
1011 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001012 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001013
1014 foreach my $entry (@email_to) {
1015 if ($email_remove_duplicates) {
1016 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001017 if (($name eq $entry_name || $address eq $entry_address)
1018 && ($role eq "" || !($entry->[1] =~ m/$role/))
1019 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001020 if ($entry->[1] eq "") {
1021 $entry->[1] = "$role";
1022 } else {
1023 $entry->[1] = "$entry->[1],$role";
1024 }
1025 }
1026 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001027 if ($email eq $entry->[0]
1028 && ($role eq "" || !($entry->[1] =~ m/$role/))
1029 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001030 if ($entry->[1] eq "") {
1031 $entry->[1] = "$role";
1032 } else {
1033 $entry->[1] = "$entry->[1],$role";
1034 }
1035 }
1036 }
1037 }
1038}
1039
Joe Perchescb7301c2009-04-07 20:40:12 -07001040sub which {
1041 my ($bin) = @_;
1042
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001043 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001044 if (-e "$path/$bin") {
1045 return "$path/$bin";
1046 }
1047 }
1048
1049 return "";
1050}
1051
Joe Perchesbcde44e2010-10-26 14:22:53 -07001052sub which_conf {
1053 my ($conf) = @_;
1054
1055 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1056 if (-e "$path/$conf") {
1057 return "$path/$conf";
1058 }
1059 }
1060
1061 return "";
1062}
1063
Joe Perches8cbb3a72009-09-21 17:04:21 -07001064sub mailmap {
Joe Perchesa8af2432009-12-14 18:00:49 -08001065 my (@lines) = @_;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001066 my %hash;
1067
1068 foreach my $line (@lines) {
1069 my ($name, $address) = parse_email($line);
1070 if (!exists($hash{$name})) {
1071 $hash{$name} = $address;
Joe Perches11ecf532009-09-21 17:04:22 -07001072 } elsif ($address ne $hash{$name}) {
1073 $address = $hash{$name};
Joe Perchesa8af2432009-12-14 18:00:49 -08001074 $line = format_email($name, $address, $email_usename);
Joe Perches8cbb3a72009-09-21 17:04:21 -07001075 }
1076 if (exists($mailmap{$name})) {
1077 my $obj = $mailmap{$name};
1078 foreach my $map_address (@$obj) {
1079 if (($map_address eq $address) &&
1080 ($map_address ne $hash{$name})) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001081 $line = format_email($name, $hash{$name}, $email_usename);
Joe Perches8cbb3a72009-09-21 17:04:21 -07001082 }
1083 }
1084 }
1085 }
1086
1087 return @lines;
1088}
1089
Joe Perches60db31a2009-12-14 18:00:50 -08001090sub git_execute_cmd {
1091 my ($cmd) = @_;
1092 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001093
Joe Perches60db31a2009-12-14 18:00:50 -08001094 my $output = `$cmd`;
1095 $output =~ s/^\s*//gm;
1096 @lines = split("\n", $output);
1097
1098 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001099}
1100
Joe Perches60db31a2009-12-14 18:00:50 -08001101sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001102 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001103 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001104
Joe Perches60db31a2009-12-14 18:00:50 -08001105 my $output = `$cmd`;
1106 @lines = split("\n", $output);
1107
1108 return @lines;
1109}
1110
Joe Perches683c6f82010-10-26 14:22:55 -07001111sub extract_formatted_signatures {
1112 my (@signature_lines) = @_;
1113
1114 my @type = @signature_lines;
1115
1116 s/\s*(.*):.*/$1/ for (@type);
1117
1118 # cut -f2- -d":"
1119 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1120
1121## Reformat email addresses (with names) to avoid badly written signatures
1122
1123 foreach my $signer (@signature_lines) {
1124 my ($name, $address) = parse_email($signer);
1125 $signer = format_email($name, $address, 1);
1126 }
1127
1128 return (\@type, \@signature_lines);
1129}
1130
Joe Perches60db31a2009-12-14 18:00:50 -08001131sub vcs_find_signers {
1132 my ($cmd) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001133 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001134 my @lines = ();
1135 my @signatures = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001136
Joe Perches60db31a2009-12-14 18:00:50 -08001137 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001138
Joe Perches60db31a2009-12-14 18:00:50 -08001139 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchescb7301c2009-04-07 20:40:12 -07001140
Joe Perches60db31a2009-12-14 18:00:50 -08001141 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001142
Joe Perches683c6f82010-10-26 14:22:55 -07001143 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
1144
1145 return (0, @signatures) if !@signatures;
1146
1147 save_commits_by_author(@lines) if ($interactive);
1148 save_commits_by_signer(@lines) if ($interactive);
1149
Joe Perches0e70e832009-09-21 17:04:20 -07001150 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001151 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001152 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001153
Joe Perches683c6f82010-10-26 14:22:55 -07001154 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001155
Joe Perches683c6f82010-10-26 14:22:55 -07001156 return ($commits, @$signers_ref);
Joe Perchesa8af2432009-12-14 18:00:49 -08001157}
1158
Joe Perches63ab52d2010-10-26 14:22:51 -07001159sub vcs_find_author {
1160 my ($cmd) = @_;
1161 my @lines = ();
1162
1163 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1164
1165 if (!$email_git_penguin_chiefs) {
1166 @lines = grep(!/${penguin_chiefs}/i, @lines);
1167 }
1168
1169 return @lines if !@lines;
1170
Joe Perches683c6f82010-10-26 14:22:55 -07001171 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001172 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001173 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1174 my $author = $1;
1175 my ($name, $address) = parse_email($author);
1176 $author = format_email($name, $address, 1);
1177 push(@authors, $author);
1178 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001179 }
1180
Joe Perches683c6f82010-10-26 14:22:55 -07001181 save_commits_by_author(@lines) if ($interactive);
1182 save_commits_by_signer(@lines) if ($interactive);
1183
1184 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001185}
1186
Joe Perches60db31a2009-12-14 18:00:50 -08001187sub vcs_save_commits {
1188 my ($cmd) = @_;
1189 my @lines = ();
1190 my @commits = ();
1191
1192 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1193
1194 foreach my $line (@lines) {
1195 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1196 push(@commits, $1);
1197 }
1198 }
1199
1200 return @commits;
1201}
1202
1203sub vcs_blame {
1204 my ($file) = @_;
1205 my $cmd;
1206 my @commits = ();
1207
1208 return @commits if (!(-f $file));
1209
1210 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1211 my @all_commits = ();
1212
1213 $cmd = $VCS_cmds{"blame_file_cmd"};
1214 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1215 @all_commits = vcs_save_commits($cmd);
1216
1217 foreach my $file_range_diff (@range) {
1218 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1219 my $diff_file = $1;
1220 my $diff_start = $2;
1221 my $diff_length = $3;
1222 next if ("$file" ne "$diff_file");
1223 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1224 push(@commits, $all_commits[$i]);
1225 }
1226 }
1227 } elsif (@range) {
1228 foreach my $file_range_diff (@range) {
1229 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1230 my $diff_file = $1;
1231 my $diff_start = $2;
1232 my $diff_length = $3;
1233 next if ("$file" ne "$diff_file");
1234 $cmd = $VCS_cmds{"blame_range_cmd"};
1235 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1236 push(@commits, vcs_save_commits($cmd));
1237 }
1238 } else {
1239 $cmd = $VCS_cmds{"blame_file_cmd"};
1240 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1241 @commits = vcs_save_commits($cmd);
1242 }
1243
Joe Perches63ab52d2010-10-26 14:22:51 -07001244 foreach my $commit (@commits) {
1245 $commit =~ s/^\^//g;
1246 }
1247
Joe Perches60db31a2009-12-14 18:00:50 -08001248 return @commits;
1249}
1250
1251my $printed_novcs = 0;
1252sub vcs_exists {
1253 %VCS_cmds = %VCS_cmds_git;
1254 return 1 if eval $VCS_cmds{"available"};
1255 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001256 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001257 %VCS_cmds = ();
1258 if (!$printed_novcs) {
1259 warn("$P: No supported VCS found. Add --nogit to options?\n");
1260 warn("Using a git repository produces better results.\n");
1261 warn("Try Linus Torvalds' latest git repository using:\n");
1262 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git\n");
1263 $printed_novcs = 1;
1264 }
1265 return 0;
1266}
1267
Joe Perches683c6f82010-10-26 14:22:55 -07001268sub vcs_is_git {
1269 return $vcs_used == 1;
1270}
1271
1272sub vcs_is_hg {
1273 return $vcs_used == 2;
1274}
1275
Joe Perches6ef1c522010-10-26 14:22:56 -07001276sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001277 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001278 my @list = @$list_ref;
1279
Joe Perches683c6f82010-10-26 14:22:55 -07001280 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001281
1282 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001283 my %authored;
1284 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001285 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001286 my $maintained = 0;
Florian Micklerdace8e32010-10-26 14:22:54 -07001287 #select maintainers by default
Joe Perches6ef1c522010-10-26 14:22:56 -07001288 foreach my $entry (@list) {
Joe Perches683c6f82010-10-26 14:22:55 -07001289 my $role = $entry->[1];
Joe Perches6ef1c522010-10-26 14:22:56 -07001290 $selected{$count} = ($role =~ /^(maintainer|supporter|open list)/i);
1291 $maintained = 1 if ($role =~ /^(maintainer|supporter)/i);
Joe Perches683c6f82010-10-26 14:22:55 -07001292 $authored{$count} = 0;
1293 $signed{$count} = 0;
1294 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001295 }
1296
1297 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001298 my $done = 0;
1299 my $print_options = 0;
1300 my $redraw = 1;
1301 while (!$done) {
1302 $count = 0;
1303 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001304 printf STDERR "\n%1s %2s %-65s",
1305 "*", "#", "email/list and role:stats";
1306 if ($email_git ||
1307 ($email_git_fallback && !$maintained) ||
1308 $email_git_blame) {
1309 print STDERR "auth sign";
1310 }
1311 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001312 foreach my $entry (@list) {
1313 my $email = $entry->[0];
1314 my $role = $entry->[1];
1315 my $sel = "";
1316 $sel = "*" if ($selected{$count});
1317 my $commit_author = $commit_author_hash{$email};
1318 my $commit_signer = $commit_signer_hash{$email};
1319 my $authored = 0;
1320 my $signed = 0;
1321 $authored++ for (@{$commit_author});
1322 $signed++ for (@{$commit_signer});
1323 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1324 printf STDERR "%4d %4d", $authored, $signed
1325 if ($authored > 0 || $signed > 0);
1326 printf STDERR "\n %s\n", $role;
1327 if ($authored{$count}) {
1328 my $commit_author = $commit_author_hash{$email};
1329 foreach my $ref (@{$commit_author}) {
1330 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001331 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001332 }
Joe Perches683c6f82010-10-26 14:22:55 -07001333 if ($signed{$count}) {
1334 my $commit_signer = $commit_signer_hash{$email};
1335 foreach my $ref (@{$commit_signer}) {
1336 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1337 }
1338 }
1339
1340 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001341 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001342 }
Joe Perches683c6f82010-10-26 14:22:55 -07001343 my $date_ref = \$email_git_since;
1344 $date_ref = \$email_hg_since if (vcs_is_hg());
1345 if ($print_options) {
1346 $print_options = 0;
1347 if (vcs_exists()) {
1348 print STDERR
1349"\nVersion Control options:\n" .
1350"g use git history [$email_git]\n" .
1351"gf use git-fallback [$email_git_fallback]\n" .
1352"b use git blame [$email_git_blame]\n" .
1353"bs use blame signatures [$email_git_blame_signatures]\n" .
1354"c# minimum commits [$email_git_min_signatures]\n" .
1355"%# min percent [$email_git_min_percent]\n" .
1356"d# history to use [$$date_ref]\n" .
1357"x# max maintainers [$email_git_max_maintainers]\n" .
1358"t all signature types [$email_git_all_signature_types]\n";
1359 }
1360 print STDERR "\nAdditional options:\n" .
1361"0 toggle all\n" .
1362"f emails in file [$file_emails]\n" .
1363"k keywords in file [$keywords]\n" .
1364"r remove duplicates [$email_remove_duplicates]\n" .
1365"p# pattern match depth [$pattern_depth]\n";
1366 }
1367 print STDERR
1368"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1369
1370 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001371 chomp($input);
1372
Joe Perches683c6f82010-10-26 14:22:55 -07001373 $redraw = 1;
1374 my $rerun = 0;
1375 my @wish = split(/[, ]+/, $input);
1376 foreach my $nr (@wish) {
1377 $nr = lc($nr);
1378 my $sel = substr($nr, 0, 1);
1379 my $str = substr($nr, 1);
1380 my $val = 0;
1381 $val = $1 if $str =~ /^(\d+)$/;
1382
1383 if ($sel eq "y") {
1384 $interactive = 0;
1385 $done = 1;
1386 $output_rolestats = 0;
1387 $output_roles = 0;
1388 last;
1389 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1390 $selected{$nr - 1} = !$selected{$nr - 1};
1391 } elsif ($sel eq "*" || $sel eq '^') {
1392 my $toggle = 0;
1393 $toggle = 1 if ($sel eq '*');
1394 for (my $i = 0; $i < $count; $i++) {
1395 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001396 }
Joe Perches683c6f82010-10-26 14:22:55 -07001397 } elsif ($sel eq "0") {
1398 for (my $i = 0; $i < $count; $i++) {
1399 $selected{$i} = !$selected{$i};
1400 }
1401 } elsif ($sel eq "a") {
1402 if ($val > 0 && $val <= $count) {
1403 $authored{$val - 1} = !$authored{$val - 1};
1404 } elsif ($str eq '*' || $str eq '^') {
1405 my $toggle = 0;
1406 $toggle = 1 if ($str eq '*');
1407 for (my $i = 0; $i < $count; $i++) {
1408 $authored{$i} = $toggle;
1409 }
1410 }
1411 } elsif ($sel eq "s") {
1412 if ($val > 0 && $val <= $count) {
1413 $signed{$val - 1} = !$signed{$val - 1};
1414 } elsif ($str eq '*' || $str eq '^') {
1415 my $toggle = 0;
1416 $toggle = 1 if ($str eq '*');
1417 for (my $i = 0; $i < $count; $i++) {
1418 $signed{$i} = $toggle;
1419 }
1420 }
1421 } elsif ($sel eq "o") {
1422 $print_options = 1;
1423 $redraw = 1;
1424 } elsif ($sel eq "g") {
1425 if ($str eq "f") {
1426 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001427 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001428 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001429 }
Joe Perches683c6f82010-10-26 14:22:55 -07001430 $rerun = 1;
1431 } elsif ($sel eq "b") {
1432 if ($str eq "s") {
1433 bool_invert(\$email_git_blame_signatures);
1434 } else {
1435 bool_invert(\$email_git_blame);
1436 }
1437 $rerun = 1;
1438 } elsif ($sel eq "c") {
1439 if ($val > 0) {
1440 $email_git_min_signatures = $val;
1441 $rerun = 1;
1442 }
1443 } elsif ($sel eq "x") {
1444 if ($val > 0) {
1445 $email_git_max_maintainers = $val;
1446 $rerun = 1;
1447 }
1448 } elsif ($sel eq "%") {
1449 if ($str ne "" && $val >= 0) {
1450 $email_git_min_percent = $val;
1451 $rerun = 1;
1452 }
1453 } elsif ($sel eq "d") {
1454 if (vcs_is_git()) {
1455 $email_git_since = $str;
1456 } elsif (vcs_is_hg()) {
1457 $email_hg_since = $str;
1458 }
1459 $rerun = 1;
1460 } elsif ($sel eq "t") {
1461 bool_invert(\$email_git_all_signature_types);
1462 $rerun = 1;
1463 } elsif ($sel eq "f") {
1464 bool_invert(\$file_emails);
1465 $rerun = 1;
1466 } elsif ($sel eq "r") {
1467 bool_invert(\$email_remove_duplicates);
1468 $rerun = 1;
1469 } elsif ($sel eq "k") {
1470 bool_invert(\$keywords);
1471 $rerun = 1;
1472 } elsif ($sel eq "p") {
1473 if ($str ne "" && $val >= 0) {
1474 $pattern_depth = $val;
1475 $rerun = 1;
1476 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001477 } elsif ($sel eq "h" || $sel eq "?") {
1478 print STDERR <<EOT
1479
1480Interactive mode allows you to select the various maintainers, submitters,
1481commit signers and mailing lists that could be CC'd on a patch.
1482
1483Any *'d entry is selected.
1484
1485If you have git or hg installed, You can choose to summarize the commit
1486history of files in the patch. Also, each line of the current file can
1487be matched to its commit author and that commits signers with blame.
1488
1489Various knobs exist to control the length of time for active commit
1490tracking, the maximum number of commit authors and signers to add,
1491and such.
1492
1493Enter selections at the prompt until you are satisfied that the selected
1494maintainers are appropriate. You may enter multiple selections separated
1495by either commas or spaces.
1496
1497EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001498 } else {
1499 print STDERR "invalid option: '$nr'\n";
1500 $redraw = 0;
1501 }
1502 }
1503 if ($rerun) {
1504 print STDERR "git-blame can be very slow, please have patience..."
1505 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001506 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001507 }
1508 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001509
1510 #drop not selected entries
1511 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001512 my @new_emailto = ();
1513 foreach my $entry (@list) {
1514 if ($selected{$count}) {
1515 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001516 }
1517 $count++;
1518 }
Joe Perches683c6f82010-10-26 14:22:55 -07001519 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001520}
1521
Joe Perches683c6f82010-10-26 14:22:55 -07001522sub bool_invert {
1523 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001524
Joe Perches683c6f82010-10-26 14:22:55 -07001525 if ($$bool_ref) {
1526 $$bool_ref = 0;
1527 } else {
1528 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001529 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001530}
1531
Joe Perches683c6f82010-10-26 14:22:55 -07001532sub save_commits_by_author {
1533 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001534
Joe Perches683c6f82010-10-26 14:22:55 -07001535 my @authors = ();
1536 my @commits = ();
1537 my @subjects = ();
1538
1539 foreach my $line (@lines) {
1540 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001541 my $matched = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001542 my $author = $1;
1543 my ($name, $address) = parse_email($author);
Joe Perches6ef1c522010-10-26 14:22:56 -07001544 foreach my $to (@interactive_to) {
1545 my ($to_name, $to_address) = parse_email($to->[0]);
1546 if ($email_remove_duplicates &&
1547 ((lc($name) eq lc($to_name)) ||
1548 (lc($address) eq lc($to_address)))) {
1549 $author = $to->[0];
1550 $matched = 1;
1551 last;
1552 }
1553 }
1554 $author = format_email($name, $address, 1) if (!$matched);
Joe Perches683c6f82010-10-26 14:22:55 -07001555 push(@authors, $author);
1556 }
1557 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1558 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1559 }
1560
1561 for (my $i = 0; $i < @authors; $i++) {
1562 my $exists = 0;
1563 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1564 if (@{$ref}[0] eq $commits[$i] &&
1565 @{$ref}[1] eq $subjects[$i]) {
1566 $exists = 1;
1567 last;
1568 }
1569 }
1570 if (!$exists) {
1571 push(@{$commit_author_hash{$authors[$i]}},
1572 [ ($commits[$i], $subjects[$i]) ]);
1573 }
1574 }
1575}
1576
1577sub save_commits_by_signer {
1578 my (@lines) = @_;
1579
1580 my $commit = "";
1581 my $subject = "";
1582
1583 foreach my $line (@lines) {
1584 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1585 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1586 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1587 my @signatures = ($line);
1588 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1589 my @types = @$types_ref;
1590 my @signers = @$signers_ref;
1591
1592 my $type = $types[0];
1593 my $signer = $signers[0];
1594
Joe Perches6ef1c522010-10-26 14:22:56 -07001595 my $matched = 0;
1596 my ($name, $address) = parse_email($signer);
1597 foreach my $to (@interactive_to) {
1598 my ($to_name, $to_address) = parse_email($to->[0]);
1599 if ($email_remove_duplicates &&
1600 ((lc($name) eq lc($to_name)) ||
1601 (lc($address) eq lc($to_address)))) {
1602 $signer = $to->[0];
1603 $matched = 1;
1604 last;
1605 }
1606 $signer = format_email($name, $address, 1) if (!$matched);
1607 }
1608
Joe Perches683c6f82010-10-26 14:22:55 -07001609 my $exists = 0;
1610 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1611 if (@{$ref}[0] eq $commit &&
1612 @{$ref}[1] eq $subject &&
1613 @{$ref}[2] eq $type) {
1614 $exists = 1;
1615 last;
1616 }
1617 }
1618 if (!$exists) {
1619 push(@{$commit_signer_hash{$signer}},
1620 [ ($commit, $subject, $type) ]);
1621 }
1622 }
1623 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001624}
1625
Joe Perches60db31a2009-12-14 18:00:50 -08001626sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001627 my ($role, $divisor, @lines) = @_;
1628
1629 my %hash;
1630 my $count = 0;
1631
Joe Perchesa8af2432009-12-14 18:00:49 -08001632 return if (@lines <= 0);
1633
1634 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001635 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001636 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001637 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001638
Joe Perches11ecf532009-09-21 17:04:22 -07001639 if ($email_remove_duplicates) {
1640 @lines = mailmap(@lines);
1641 }
Joe Perches0e70e832009-09-21 17:04:20 -07001642
Joe Perches63ab52d2010-10-26 14:22:51 -07001643 return if (@lines <= 0);
1644
Joe Perches0e70e832009-09-21 17:04:20 -07001645 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001646
Joe Perches11ecf532009-09-21 17:04:22 -07001647 # uniq -c
1648 $hash{$_}++ for @lines;
1649
1650 # sort -rn
1651 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1652 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001653 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001654
Joe Perchesa8af2432009-12-14 18:00:49 -08001655 $percent = 100 if ($percent > 100);
Joe Perches11ecf532009-09-21 17:04:22 -07001656 $count++;
1657 last if ($sign_offs < $email_git_min_signatures ||
1658 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001659 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001660 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001661 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001662 my $fmt_percent = sprintf("%.0f", $percent);
1663 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1664 } else {
1665 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001666 }
Joe Perchesf5492662009-09-21 17:04:13 -07001667 }
1668}
1669
Joe Perches60db31a2009-12-14 18:00:50 -08001670sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001671 my ($file) = @_;
1672
1673 my @signers = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001674 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001675
Joe Perches683c6f82010-10-26 14:22:55 -07001676 $vcs_used = vcs_exists();
1677 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001678
Joe Perches60db31a2009-12-14 18:00:50 -08001679 my $cmd = $VCS_cmds{"find_signers_cmd"};
1680 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1681
1682 ($commits, @signers) = vcs_find_signers($cmd);
1683 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001684}
1685
Joe Perches60db31a2009-12-14 18:00:50 -08001686sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001687 my ($file) = @_;
1688
Joe Perches60db31a2009-12-14 18:00:50 -08001689 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001690 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001691 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001692 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001693 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001694
Joe Perches683c6f82010-10-26 14:22:55 -07001695 $vcs_used = vcs_exists();
1696 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07001697
Joe Perches63ab52d2010-10-26 14:22:51 -07001698 @all_commits = vcs_blame($file);
1699 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08001700 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001701 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001702
Joe Perches683c6f82010-10-26 14:22:55 -07001703 if ($email_git_blame_signatures) {
1704 if (vcs_is_hg()) {
1705 my $commit_count;
1706 my @commit_signers = ();
1707 my $commit = join(" -r ", @commits);
1708 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07001709
Joe Perches683c6f82010-10-26 14:22:55 -07001710 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1711 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08001712
Joe Perches683c6f82010-10-26 14:22:55 -07001713 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
Joe Perches63ab52d2010-10-26 14:22:51 -07001714
Joe Perches683c6f82010-10-26 14:22:55 -07001715 push(@signers, @commit_signers);
1716 } else {
1717 foreach my $commit (@commits) {
1718 my $commit_count;
1719 my @commit_signers = ();
1720 my $cmd;
1721
1722 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1723 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1724
1725 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
1726
1727 push(@signers, @commit_signers);
1728 }
1729 }
Joe Perchesf5492662009-09-21 17:04:13 -07001730 }
1731
Joe Perchesa8af2432009-12-14 18:00:49 -08001732 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07001733 if ($output_rolestats) {
1734 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07001735 if (vcs_is_hg()) {{ # Double brace for last exit
1736 my $commit_count;
1737 my @commit_signers = ();
1738 @commits = uniq(@commits);
1739 @commits = sort(@commits);
1740 my $commit = join(" -r ", @commits);
1741 my $cmd;
1742
1743 $cmd = $VCS_cmds{"find_commit_author_cmd"};
1744 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1745
1746 my @lines = ();
1747
1748 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1749
1750 if (!$email_git_penguin_chiefs) {
1751 @lines = grep(!/${penguin_chiefs}/i, @lines);
1752 }
1753
1754 last if !@lines;
1755
1756 my @authors = ();
1757 foreach my $line (@lines) {
1758 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1759 my $author = $1;
1760 my ($name, $address) = parse_email($author);
1761 $author = format_email($name, $address, 1);
1762 push(@authors, $1);
1763 }
1764 }
1765
1766 save_commits_by_author(@lines) if ($interactive);
1767 save_commits_by_signer(@lines) if ($interactive);
1768
1769 push(@signers, @authors);
1770 }}
1771 else {
1772 foreach my $commit (@commits) {
1773 my $i;
1774 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
1775 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1776 my @author = vcs_find_author($cmd);
1777 next if !@author;
1778 my $count = grep(/$commit/, @all_commits);
1779 for ($i = 0; $i < $count ; $i++) {
1780 push(@blame_signers, $author[0]);
1781 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001782 }
1783 }
1784 if (@blame_signers) {
1785 vcs_assign("authored lines", $total_lines, @blame_signers);
1786 }
1787 }
Joe Perches60db31a2009-12-14 18:00:50 -08001788 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001789 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08001790 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07001791 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001792}
1793
1794sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001795 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001796
1797 my %saw;
1798 @parms = grep(!$saw{$_}++, @parms);
1799 return @parms;
1800}
1801
1802sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001803 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001804
1805 my %saw;
1806 @parms = sort @parms;
1807 @parms = grep(!$saw{$_}++, @parms);
1808 return @parms;
1809}
1810
Joe Perches03372db2010-03-05 13:43:00 -08001811sub clean_file_emails {
1812 my (@file_emails) = @_;
1813 my @fmt_emails = ();
1814
1815 foreach my $email (@file_emails) {
1816 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
1817 my ($name, $address) = parse_email($email);
1818 if ($name eq '"[,\.]"') {
1819 $name = "";
1820 }
1821
1822 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
1823 if (@nw > 2) {
1824 my $first = $nw[@nw - 3];
1825 my $middle = $nw[@nw - 2];
1826 my $last = $nw[@nw - 1];
1827
1828 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
1829 (length($first) == 2 && substr($first, -1) eq ".")) ||
1830 (length($middle) == 1 ||
1831 (length($middle) == 2 && substr($middle, -1) eq "."))) {
1832 $name = "$first $middle $last";
1833 } else {
1834 $name = "$middle $last";
1835 }
1836 }
1837
1838 if (substr($name, -1) =~ /[,\.]/) {
1839 $name = substr($name, 0, length($name) - 1);
1840 } elsif (substr($name, -2) =~ /[,\.]"/) {
1841 $name = substr($name, 0, length($name) - 2) . '"';
1842 }
1843
1844 if (substr($name, 0, 1) =~ /[,\.]/) {
1845 $name = substr($name, 1, length($name) - 1);
1846 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
1847 $name = '"' . substr($name, 2, length($name) - 2);
1848 }
1849
1850 my $fmt_email = format_email($name, $address, $email_usename);
1851 push(@fmt_emails, $fmt_email);
1852 }
1853 return @fmt_emails;
1854}
1855
Joe Perches3c7385b2009-12-14 18:00:46 -08001856sub merge_email {
1857 my @lines;
1858 my %saw;
1859
1860 for (@_) {
1861 my ($address, $role) = @$_;
1862 if (!$saw{$address}) {
1863 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08001864 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08001865 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08001866 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08001867 }
1868 $saw{$address} = 1;
1869 }
1870 }
1871
1872 return @lines;
1873}
1874
Joe Perchescb7301c2009-04-07 20:40:12 -07001875sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08001876 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001877
1878 if ($output_multiline) {
1879 foreach my $line (@parms) {
1880 print("${line}\n");
1881 }
1882 } else {
1883 print(join($output_separator, @parms));
1884 print("\n");
1885 }
1886}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001887
1888my $rfc822re;
1889
1890sub make_rfc822re {
1891# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
1892# comment. We must allow for rfc822_lwsp (or comments) after each of these.
1893# This regexp will only work on addresses which have had comments stripped
1894# and replaced with rfc822_lwsp.
1895
1896 my $specials = '()<>@,;:\\\\".\\[\\]';
1897 my $controls = '\\000-\\037\\177';
1898
1899 my $dtext = "[^\\[\\]\\r\\\\]";
1900 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
1901
1902 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
1903
1904# Use zero-width assertion to spot the limit of an atom. A simple
1905# $rfc822_lwsp* causes the regexp engine to hang occasionally.
1906 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
1907 my $word = "(?:$atom|$quoted_string)";
1908 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
1909
1910 my $sub_domain = "(?:$atom|$domain_literal)";
1911 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
1912
1913 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
1914
1915 my $phrase = "$word*";
1916 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
1917 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
1918 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
1919
1920 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
1921 my $address = "(?:$mailbox|$group)";
1922
1923 return "$rfc822_lwsp*$address";
1924}
1925
1926sub rfc822_strip_comments {
1927 my $s = shift;
1928# Recursively remove comments, and replace with a single space. The simpler
1929# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
1930# chars in atoms, for example.
1931
1932 while ($s =~ s/^((?:[^"\\]|\\.)*
1933 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
1934 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
1935 return $s;
1936}
1937
1938# valid: returns true if the parameter is an RFC822 valid address
1939#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08001940sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001941 my $s = rfc822_strip_comments(shift);
1942
1943 if (!$rfc822re) {
1944 $rfc822re = make_rfc822re();
1945 }
1946
1947 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
1948}
1949
1950# validlist: In scalar context, returns true if the parameter is an RFC822
1951# valid list of addresses.
1952#
1953# In list context, returns an empty list on failure (an invalid
1954# address was found); otherwise a list whose first element is the
1955# number of addresses found and whose remaining elements are the
1956# addresses. This is needed to disambiguate failure (invalid)
1957# from success with no addresses found, because an empty string is
1958# a valid list.
1959
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08001960sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001961 my $s = rfc822_strip_comments(shift);
1962
1963 if (!$rfc822re) {
1964 $rfc822re = make_rfc822re();
1965 }
1966 # * null list items are valid according to the RFC
1967 # * the '1' business is to aid in distinguishing failure from no results
1968
1969 my @r;
1970 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
1971 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07001972 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08001973 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001974 }
1975 return wantarray ? (scalar(@r), @r) : 1;
1976 }
Joe Perches60db31a2009-12-14 18:00:50 -08001977 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001978}