blob: 0abfdbc5cdffb467ad7adc14548149351fa9ab14 [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 -0700298
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700299#
300# Read mail address map
301#
302
303my $mailmap = read_mailmap();
304
305sub read_mailmap {
306 my $mailmap = {
307 names => {},
308 addresses => {}
Joe Perches47abc722010-10-26 14:22:57 -0700309 };
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700310
311 if (!$email_remove_duplicates) {
312 return $mailmap;
313 }
314
315 open(my $mailmap_file, '<', "${lk_path}.mailmap")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800316 or warn "$P: Can't open .mailmap: $!\n";
Joe Perches8cbb3a72009-09-21 17:04:21 -0700317
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700318 while (<$mailmap_file>) {
319 s/#.*$//; #strip comments
320 s/^\s+|\s+$//g; #trim
Joe Perches8cbb3a72009-09-21 17:04:21 -0700321
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700322 next if (/^\s*$/); #skip empty lines
323 #entries have one of the following formats:
324 # name1 <mail1>
325 # <mail1> <mail2>
326 # name1 <mail1> <mail2>
327 # name1 <mail1> name2 <mail2>
328 # (see man git-shortlog)
329 if (/^(.+)<(.+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700330 my $real_name = $1;
331 my $address = $2;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700332
Joe Perches47abc722010-10-26 14:22:57 -0700333 $real_name =~ s/\s+$//;
334 $mailmap->{names}->{$address} = $real_name;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700335
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700336 } elsif (/^<([^\s]+)>\s*<([^\s]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700337 my $real_address = $1;
338 my $wrong_address = $2;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700339
Joe Perches47abc722010-10-26 14:22:57 -0700340 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700341
342 } elsif (/^(.+)<([^\s]+)>\s*<([^\s]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700343 my $real_name= $1;
344 my $real_address = $2;
345 my $wrong_address = $3;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700346
Joe Perches47abc722010-10-26 14:22:57 -0700347 $real_name =~ s/\s+$//;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700348
Joe Perches47abc722010-10-26 14:22:57 -0700349 $mailmap->{names}->{$wrong_address} = $real_name;
350 $mailmap->{addresses}->{$wrong_address} = $real_address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700351
352 } elsif (/^(.+)<([^\s]+)>\s*([^\s].*)<([^\s]+)>$/) {
Joe Perches47abc722010-10-26 14:22:57 -0700353 my $real_name = $1;
354 my $real_address = $2;
355 my $wrong_name = $3;
356 my $wrong_address = $4;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700357
Joe Perches47abc722010-10-26 14:22:57 -0700358 $real_name =~ s/\s+$//;
359 $wrong_name =~ s/\s+$//;
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700360
Joe Perches47abc722010-10-26 14:22:57 -0700361 $mailmap->{names}->{format_email($wrong_name,$wrong_address,1)} = $real_name;
362 $mailmap->{addresses}->{format_email($wrong_name,$wrong_address,1)} = $real_address;
Joe Perches11ecf532009-09-21 17:04:22 -0700363 }
Joe Perches8cbb3a72009-09-21 17:04:21 -0700364 }
Florian Mickler7fa8ff22010-10-26 14:22:56 -0700365 close($mailmap_file);
366
367 return $mailmap;
Joe Perches8cbb3a72009-09-21 17:04:21 -0700368}
369
Joe Perches4a7fdb52009-04-10 12:28:57 -0700370## use the filenames on the command line or find the filenames in the patchfiles
Joe Perchescb7301c2009-04-07 20:40:12 -0700371
372my @files = ();
Joe Perchesf5492662009-09-21 17:04:13 -0700373my @range = ();
Joe Perchesdcf36a92009-10-26 16:49:47 -0700374my @keyword_tvi = ();
Joe Perches03372db2010-03-05 13:43:00 -0800375my @file_emails = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700376
Joe Perches64f77f32010-03-05 13:43:04 -0800377if (!@ARGV) {
378 push(@ARGV, "&STDIN");
379}
380
Joe Perches4a7fdb52009-04-10 12:28:57 -0700381foreach my $file (@ARGV) {
Joe Perches64f77f32010-03-05 13:43:04 -0800382 if ($file ne "&STDIN") {
383 ##if $file is a directory and it lacks a trailing slash, add one
384 if ((-d $file)) {
385 $file =~ s@([^/])$@$1/@;
386 } elsif (!(-f $file)) {
387 die "$P: file '${file}' not found\n";
388 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700389 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700390 if ($from_filename) {
391 push(@files, $file);
Joe Perchesfab9ed12010-10-26 14:22:52 -0700392 if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) {
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800393 open(my $f, '<', $file)
394 or die "$P: Can't open $file: $!\n";
395 my $text = do { local($/) ; <$f> };
396 close($f);
Joe Perches03372db2010-03-05 13:43:00 -0800397 if ($keywords) {
398 foreach my $line (keys %keyword_hash) {
399 if ($text =~ m/$keyword_hash{$line}/x) {
400 push(@keyword_tvi, $line);
401 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700402 }
403 }
Joe Perches03372db2010-03-05 13:43:00 -0800404 if ($file_emails) {
405 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;
406 push(@file_emails, clean_file_emails(@poss_addr));
407 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700408 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700409 } else {
410 my $file_cnt = @files;
Joe Perchesf5492662009-09-21 17:04:13 -0700411 my $lastfile;
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800412
Wolfram Sang3a4df132010-03-23 13:35:18 -0700413 open(my $patch, "< $file")
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800414 or die "$P: Can't open $file: $!\n";
415 while (<$patch>) {
Joe Perchesdcf36a92009-10-26 16:49:47 -0700416 my $patch_line = $_;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700417 if (m/^\+\+\+\s+(\S+)/) {
418 my $filename = $1;
419 $filename =~ s@^[^/]*/@@;
420 $filename =~ s@\n@@;
Joe Perchesf5492662009-09-21 17:04:13 -0700421 $lastfile = $filename;
Joe Perches4a7fdb52009-04-10 12:28:57 -0700422 push(@files, $filename);
Joe Perchesf5492662009-09-21 17:04:13 -0700423 } elsif (m/^\@\@ -(\d+),(\d+)/) {
424 if ($email_git_blame) {
425 push(@range, "$lastfile:$1:$2");
426 }
Joe Perchesdcf36a92009-10-26 16:49:47 -0700427 } elsif ($keywords) {
428 foreach my $line (keys %keyword_hash) {
429 if ($patch_line =~ m/^[+-].*$keyword_hash{$line}/x) {
430 push(@keyword_tvi, $line);
431 }
432 }
Joe Perches4a7fdb52009-04-10 12:28:57 -0700433 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700434 }
Stephen Hemminger22dd5b02010-03-05 13:43:06 -0800435 close($patch);
436
Joe Perches4a7fdb52009-04-10 12:28:57 -0700437 if ($file_cnt == @files) {
Joe Perches7f29fd272009-06-16 15:34:04 -0700438 warn "$P: file '${file}' doesn't appear to be a patch. "
Joe Perches4a7fdb52009-04-10 12:28:57 -0700439 . "Add -f to options?\n";
440 }
441 @files = sort_and_uniq(@files);
Joe Perchescb7301c2009-04-07 20:40:12 -0700442 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700443}
444
Joe Perches03372db2010-03-05 13:43:00 -0800445@file_emails = uniq(@file_emails);
446
Joe Perches683c6f82010-10-26 14:22:55 -0700447my %email_hash_name;
448my %email_hash_address;
Joe Perchescb7301c2009-04-07 20:40:12 -0700449my @email_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700450my %hash_list_to;
Joe Perches290603c12009-06-16 15:33:58 -0700451my @list_to = ();
Joe Perchescb7301c2009-04-07 20:40:12 -0700452my @scm = ();
453my @web = ();
454my @subsystem = ();
455my @status = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700456my @interactive_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700457my $signature_pattern;
Joe Perchescb7301c2009-04-07 20:40:12 -0700458
Joe Perches6ef1c522010-10-26 14:22:56 -0700459my @maintainers = get_maintainers();
Joe Perchescb7301c2009-04-07 20:40:12 -0700460
Joe Perches6ef1c522010-10-26 14:22:56 -0700461if (@maintainers) {
462 @maintainers = merge_email(@maintainers);
463 output(@maintainers);
464}
Joe Perchescb7301c2009-04-07 20:40:12 -0700465
466if ($scm) {
Joe Perchesb7816552009-09-21 17:04:24 -0700467 @scm = uniq(@scm);
Joe Perchescb7301c2009-04-07 20:40:12 -0700468 output(@scm);
469}
Joe Perches683c6f82010-10-26 14:22:55 -0700470
Joe Perchescb7301c2009-04-07 20:40:12 -0700471if ($status) {
Joe Perchesb7816552009-09-21 17:04:24 -0700472 @status = uniq(@status);
Joe Perchescb7301c2009-04-07 20:40:12 -0700473 output(@status);
474}
475
476if ($subsystem) {
Joe Perchesb7816552009-09-21 17:04:24 -0700477 @subsystem = uniq(@subsystem);
Joe Perchescb7301c2009-04-07 20:40:12 -0700478 output(@subsystem);
479}
480
481if ($web) {
Joe Perchesb7816552009-09-21 17:04:24 -0700482 @web = uniq(@web);
Joe Perchescb7301c2009-04-07 20:40:12 -0700483 output(@web);
484}
485
486exit($exit);
487
Joe Perches6ef1c522010-10-26 14:22:56 -0700488sub get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -0700489 %email_hash_name = ();
490 %email_hash_address = ();
491 %commit_author_hash = ();
492 %commit_signer_hash = ();
493 @email_to = ();
494 %hash_list_to = ();
495 @list_to = ();
496 @scm = ();
497 @web = ();
498 @subsystem = ();
499 @status = ();
Joe Perches6ef1c522010-10-26 14:22:56 -0700500 @interactive_to = ();
Joe Perches683c6f82010-10-26 14:22:55 -0700501 if ($email_git_all_signature_types) {
502 $signature_pattern = "(.+?)[Bb][Yy]:";
503 } else {
504 $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
505 }
506
507 # Find responsible parties
508
Joe Perches6ef1c522010-10-26 14:22:56 -0700509 my %exact_pattern_match_hash;
510
Joe Perches683c6f82010-10-26 14:22:55 -0700511 foreach my $file (@files) {
512
513 my %hash;
Joe Perches683c6f82010-10-26 14:22:55 -0700514 my $tvi = find_first_section();
515 while ($tvi < @typevalue) {
516 my $start = find_starting_index($tvi);
517 my $end = find_ending_index($tvi);
518 my $exclude = 0;
519 my $i;
520
521 #Do not match excluded file patterns
522
523 for ($i = $start; $i < $end; $i++) {
524 my $line = $typevalue[$i];
525 if ($line =~ m/^(\C):\s*(.*)/) {
526 my $type = $1;
527 my $value = $2;
528 if ($type eq 'X') {
529 if (file_match_pattern($file, $value)) {
530 $exclude = 1;
531 last;
532 }
533 }
534 }
535 }
536
537 if (!$exclude) {
538 for ($i = $start; $i < $end; $i++) {
539 my $line = $typevalue[$i];
540 if ($line =~ m/^(\C):\s*(.*)/) {
541 my $type = $1;
542 my $value = $2;
543 if ($type eq 'F') {
544 if (file_match_pattern($file, $value)) {
545 my $value_pd = ($value =~ tr@/@@);
546 my $file_pd = ($file =~ tr@/@@);
547 $value_pd++ if (substr($value,-1,1) ne "/");
548 $value_pd = -1 if ($value =~ /^\.\*/);
Joe Perches6ef1c522010-10-26 14:22:56 -0700549 if ($value_pd >= $file_pd) {
550 $exact_pattern_match_hash{$file} = 1;
551 }
Joe Perches683c6f82010-10-26 14:22:55 -0700552 if ($pattern_depth == 0 ||
553 (($file_pd - $value_pd) < $pattern_depth)) {
554 $hash{$tvi} = $value_pd;
555 }
556 }
557 }
558 }
559 }
560 }
561 $tvi = $end + 1;
562 }
563
564 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
565 add_categories($line);
566 if ($sections) {
567 my $i;
568 my $start = find_starting_index($line);
569 my $end = find_ending_index($line);
570 for ($i = $start; $i < $end; $i++) {
571 my $line = $typevalue[$i];
572 if ($line =~ /^[FX]:/) { ##Restore file patterns
573 $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
574 $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
575 $line =~ s/\\\./\./g; ##Convert \. to .
576 $line =~ s/\.\*/\*/g; ##Convert .* to *
577 }
578 $line =~ s/^([A-Z]):/$1:\t/g;
579 print("$line\n");
580 }
581 print("\n");
582 }
583 }
Joe Perches683c6f82010-10-26 14:22:55 -0700584 }
585
586 if ($keywords) {
587 @keyword_tvi = sort_and_uniq(@keyword_tvi);
588 foreach my $line (@keyword_tvi) {
589 add_categories($line);
590 }
591 }
592
Joe Perches6ef1c522010-10-26 14:22:56 -0700593 @interactive_to = (@email_to, @list_to);
594
595 foreach my $file (@files) {
596 if ($email &&
597 ($email_git || ($email_git_fallback &&
598 !$exact_pattern_match_hash{$file}))) {
599 vcs_file_signoffs($file);
600 }
601 if ($email && $email_git_blame) {
602 vcs_file_blame($file);
603 }
604 }
605
Joe Perches683c6f82010-10-26 14:22:55 -0700606 if ($email) {
607 foreach my $chief (@penguin_chief) {
608 if ($chief =~ m/^(.*):(.*)/) {
609 my $email_address;
610
611 $email_address = format_email($1, $2, $email_usename);
612 if ($email_git_penguin_chiefs) {
613 push(@email_to, [$email_address, 'chief penguin']);
614 } else {
615 @email_to = grep($_->[0] !~ /${email_address}/, @email_to);
616 }
617 }
618 }
619
620 foreach my $email (@file_emails) {
621 my ($name, $address) = parse_email($email);
622
623 my $tmp_email = format_email($name, $address, $email_usename);
624 push_email_address($tmp_email, '');
625 add_role($tmp_email, 'in file');
626 }
627 }
628
629 my @to = ();
630 if ($email || $email_list) {
631 if ($email) {
632 @to = (@to, @email_to);
633 }
634 if ($email_list) {
635 @to = (@to, @list_to);
636 }
637 }
638
Joe Perches6ef1c522010-10-26 14:22:56 -0700639 if ($interactive) {
640 @interactive_to = @to;
641 @to = interactive_get_maintainers(\@interactive_to);
642 }
Joe Perches683c6f82010-10-26 14:22:55 -0700643
644 return @to;
645}
646
Joe Perchescb7301c2009-04-07 20:40:12 -0700647sub file_match_pattern {
648 my ($file, $pattern) = @_;
649 if (substr($pattern, -1) eq "/") {
650 if ($file =~ m@^$pattern@) {
651 return 1;
652 }
653 } else {
654 if ($file =~ m@^$pattern@) {
655 my $s1 = ($file =~ tr@/@@);
656 my $s2 = ($pattern =~ tr@/@@);
657 if ($s1 == $s2) {
658 return 1;
659 }
660 }
661 }
662 return 0;
663}
664
665sub usage {
666 print <<EOT;
667usage: $P [options] patchfile
Joe Perches870020f2009-07-29 15:04:28 -0700668 $P [options] -f file|directory
Joe Perchescb7301c2009-04-07 20:40:12 -0700669version: $V
670
671MAINTAINER field selection options:
672 --email => print email address(es) if any
673 --git => include recent git \*-by: signers
Joe Perchese4d26b02010-05-24 14:33:17 -0700674 --git-all-signature-types => include signers regardless of signature type
Joe Perches683c6f82010-10-26 14:22:55 -0700675 or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
Joe Perchese3e9d112010-10-26 14:22:53 -0700676 --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
Joe Perchescb7301c2009-04-07 20:40:12 -0700677 --git-chief-penguins => include ${penguin_chiefs}
Joe Perchese4d26b02010-05-24 14:33:17 -0700678 --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
679 --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
680 --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
Joe Perchesf5492662009-09-21 17:04:13 -0700681 --git-blame => use git blame to find modified commits for patch or file
Joe Perchese4d26b02010-05-24 14:33:17 -0700682 --git-since => git history to use (default: $email_git_since)
683 --hg-since => hg history to use (default: $email_hg_since)
Florian Micklerdace8e32010-10-26 14:22:54 -0700684 --interactive => display a menu (mostly useful if used with the --git option)
Joe Perchescb7301c2009-04-07 20:40:12 -0700685 --m => include maintainer(s) if any
686 --n => include name 'Full Name <addr\@domain.tld>'
687 --l => include list(s) if any
688 --s => include subscriber only list(s) if any
Joe Perches11ecf532009-09-21 17:04:22 -0700689 --remove-duplicates => minimize duplicate email names/addresses
Joe Perches3c7385b2009-12-14 18:00:46 -0800690 --roles => show roles (status:subsystem, git-signer, list, etc...)
691 --rolestats => show roles and statistics (commits/total_commits, %)
Joe Perches03372db2010-03-05 13:43:00 -0800692 --file-emails => add email addresses found in -f file (default: 0 (off))
Joe Perchescb7301c2009-04-07 20:40:12 -0700693 --scm => print SCM tree(s) if any
694 --status => print status if any
695 --subsystem => print subsystem name if any
696 --web => print website(s) if any
697
698Output type options:
699 --separator [, ] => separator for multiple entries on 1 line
Joe Perches42498312009-09-21 17:04:21 -0700700 using --separator also sets --nomultiline if --separator is not [, ]
Joe Perchescb7301c2009-04-07 20:40:12 -0700701 --multiline => print 1 entry per line
702
Joe Perchescb7301c2009-04-07 20:40:12 -0700703Other options:
Joe Perches3fb55652009-09-21 17:04:17 -0700704 --pattern-depth => Number of pattern directory traversals (default: 0 (all))
Joe Perchesdcf36a92009-10-26 16:49:47 -0700705 --keywords => scan patch for keywords (default: 1 (on))
Joe Perches4b76c9d2010-03-05 13:43:03 -0800706 --sections => print the entire subsystem sections with pattern matches
Joe Perchesf5f5078d2009-06-16 15:34:00 -0700707 --version => show version
Joe Perchescb7301c2009-04-07 20:40:12 -0700708 --help => show this help information
709
Joe Perches3fb55652009-09-21 17:04:17 -0700710Default options:
Joe Perches11ecf532009-09-21 17:04:22 -0700711 [--email --git --m --n --l --multiline --pattern-depth=0 --remove-duplicates]
Joe Perches3fb55652009-09-21 17:04:17 -0700712
Joe Perches870020f2009-07-29 15:04:28 -0700713Notes:
714 Using "-f directory" may give unexpected results:
Joe Perchesf5492662009-09-21 17:04:13 -0700715 Used with "--git", git signators for _all_ files in and below
716 directory are examined as git recurses directories.
717 Any specified X: (exclude) pattern matches are _not_ ignored.
718 Used with "--nogit", directory is used as a pattern match,
Joe Perches60db31a2009-12-14 18:00:50 -0800719 no individual file within the directory or subdirectory
720 is matched.
Joe Perchesf5492662009-09-21 17:04:13 -0700721 Used with "--git-blame", does not iterate all files in directory
722 Using "--git-blame" is slow and may add old committers and authors
723 that are no longer active maintainers to the output.
Joe Perches3c7385b2009-12-14 18:00:46 -0800724 Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
725 other automated tools that expect only ["name"] <email address>
726 may not work because of additional output after <email address>.
727 Using "--rolestats" and "--git-blame" shows the #/total=% commits,
728 not the percentage of the entire file authored. # of commits is
729 not a good measure of amount of code authored. 1 major commit may
730 contain a thousand lines, 5 trivial commits may modify a single line.
Joe Perches60db31a2009-12-14 18:00:50 -0800731 If git is not installed, but mercurial (hg) is installed and an .hg
732 repository exists, the following options apply to mercurial:
733 --git,
734 --git-min-signatures, --git-max-maintainers, --git-min-percent, and
735 --git-blame
736 Use --hg-since not --git-since to control date selection
Joe Perches368669d2010-05-24 14:33:19 -0700737 File ".get_maintainer.conf", if it exists in the linux kernel source root
738 directory, can change whatever get_maintainer defaults are desired.
739 Entries in this file can be any command line argument.
740 This file is prepended to any additional command line arguments.
741 Multiple lines and # comments are allowed.
Joe Perchescb7301c2009-04-07 20:40:12 -0700742EOT
743}
744
745sub top_of_kernel_tree {
Joe Perches47abc722010-10-26 14:22:57 -0700746 my ($lk_path) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -0700747
Joe Perches47abc722010-10-26 14:22:57 -0700748 if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
749 $lk_path .= "/";
750 }
751 if ( (-f "${lk_path}COPYING")
752 && (-f "${lk_path}CREDITS")
753 && (-f "${lk_path}Kbuild")
754 && (-f "${lk_path}MAINTAINERS")
755 && (-f "${lk_path}Makefile")
756 && (-f "${lk_path}README")
757 && (-d "${lk_path}Documentation")
758 && (-d "${lk_path}arch")
759 && (-d "${lk_path}include")
760 && (-d "${lk_path}drivers")
761 && (-d "${lk_path}fs")
762 && (-d "${lk_path}init")
763 && (-d "${lk_path}ipc")
764 && (-d "${lk_path}kernel")
765 && (-d "${lk_path}lib")
766 && (-d "${lk_path}scripts")) {
767 return 1;
768 }
769 return 0;
Joe Perchescb7301c2009-04-07 20:40:12 -0700770}
771
Joe Perches0e70e832009-09-21 17:04:20 -0700772sub parse_email {
773 my ($formatted_email) = @_;
774
775 my $name = "";
776 my $address = "";
777
Joe Perches11ecf532009-09-21 17:04:22 -0700778 if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700779 $name = $1;
780 $address = $2;
Joe Perches11ecf532009-09-21 17:04:22 -0700781 } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700782 $address = $1;
Joe Perchesb7816552009-09-21 17:04:24 -0700783 } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
Joe Perches0e70e832009-09-21 17:04:20 -0700784 $address = $1;
785 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700786
787 $name =~ s/^\s+|\s+$//g;
Joe Perchesd7895042009-06-16 15:34:02 -0700788 $name =~ s/^\"|\"$//g;
Joe Perches0e70e832009-09-21 17:04:20 -0700789 $address =~ s/^\s+|\s+$//g;
Joe Perchescb7301c2009-04-07 20:40:12 -0700790
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800791 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perchescb7301c2009-04-07 20:40:12 -0700792 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
Joe Perches0e70e832009-09-21 17:04:20 -0700793 $name = "\"$name\"";
Joe Perchescb7301c2009-04-07 20:40:12 -0700794 }
Joe Perches0e70e832009-09-21 17:04:20 -0700795
796 return ($name, $address);
797}
798
799sub format_email {
Joe Perchesa8af2432009-12-14 18:00:49 -0800800 my ($name, $address, $usename) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -0700801
802 my $formatted_email;
803
804 $name =~ s/^\s+|\s+$//g;
805 $name =~ s/^\"|\"$//g;
806 $address =~ s/^\s+|\s+$//g;
807
Stephen Hemmingera63ceb42010-03-05 13:43:06 -0800808 if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
Joe Perches0e70e832009-09-21 17:04:20 -0700809 $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
810 $name = "\"$name\"";
811 }
812
Joe Perchesa8af2432009-12-14 18:00:49 -0800813 if ($usename) {
Joe Perches0e70e832009-09-21 17:04:20 -0700814 if ("$name" eq "") {
815 $formatted_email = "$address";
816 } else {
Joe Perchesa8af2432009-12-14 18:00:49 -0800817 $formatted_email = "$name <$address>";
Joe Perches0e70e832009-09-21 17:04:20 -0700818 }
819 } else {
820 $formatted_email = $address;
821 }
822
Joe Perchescb7301c2009-04-07 20:40:12 -0700823 return $formatted_email;
824}
825
Joe Perches272a8972010-01-08 14:42:48 -0800826sub find_first_section {
827 my $index = 0;
828
829 while ($index < @typevalue) {
830 my $tv = $typevalue[$index];
831 if (($tv =~ m/^(\C):\s*(.*)/)) {
832 last;
833 }
834 $index++;
835 }
836
837 return $index;
838}
839
Joe Perchesb7816552009-09-21 17:04:24 -0700840sub find_starting_index {
Joe Perchesb7816552009-09-21 17:04:24 -0700841 my ($index) = @_;
842
843 while ($index > 0) {
844 my $tv = $typevalue[$index];
845 if (!($tv =~ m/^(\C):\s*(.*)/)) {
846 last;
847 }
848 $index--;
849 }
850
851 return $index;
852}
853
854sub find_ending_index {
855 my ($index) = @_;
856
857 while ($index < @typevalue) {
858 my $tv = $typevalue[$index];
859 if (!($tv =~ m/^(\C):\s*(.*)/)) {
860 last;
861 }
862 $index++;
863 }
864
865 return $index;
866}
867
Joe Perches3c7385b2009-12-14 18:00:46 -0800868sub get_maintainer_role {
869 my ($index) = @_;
870
871 my $i;
872 my $start = find_starting_index($index);
873 my $end = find_ending_index($index);
874
875 my $role;
876 my $subsystem = $typevalue[$start];
877 if (length($subsystem) > 20) {
878 $subsystem = substr($subsystem, 0, 17);
879 $subsystem =~ s/\s*$//;
880 $subsystem = $subsystem . "...";
881 }
882
883 for ($i = $start + 1; $i < $end; $i++) {
884 my $tv = $typevalue[$i];
885 if ($tv =~ m/^(\C):\s*(.*)/) {
886 my $ptype = $1;
887 my $pvalue = $2;
888 if ($ptype eq "S") {
889 $role = $pvalue;
890 }
891 }
892 }
893
894 $role = lc($role);
895 if ($role eq "supported") {
896 $role = "supporter";
897 } elsif ($role eq "maintained") {
898 $role = "maintainer";
899 } elsif ($role eq "odd fixes") {
900 $role = "odd fixer";
901 } elsif ($role eq "orphan") {
902 $role = "orphan minder";
903 } elsif ($role eq "obsolete") {
904 $role = "obsolete minder";
905 } elsif ($role eq "buried alive in reporters") {
906 $role = "chief penguin";
907 }
908
909 return $role . ":" . $subsystem;
910}
911
912sub get_list_role {
913 my ($index) = @_;
914
915 my $i;
916 my $start = find_starting_index($index);
917 my $end = find_ending_index($index);
918
919 my $subsystem = $typevalue[$start];
920 if (length($subsystem) > 20) {
921 $subsystem = substr($subsystem, 0, 17);
922 $subsystem =~ s/\s*$//;
923 $subsystem = $subsystem . "...";
924 }
925
926 if ($subsystem eq "THE REST") {
927 $subsystem = "";
928 }
929
930 return $subsystem;
931}
932
Joe Perchescb7301c2009-04-07 20:40:12 -0700933sub add_categories {
934 my ($index) = @_;
935
Joe Perchesb7816552009-09-21 17:04:24 -0700936 my $i;
937 my $start = find_starting_index($index);
938 my $end = find_ending_index($index);
939
940 push(@subsystem, $typevalue[$start]);
941
942 for ($i = $start + 1; $i < $end; $i++) {
943 my $tv = $typevalue[$i];
Joe Perches290603c12009-06-16 15:33:58 -0700944 if ($tv =~ m/^(\C):\s*(.*)/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700945 my $ptype = $1;
946 my $pvalue = $2;
947 if ($ptype eq "L") {
Joe Perches290603c12009-06-16 15:33:58 -0700948 my $list_address = $pvalue;
949 my $list_additional = "";
Joe Perches3c7385b2009-12-14 18:00:46 -0800950 my $list_role = get_list_role($i);
951
952 if ($list_role ne "") {
953 $list_role = ":" . $list_role;
954 }
Joe Perches290603c12009-06-16 15:33:58 -0700955 if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
956 $list_address = $1;
957 $list_additional = $2;
958 }
Joe Perchesbdf7c682009-06-16 15:33:59 -0700959 if ($list_additional =~ m/subscribers-only/) {
Joe Perchescb7301c2009-04-07 20:40:12 -0700960 if ($email_subscriber_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700961 if (!$hash_list_to{lc($list_address)}) {
962 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -0700963 push(@list_to, [$list_address,
964 "subscriber list${list_role}"]);
965 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700966 }
967 } else {
968 if ($email_list) {
Joe Perches6ef1c522010-10-26 14:22:56 -0700969 if (!$hash_list_to{lc($list_address)}) {
970 $hash_list_to{lc($list_address)} = 1;
Joe Perches683c6f82010-10-26 14:22:55 -0700971 push(@list_to, [$list_address,
972 "open list${list_role}"]);
973 }
Joe Perchescb7301c2009-04-07 20:40:12 -0700974 }
975 }
976 } elsif ($ptype eq "M") {
Joe Perches0e70e832009-09-21 17:04:20 -0700977 my ($name, $address) = parse_email($pvalue);
978 if ($name eq "") {
Joe Perchesb7816552009-09-21 17:04:24 -0700979 if ($i > 0) {
980 my $tv = $typevalue[$i - 1];
Joe Perches0e70e832009-09-21 17:04:20 -0700981 if ($tv =~ m/^(\C):\s*(.*)/) {
982 if ($1 eq "P") {
983 $name = $2;
Joe Perchesa8af2432009-12-14 18:00:49 -0800984 $pvalue = format_email($name, $address, $email_usename);
Joe Perches5f2441e2009-06-16 15:34:02 -0700985 }
986 }
987 }
988 }
Joe Perches0e70e832009-09-21 17:04:20 -0700989 if ($email_maintainer) {
Joe Perches3c7385b2009-12-14 18:00:46 -0800990 my $role = get_maintainer_role($i);
991 push_email_addresses($pvalue, $role);
Joe Perchescb7301c2009-04-07 20:40:12 -0700992 }
993 } elsif ($ptype eq "T") {
994 push(@scm, $pvalue);
995 } elsif ($ptype eq "W") {
996 push(@web, $pvalue);
997 } elsif ($ptype eq "S") {
998 push(@status, $pvalue);
999 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001000 }
1001 }
1002}
1003
Joe Perches11ecf532009-09-21 17:04:22 -07001004sub email_inuse {
1005 my ($name, $address) = @_;
Joe Perches0e70e832009-09-21 17:04:20 -07001006
Joe Perches11ecf532009-09-21 17:04:22 -07001007 return 1 if (($name eq "") && ($address eq ""));
Joe Perches6ef1c522010-10-26 14:22:56 -07001008 return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
1009 return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
Joe Perches11ecf532009-09-21 17:04:22 -07001010
Joe Perches0e70e832009-09-21 17:04:20 -07001011 return 0;
1012}
1013
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001014sub push_email_address {
Joe Perches3c7385b2009-12-14 18:00:46 -08001015 my ($line, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001016
Joe Perches0e70e832009-09-21 17:04:20 -07001017 my ($name, $address) = parse_email($line);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001018
Joe Perchesb7816552009-09-21 17:04:24 -07001019 if ($address eq "") {
1020 return 0;
1021 }
1022
Joe Perches11ecf532009-09-21 17:04:22 -07001023 if (!$email_remove_duplicates) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001024 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches11ecf532009-09-21 17:04:22 -07001025 } elsif (!email_inuse($name, $address)) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001026 push(@email_to, [format_email($name, $address, $email_usename), $role]);
Joe Perches6ef1c522010-10-26 14:22:56 -07001027 $email_hash_name{lc($name)}++;
1028 $email_hash_address{lc($address)}++;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001029 }
Joe Perchesb7816552009-09-21 17:04:24 -07001030
1031 return 1;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001032}
1033
1034sub push_email_addresses {
Joe Perches3c7385b2009-12-14 18:00:46 -08001035 my ($address, $role) = @_;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001036
1037 my @address_list = ();
1038
Joe Perches5f2441e2009-06-16 15:34:02 -07001039 if (rfc822_valid($address)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001040 push_email_address($address, $role);
Joe Perches5f2441e2009-06-16 15:34:02 -07001041 } elsif (@address_list = rfc822_validlist($address)) {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001042 my $array_count = shift(@address_list);
1043 while (my $entry = shift(@address_list)) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001044 push_email_address($entry, $role);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001045 }
Joe Perches5f2441e2009-06-16 15:34:02 -07001046 } else {
Joe Perches3c7385b2009-12-14 18:00:46 -08001047 if (!push_email_address($address, $role)) {
Joe Perchesb7816552009-09-21 17:04:24 -07001048 warn("Invalid MAINTAINERS address: '" . $address . "'\n");
1049 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001050 }
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001051}
1052
Joe Perches3c7385b2009-12-14 18:00:46 -08001053sub add_role {
1054 my ($line, $role) = @_;
1055
1056 my ($name, $address) = parse_email($line);
Joe Perchesa8af2432009-12-14 18:00:49 -08001057 my $email = format_email($name, $address, $email_usename);
Joe Perches3c7385b2009-12-14 18:00:46 -08001058
1059 foreach my $entry (@email_to) {
1060 if ($email_remove_duplicates) {
1061 my ($entry_name, $entry_address) = parse_email($entry->[0]);
Joe Perches03372db2010-03-05 13:43:00 -08001062 if (($name eq $entry_name || $address eq $entry_address)
1063 && ($role eq "" || !($entry->[1] =~ m/$role/))
1064 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001065 if ($entry->[1] eq "") {
1066 $entry->[1] = "$role";
1067 } else {
1068 $entry->[1] = "$entry->[1],$role";
1069 }
1070 }
1071 } else {
Joe Perches03372db2010-03-05 13:43:00 -08001072 if ($email eq $entry->[0]
1073 && ($role eq "" || !($entry->[1] =~ m/$role/))
1074 ) {
Joe Perches3c7385b2009-12-14 18:00:46 -08001075 if ($entry->[1] eq "") {
1076 $entry->[1] = "$role";
1077 } else {
1078 $entry->[1] = "$entry->[1],$role";
1079 }
1080 }
1081 }
1082 }
1083}
1084
Joe Perchescb7301c2009-04-07 20:40:12 -07001085sub which {
1086 my ($bin) = @_;
1087
Joe Perchesf5f5078d2009-06-16 15:34:00 -07001088 foreach my $path (split(/:/, $ENV{PATH})) {
Joe Perchescb7301c2009-04-07 20:40:12 -07001089 if (-e "$path/$bin") {
1090 return "$path/$bin";
1091 }
1092 }
1093
1094 return "";
1095}
1096
Joe Perchesbcde44e2010-10-26 14:22:53 -07001097sub which_conf {
1098 my ($conf) = @_;
1099
1100 foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
1101 if (-e "$path/$conf") {
1102 return "$path/$conf";
1103 }
1104 }
1105
1106 return "";
1107}
1108
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001109sub mailmap_email {
Joe Perches47abc722010-10-26 14:22:57 -07001110 my $line = shift;
Joe Perches8cbb3a72009-09-21 17:04:21 -07001111
Joe Perches47abc722010-10-26 14:22:57 -07001112 my ($name, $address) = parse_email($line);
1113 my $email = format_email($name, $address, 1);
1114 my $real_name = $name;
1115 my $real_address = $address;
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001116
Joe Perches47abc722010-10-26 14:22:57 -07001117 if (exists $mailmap->{names}->{$email} ||
1118 exists $mailmap->{addresses}->{$email}) {
1119 if (exists $mailmap->{names}->{$email}) {
1120 $real_name = $mailmap->{names}->{$email};
Joe Perches8cbb3a72009-09-21 17:04:21 -07001121 }
Joe Perches47abc722010-10-26 14:22:57 -07001122 if (exists $mailmap->{addresses}->{$email}) {
1123 $real_address = $mailmap->{addresses}->{$email};
1124 }
1125 } else {
1126 if (exists $mailmap->{names}->{$address}) {
1127 $real_name = $mailmap->{names}->{$address};
1128 }
1129 if (exists $mailmap->{addresses}->{$address}) {
1130 $real_address = $mailmap->{addresses}->{$address};
1131 }
1132 }
1133 return format_email($real_name, $real_address, 1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001134}
1135
1136sub mailmap {
1137 my (@addresses) = @_;
1138
1139 my @ret = ();
1140 foreach my $line (@addresses) {
1141 push(@ret, mailmap_email($line), 1);
Joe Perches8cbb3a72009-09-21 17:04:21 -07001142 }
1143
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001144 merge_by_realname(@ret) if $email_remove_duplicates;
1145
1146 return @ret;
1147}
1148
1149sub merge_by_realname {
Joe Perches47abc722010-10-26 14:22:57 -07001150 my %address_map;
1151 my (@emails) = @_;
1152 foreach my $email (@emails) {
1153 my ($name, $address) = parse_email($email);
1154 if (!exists $address_map{$name}) {
1155 $address_map{$name} = $address;
1156 } else {
1157 $address = $address_map{$name};
1158 $email = format_email($name,$address,1);
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001159 }
Joe Perches47abc722010-10-26 14:22:57 -07001160 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001161}
1162
Joe Perches60db31a2009-12-14 18:00:50 -08001163sub git_execute_cmd {
1164 my ($cmd) = @_;
1165 my @lines = ();
Joe Perchescb7301c2009-04-07 20:40:12 -07001166
Joe Perches60db31a2009-12-14 18:00:50 -08001167 my $output = `$cmd`;
1168 $output =~ s/^\s*//gm;
1169 @lines = split("\n", $output);
1170
1171 return @lines;
Joe Perchesa8af2432009-12-14 18:00:49 -08001172}
1173
Joe Perches60db31a2009-12-14 18:00:50 -08001174sub hg_execute_cmd {
Joe Perchesa8af2432009-12-14 18:00:49 -08001175 my ($cmd) = @_;
Joe Perches60db31a2009-12-14 18:00:50 -08001176 my @lines = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001177
Joe Perches60db31a2009-12-14 18:00:50 -08001178 my $output = `$cmd`;
1179 @lines = split("\n", $output);
1180
1181 return @lines;
1182}
1183
Joe Perches683c6f82010-10-26 14:22:55 -07001184sub extract_formatted_signatures {
1185 my (@signature_lines) = @_;
1186
1187 my @type = @signature_lines;
1188
1189 s/\s*(.*):.*/$1/ for (@type);
1190
1191 # cut -f2- -d":"
1192 s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
1193
1194## Reformat email addresses (with names) to avoid badly written signatures
1195
1196 foreach my $signer (@signature_lines) {
1197 my ($name, $address) = parse_email($signer);
1198 $signer = format_email($name, $address, 1);
1199 }
1200
1201 return (\@type, \@signature_lines);
1202}
1203
Joe Perches60db31a2009-12-14 18:00:50 -08001204sub vcs_find_signers {
1205 my ($cmd) = @_;
Joe Perchesa8af2432009-12-14 18:00:49 -08001206 my $commits;
Joe Perches683c6f82010-10-26 14:22:55 -07001207 my @lines = ();
1208 my @signatures = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001209
Joe Perches60db31a2009-12-14 18:00:50 -08001210 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
Joe Perchescb7301c2009-04-07 20:40:12 -07001211
Joe Perches60db31a2009-12-14 18:00:50 -08001212 my $pattern = $VCS_cmds{"commit_pattern"};
Joe Perchescb7301c2009-04-07 20:40:12 -07001213
Joe Perches60db31a2009-12-14 18:00:50 -08001214 $commits = grep(/$pattern/, @lines); # of commits
Joe Perchesafa81ee2009-07-29 15:04:28 -07001215
Joe Perches683c6f82010-10-26 14:22:55 -07001216 @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
1217
1218 return (0, @signatures) if !@signatures;
1219
1220 save_commits_by_author(@lines) if ($interactive);
1221 save_commits_by_signer(@lines) if ($interactive);
1222
Joe Perches0e70e832009-09-21 17:04:20 -07001223 if (!$email_git_penguin_chiefs) {
Joe Perches683c6f82010-10-26 14:22:55 -07001224 @signatures = grep(!/${penguin_chiefs}/i, @signatures);
Joe Perches0e70e832009-09-21 17:04:20 -07001225 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001226
Joe Perches683c6f82010-10-26 14:22:55 -07001227 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
Joe Perches63ab52d2010-10-26 14:22:51 -07001228
Joe Perches683c6f82010-10-26 14:22:55 -07001229 return ($commits, @$signers_ref);
Joe Perchesa8af2432009-12-14 18:00:49 -08001230}
1231
Joe Perches63ab52d2010-10-26 14:22:51 -07001232sub vcs_find_author {
1233 my ($cmd) = @_;
1234 my @lines = ();
1235
1236 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1237
1238 if (!$email_git_penguin_chiefs) {
1239 @lines = grep(!/${penguin_chiefs}/i, @lines);
1240 }
1241
1242 return @lines if !@lines;
1243
Joe Perches683c6f82010-10-26 14:22:55 -07001244 my @authors = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001245 foreach my $line (@lines) {
Joe Perches683c6f82010-10-26 14:22:55 -07001246 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1247 my $author = $1;
1248 my ($name, $address) = parse_email($author);
1249 $author = format_email($name, $address, 1);
1250 push(@authors, $author);
1251 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001252 }
1253
Joe Perches683c6f82010-10-26 14:22:55 -07001254 save_commits_by_author(@lines) if ($interactive);
1255 save_commits_by_signer(@lines) if ($interactive);
1256
1257 return @authors;
Joe Perches63ab52d2010-10-26 14:22:51 -07001258}
1259
Joe Perches60db31a2009-12-14 18:00:50 -08001260sub vcs_save_commits {
1261 my ($cmd) = @_;
1262 my @lines = ();
1263 my @commits = ();
1264
1265 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1266
1267 foreach my $line (@lines) {
1268 if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
1269 push(@commits, $1);
1270 }
1271 }
1272
1273 return @commits;
1274}
1275
1276sub vcs_blame {
1277 my ($file) = @_;
1278 my $cmd;
1279 my @commits = ();
1280
1281 return @commits if (!(-f $file));
1282
1283 if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
1284 my @all_commits = ();
1285
1286 $cmd = $VCS_cmds{"blame_file_cmd"};
1287 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1288 @all_commits = vcs_save_commits($cmd);
1289
1290 foreach my $file_range_diff (@range) {
1291 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1292 my $diff_file = $1;
1293 my $diff_start = $2;
1294 my $diff_length = $3;
1295 next if ("$file" ne "$diff_file");
1296 for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
1297 push(@commits, $all_commits[$i]);
1298 }
1299 }
1300 } elsif (@range) {
1301 foreach my $file_range_diff (@range) {
1302 next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
1303 my $diff_file = $1;
1304 my $diff_start = $2;
1305 my $diff_length = $3;
1306 next if ("$file" ne "$diff_file");
1307 $cmd = $VCS_cmds{"blame_range_cmd"};
1308 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1309 push(@commits, vcs_save_commits($cmd));
1310 }
1311 } else {
1312 $cmd = $VCS_cmds{"blame_file_cmd"};
1313 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1314 @commits = vcs_save_commits($cmd);
1315 }
1316
Joe Perches63ab52d2010-10-26 14:22:51 -07001317 foreach my $commit (@commits) {
1318 $commit =~ s/^\^//g;
1319 }
1320
Joe Perches60db31a2009-12-14 18:00:50 -08001321 return @commits;
1322}
1323
1324my $printed_novcs = 0;
1325sub vcs_exists {
1326 %VCS_cmds = %VCS_cmds_git;
1327 return 1 if eval $VCS_cmds{"available"};
1328 %VCS_cmds = %VCS_cmds_hg;
Joe Perches683c6f82010-10-26 14:22:55 -07001329 return 2 if eval $VCS_cmds{"available"};
Joe Perches60db31a2009-12-14 18:00:50 -08001330 %VCS_cmds = ();
1331 if (!$printed_novcs) {
1332 warn("$P: No supported VCS found. Add --nogit to options?\n");
1333 warn("Using a git repository produces better results.\n");
1334 warn("Try Linus Torvalds' latest git repository using:\n");
1335 warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git\n");
1336 $printed_novcs = 1;
1337 }
1338 return 0;
1339}
1340
Joe Perches683c6f82010-10-26 14:22:55 -07001341sub vcs_is_git {
1342 return $vcs_used == 1;
1343}
1344
1345sub vcs_is_hg {
1346 return $vcs_used == 2;
1347}
1348
Joe Perches6ef1c522010-10-26 14:22:56 -07001349sub interactive_get_maintainers {
Joe Perches683c6f82010-10-26 14:22:55 -07001350 my ($list_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001351 my @list = @$list_ref;
1352
Joe Perches683c6f82010-10-26 14:22:55 -07001353 vcs_exists();
Florian Micklerdace8e32010-10-26 14:22:54 -07001354
1355 my %selected;
Joe Perches683c6f82010-10-26 14:22:55 -07001356 my %authored;
1357 my %signed;
Florian Micklerdace8e32010-10-26 14:22:54 -07001358 my $count = 0;
Joe Perches6ef1c522010-10-26 14:22:56 -07001359 my $maintained = 0;
Florian Micklerdace8e32010-10-26 14:22:54 -07001360 #select maintainers by default
Joe Perches6ef1c522010-10-26 14:22:56 -07001361 foreach my $entry (@list) {
Joe Perches683c6f82010-10-26 14:22:55 -07001362 my $role = $entry->[1];
Joe Perches6ef1c522010-10-26 14:22:56 -07001363 $selected{$count} = ($role =~ /^(maintainer|supporter|open list)/i);
1364 $maintained = 1 if ($role =~ /^(maintainer|supporter)/i);
Joe Perches683c6f82010-10-26 14:22:55 -07001365 $authored{$count} = 0;
1366 $signed{$count} = 0;
1367 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001368 }
1369
1370 #menu loop
Joe Perches683c6f82010-10-26 14:22:55 -07001371 my $done = 0;
1372 my $print_options = 0;
1373 my $redraw = 1;
1374 while (!$done) {
1375 $count = 0;
1376 if ($redraw) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001377 printf STDERR "\n%1s %2s %-65s",
1378 "*", "#", "email/list and role:stats";
1379 if ($email_git ||
1380 ($email_git_fallback && !$maintained) ||
1381 $email_git_blame) {
1382 print STDERR "auth sign";
1383 }
1384 print STDERR "\n";
Joe Perches683c6f82010-10-26 14:22:55 -07001385 foreach my $entry (@list) {
1386 my $email = $entry->[0];
1387 my $role = $entry->[1];
1388 my $sel = "";
1389 $sel = "*" if ($selected{$count});
1390 my $commit_author = $commit_author_hash{$email};
1391 my $commit_signer = $commit_signer_hash{$email};
1392 my $authored = 0;
1393 my $signed = 0;
1394 $authored++ for (@{$commit_author});
1395 $signed++ for (@{$commit_signer});
1396 printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
1397 printf STDERR "%4d %4d", $authored, $signed
1398 if ($authored > 0 || $signed > 0);
1399 printf STDERR "\n %s\n", $role;
1400 if ($authored{$count}) {
1401 my $commit_author = $commit_author_hash{$email};
1402 foreach my $ref (@{$commit_author}) {
1403 print STDERR " Author: @{$ref}[1]\n";
Florian Micklerdace8e32010-10-26 14:22:54 -07001404 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001405 }
Joe Perches683c6f82010-10-26 14:22:55 -07001406 if ($signed{$count}) {
1407 my $commit_signer = $commit_signer_hash{$email};
1408 foreach my $ref (@{$commit_signer}) {
1409 print STDERR " @{$ref}[2]: @{$ref}[1]\n";
1410 }
1411 }
1412
1413 $count++;
Florian Micklerdace8e32010-10-26 14:22:54 -07001414 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001415 }
Joe Perches683c6f82010-10-26 14:22:55 -07001416 my $date_ref = \$email_git_since;
1417 $date_ref = \$email_hg_since if (vcs_is_hg());
1418 if ($print_options) {
1419 $print_options = 0;
1420 if (vcs_exists()) {
1421 print STDERR
1422"\nVersion Control options:\n" .
1423"g use git history [$email_git]\n" .
1424"gf use git-fallback [$email_git_fallback]\n" .
1425"b use git blame [$email_git_blame]\n" .
1426"bs use blame signatures [$email_git_blame_signatures]\n" .
1427"c# minimum commits [$email_git_min_signatures]\n" .
1428"%# min percent [$email_git_min_percent]\n" .
1429"d# history to use [$$date_ref]\n" .
1430"x# max maintainers [$email_git_max_maintainers]\n" .
1431"t all signature types [$email_git_all_signature_types]\n";
1432 }
1433 print STDERR "\nAdditional options:\n" .
1434"0 toggle all\n" .
1435"f emails in file [$file_emails]\n" .
1436"k keywords in file [$keywords]\n" .
1437"r remove duplicates [$email_remove_duplicates]\n" .
1438"p# pattern match depth [$pattern_depth]\n";
1439 }
1440 print STDERR
1441"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
1442
1443 my $input = <STDIN>;
Florian Micklerdace8e32010-10-26 14:22:54 -07001444 chomp($input);
1445
Joe Perches683c6f82010-10-26 14:22:55 -07001446 $redraw = 1;
1447 my $rerun = 0;
1448 my @wish = split(/[, ]+/, $input);
1449 foreach my $nr (@wish) {
1450 $nr = lc($nr);
1451 my $sel = substr($nr, 0, 1);
1452 my $str = substr($nr, 1);
1453 my $val = 0;
1454 $val = $1 if $str =~ /^(\d+)$/;
1455
1456 if ($sel eq "y") {
1457 $interactive = 0;
1458 $done = 1;
1459 $output_rolestats = 0;
1460 $output_roles = 0;
1461 last;
1462 } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
1463 $selected{$nr - 1} = !$selected{$nr - 1};
1464 } elsif ($sel eq "*" || $sel eq '^') {
1465 my $toggle = 0;
1466 $toggle = 1 if ($sel eq '*');
1467 for (my $i = 0; $i < $count; $i++) {
1468 $selected{$i} = $toggle;
Florian Micklerdace8e32010-10-26 14:22:54 -07001469 }
Joe Perches683c6f82010-10-26 14:22:55 -07001470 } elsif ($sel eq "0") {
1471 for (my $i = 0; $i < $count; $i++) {
1472 $selected{$i} = !$selected{$i};
1473 }
1474 } elsif ($sel eq "a") {
1475 if ($val > 0 && $val <= $count) {
1476 $authored{$val - 1} = !$authored{$val - 1};
1477 } elsif ($str eq '*' || $str eq '^') {
1478 my $toggle = 0;
1479 $toggle = 1 if ($str eq '*');
1480 for (my $i = 0; $i < $count; $i++) {
1481 $authored{$i} = $toggle;
1482 }
1483 }
1484 } elsif ($sel eq "s") {
1485 if ($val > 0 && $val <= $count) {
1486 $signed{$val - 1} = !$signed{$val - 1};
1487 } elsif ($str eq '*' || $str eq '^') {
1488 my $toggle = 0;
1489 $toggle = 1 if ($str eq '*');
1490 for (my $i = 0; $i < $count; $i++) {
1491 $signed{$i} = $toggle;
1492 }
1493 }
1494 } elsif ($sel eq "o") {
1495 $print_options = 1;
1496 $redraw = 1;
1497 } elsif ($sel eq "g") {
1498 if ($str eq "f") {
1499 bool_invert(\$email_git_fallback);
Florian Micklerdace8e32010-10-26 14:22:54 -07001500 } else {
Joe Perches683c6f82010-10-26 14:22:55 -07001501 bool_invert(\$email_git);
Florian Micklerdace8e32010-10-26 14:22:54 -07001502 }
Joe Perches683c6f82010-10-26 14:22:55 -07001503 $rerun = 1;
1504 } elsif ($sel eq "b") {
1505 if ($str eq "s") {
1506 bool_invert(\$email_git_blame_signatures);
1507 } else {
1508 bool_invert(\$email_git_blame);
1509 }
1510 $rerun = 1;
1511 } elsif ($sel eq "c") {
1512 if ($val > 0) {
1513 $email_git_min_signatures = $val;
1514 $rerun = 1;
1515 }
1516 } elsif ($sel eq "x") {
1517 if ($val > 0) {
1518 $email_git_max_maintainers = $val;
1519 $rerun = 1;
1520 }
1521 } elsif ($sel eq "%") {
1522 if ($str ne "" && $val >= 0) {
1523 $email_git_min_percent = $val;
1524 $rerun = 1;
1525 }
1526 } elsif ($sel eq "d") {
1527 if (vcs_is_git()) {
1528 $email_git_since = $str;
1529 } elsif (vcs_is_hg()) {
1530 $email_hg_since = $str;
1531 }
1532 $rerun = 1;
1533 } elsif ($sel eq "t") {
1534 bool_invert(\$email_git_all_signature_types);
1535 $rerun = 1;
1536 } elsif ($sel eq "f") {
1537 bool_invert(\$file_emails);
1538 $rerun = 1;
1539 } elsif ($sel eq "r") {
1540 bool_invert(\$email_remove_duplicates);
1541 $rerun = 1;
1542 } elsif ($sel eq "k") {
1543 bool_invert(\$keywords);
1544 $rerun = 1;
1545 } elsif ($sel eq "p") {
1546 if ($str ne "" && $val >= 0) {
1547 $pattern_depth = $val;
1548 $rerun = 1;
1549 }
Joe Perches6ef1c522010-10-26 14:22:56 -07001550 } elsif ($sel eq "h" || $sel eq "?") {
1551 print STDERR <<EOT
1552
1553Interactive mode allows you to select the various maintainers, submitters,
1554commit signers and mailing lists that could be CC'd on a patch.
1555
1556Any *'d entry is selected.
1557
Joe Perches47abc722010-10-26 14:22:57 -07001558If you have git or hg installed, you can choose to summarize the commit
Joe Perches6ef1c522010-10-26 14:22:56 -07001559history of files in the patch. Also, each line of the current file can
1560be matched to its commit author and that commits signers with blame.
1561
1562Various knobs exist to control the length of time for active commit
1563tracking, the maximum number of commit authors and signers to add,
1564and such.
1565
1566Enter selections at the prompt until you are satisfied that the selected
1567maintainers are appropriate. You may enter multiple selections separated
1568by either commas or spaces.
1569
1570EOT
Joe Perches683c6f82010-10-26 14:22:55 -07001571 } else {
1572 print STDERR "invalid option: '$nr'\n";
1573 $redraw = 0;
1574 }
1575 }
1576 if ($rerun) {
1577 print STDERR "git-blame can be very slow, please have patience..."
1578 if ($email_git_blame);
Joe Perches6ef1c522010-10-26 14:22:56 -07001579 goto &get_maintainers;
Joe Perches683c6f82010-10-26 14:22:55 -07001580 }
1581 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001582
1583 #drop not selected entries
1584 $count = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001585 my @new_emailto = ();
1586 foreach my $entry (@list) {
1587 if ($selected{$count}) {
1588 push(@new_emailto, $list[$count]);
Florian Micklerdace8e32010-10-26 14:22:54 -07001589 }
1590 $count++;
1591 }
Joe Perches683c6f82010-10-26 14:22:55 -07001592 return @new_emailto;
Florian Micklerdace8e32010-10-26 14:22:54 -07001593}
1594
Joe Perches683c6f82010-10-26 14:22:55 -07001595sub bool_invert {
1596 my ($bool_ref) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001597
Joe Perches683c6f82010-10-26 14:22:55 -07001598 if ($$bool_ref) {
1599 $$bool_ref = 0;
1600 } else {
1601 $$bool_ref = 1;
Florian Micklerdace8e32010-10-26 14:22:54 -07001602 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001603}
1604
Joe Perches683c6f82010-10-26 14:22:55 -07001605sub save_commits_by_author {
1606 my (@lines) = @_;
Florian Micklerdace8e32010-10-26 14:22:54 -07001607
Joe Perches683c6f82010-10-26 14:22:55 -07001608 my @authors = ();
1609 my @commits = ();
1610 my @subjects = ();
1611
1612 foreach my $line (@lines) {
1613 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
Joe Perches6ef1c522010-10-26 14:22:56 -07001614 my $matched = 0;
Joe Perches683c6f82010-10-26 14:22:55 -07001615 my $author = $1;
1616 my ($name, $address) = parse_email($author);
Joe Perches6ef1c522010-10-26 14:22:56 -07001617 foreach my $to (@interactive_to) {
1618 my ($to_name, $to_address) = parse_email($to->[0]);
1619 if ($email_remove_duplicates &&
1620 ((lc($name) eq lc($to_name)) ||
1621 (lc($address) eq lc($to_address)))) {
1622 $author = $to->[0];
1623 $matched = 1;
1624 last;
1625 }
1626 }
1627 $author = format_email($name, $address, 1) if (!$matched);
Joe Perches683c6f82010-10-26 14:22:55 -07001628 push(@authors, $author);
1629 }
1630 push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1631 push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1632 }
1633
1634 for (my $i = 0; $i < @authors; $i++) {
1635 my $exists = 0;
1636 foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
1637 if (@{$ref}[0] eq $commits[$i] &&
1638 @{$ref}[1] eq $subjects[$i]) {
1639 $exists = 1;
1640 last;
1641 }
1642 }
1643 if (!$exists) {
1644 push(@{$commit_author_hash{$authors[$i]}},
1645 [ ($commits[$i], $subjects[$i]) ]);
1646 }
1647 }
1648}
1649
1650sub save_commits_by_signer {
1651 my (@lines) = @_;
1652
1653 my $commit = "";
1654 my $subject = "";
1655
1656 foreach my $line (@lines) {
1657 $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
1658 $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
1659 if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
1660 my @signatures = ($line);
1661 my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
1662 my @types = @$types_ref;
1663 my @signers = @$signers_ref;
1664
1665 my $type = $types[0];
1666 my $signer = $signers[0];
1667
Joe Perches6ef1c522010-10-26 14:22:56 -07001668 my $matched = 0;
1669 my ($name, $address) = parse_email($signer);
1670 foreach my $to (@interactive_to) {
1671 my ($to_name, $to_address) = parse_email($to->[0]);
1672 if ($email_remove_duplicates &&
1673 ((lc($name) eq lc($to_name)) ||
1674 (lc($address) eq lc($to_address)))) {
1675 $signer = $to->[0];
1676 $matched = 1;
1677 last;
1678 }
1679 $signer = format_email($name, $address, 1) if (!$matched);
1680 }
1681
Joe Perches683c6f82010-10-26 14:22:55 -07001682 my $exists = 0;
1683 foreach my $ref(@{$commit_signer_hash{$signer}}) {
1684 if (@{$ref}[0] eq $commit &&
1685 @{$ref}[1] eq $subject &&
1686 @{$ref}[2] eq $type) {
1687 $exists = 1;
1688 last;
1689 }
1690 }
1691 if (!$exists) {
1692 push(@{$commit_signer_hash{$signer}},
1693 [ ($commit, $subject, $type) ]);
1694 }
1695 }
1696 }
Florian Micklerdace8e32010-10-26 14:22:54 -07001697}
1698
Joe Perches60db31a2009-12-14 18:00:50 -08001699sub vcs_assign {
Joe Perchesa8af2432009-12-14 18:00:49 -08001700 my ($role, $divisor, @lines) = @_;
1701
1702 my %hash;
1703 my $count = 0;
1704
Joe Perchesa8af2432009-12-14 18:00:49 -08001705 return if (@lines <= 0);
1706
1707 if ($divisor <= 0) {
Joe Perches60db31a2009-12-14 18:00:50 -08001708 warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
Joe Perchesa8af2432009-12-14 18:00:49 -08001709 $divisor = 1;
Joe Perches3c7385b2009-12-14 18:00:46 -08001710 }
Joe Perches8cbb3a72009-09-21 17:04:21 -07001711
Florian Mickler7fa8ff22010-10-26 14:22:56 -07001712 @lines = mailmap(@lines);
Joe Perches0e70e832009-09-21 17:04:20 -07001713
Joe Perches63ab52d2010-10-26 14:22:51 -07001714 return if (@lines <= 0);
1715
Joe Perches0e70e832009-09-21 17:04:20 -07001716 @lines = sort(@lines);
Joe Perchesafa81ee2009-07-29 15:04:28 -07001717
Joe Perches11ecf532009-09-21 17:04:22 -07001718 # uniq -c
1719 $hash{$_}++ for @lines;
1720
1721 # sort -rn
1722 foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
1723 my $sign_offs = $hash{$line};
Joe Perchesa8af2432009-12-14 18:00:49 -08001724 my $percent = $sign_offs * 100 / $divisor;
Joe Perches3c7385b2009-12-14 18:00:46 -08001725
Joe Perchesa8af2432009-12-14 18:00:49 -08001726 $percent = 100 if ($percent > 100);
Joe Perches11ecf532009-09-21 17:04:22 -07001727 $count++;
1728 last if ($sign_offs < $email_git_min_signatures ||
1729 $count > $email_git_max_maintainers ||
Joe Perchesa8af2432009-12-14 18:00:49 -08001730 $percent < $email_git_min_percent);
Joe Perches3c7385b2009-12-14 18:00:46 -08001731 push_email_address($line, '');
Joe Perches3c7385b2009-12-14 18:00:46 -08001732 if ($output_rolestats) {
Joe Perchesa8af2432009-12-14 18:00:49 -08001733 my $fmt_percent = sprintf("%.0f", $percent);
1734 add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
1735 } else {
1736 add_role($line, $role);
Joe Perches3c7385b2009-12-14 18:00:46 -08001737 }
Joe Perchesf5492662009-09-21 17:04:13 -07001738 }
1739}
1740
Joe Perches60db31a2009-12-14 18:00:50 -08001741sub vcs_file_signoffs {
Joe Perchesa8af2432009-12-14 18:00:49 -08001742 my ($file) = @_;
1743
1744 my @signers = ();
Joe Perches60db31a2009-12-14 18:00:50 -08001745 my $commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001746
Joe Perches683c6f82010-10-26 14:22:55 -07001747 $vcs_used = vcs_exists();
1748 return if (!$vcs_used);
Joe Perchesa8af2432009-12-14 18:00:49 -08001749
Joe Perches60db31a2009-12-14 18:00:50 -08001750 my $cmd = $VCS_cmds{"find_signers_cmd"};
1751 $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
1752
1753 ($commits, @signers) = vcs_find_signers($cmd);
1754 vcs_assign("commit_signer", $commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001755}
1756
Joe Perches60db31a2009-12-14 18:00:50 -08001757sub vcs_file_blame {
Joe Perchesf5492662009-09-21 17:04:13 -07001758 my ($file) = @_;
1759
Joe Perches60db31a2009-12-14 18:00:50 -08001760 my @signers = ();
Joe Perches63ab52d2010-10-26 14:22:51 -07001761 my @all_commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001762 my @commits = ();
Joe Perchesa8af2432009-12-14 18:00:49 -08001763 my $total_commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001764 my $total_lines;
Joe Perchesf5492662009-09-21 17:04:13 -07001765
Joe Perches683c6f82010-10-26 14:22:55 -07001766 $vcs_used = vcs_exists();
1767 return if (!$vcs_used);
Joe Perchesf5492662009-09-21 17:04:13 -07001768
Joe Perches63ab52d2010-10-26 14:22:51 -07001769 @all_commits = vcs_blame($file);
1770 @commits = uniq(@all_commits);
Joe Perchesa8af2432009-12-14 18:00:49 -08001771 $total_commits = @commits;
Joe Perches63ab52d2010-10-26 14:22:51 -07001772 $total_lines = @all_commits;
Joe Perchesa8af2432009-12-14 18:00:49 -08001773
Joe Perches683c6f82010-10-26 14:22:55 -07001774 if ($email_git_blame_signatures) {
1775 if (vcs_is_hg()) {
1776 my $commit_count;
1777 my @commit_signers = ();
1778 my $commit = join(" -r ", @commits);
1779 my $cmd;
Joe Perchesf5492662009-09-21 17:04:13 -07001780
Joe Perches683c6f82010-10-26 14:22:55 -07001781 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1782 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
Joe Perches60db31a2009-12-14 18:00:50 -08001783
Joe Perches683c6f82010-10-26 14:22:55 -07001784 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
Joe Perches63ab52d2010-10-26 14:22:51 -07001785
Joe Perches683c6f82010-10-26 14:22:55 -07001786 push(@signers, @commit_signers);
1787 } else {
1788 foreach my $commit (@commits) {
1789 my $commit_count;
1790 my @commit_signers = ();
1791 my $cmd;
1792
1793 $cmd = $VCS_cmds{"find_commit_signers_cmd"};
1794 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1795
1796 ($commit_count, @commit_signers) = vcs_find_signers($cmd);
1797
1798 push(@signers, @commit_signers);
1799 }
1800 }
Joe Perchesf5492662009-09-21 17:04:13 -07001801 }
1802
Joe Perchesa8af2432009-12-14 18:00:49 -08001803 if ($from_filename) {
Joe Perches63ab52d2010-10-26 14:22:51 -07001804 if ($output_rolestats) {
1805 my @blame_signers;
Joe Perches683c6f82010-10-26 14:22:55 -07001806 if (vcs_is_hg()) {{ # Double brace for last exit
1807 my $commit_count;
1808 my @commit_signers = ();
1809 @commits = uniq(@commits);
1810 @commits = sort(@commits);
1811 my $commit = join(" -r ", @commits);
1812 my $cmd;
1813
1814 $cmd = $VCS_cmds{"find_commit_author_cmd"};
1815 $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
1816
1817 my @lines = ();
1818
1819 @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
1820
1821 if (!$email_git_penguin_chiefs) {
1822 @lines = grep(!/${penguin_chiefs}/i, @lines);
1823 }
1824
1825 last if !@lines;
1826
1827 my @authors = ();
1828 foreach my $line (@lines) {
1829 if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
1830 my $author = $1;
1831 my ($name, $address) = parse_email($author);
1832 $author = format_email($name, $address, 1);
1833 push(@authors, $1);
1834 }
1835 }
1836
1837 save_commits_by_author(@lines) if ($interactive);
1838 save_commits_by_signer(@lines) if ($interactive);
1839
1840 push(@signers, @authors);
1841 }}
1842 else {
1843 foreach my $commit (@commits) {
1844 my $i;
1845 my $cmd = $VCS_cmds{"find_commit_author_cmd"};
1846 $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
1847 my @author = vcs_find_author($cmd);
1848 next if !@author;
1849 my $count = grep(/$commit/, @all_commits);
1850 for ($i = 0; $i < $count ; $i++) {
1851 push(@blame_signers, $author[0]);
1852 }
Joe Perches63ab52d2010-10-26 14:22:51 -07001853 }
1854 }
1855 if (@blame_signers) {
1856 vcs_assign("authored lines", $total_lines, @blame_signers);
1857 }
1858 }
Joe Perches60db31a2009-12-14 18:00:50 -08001859 vcs_assign("commits", $total_commits, @signers);
Joe Perchesa8af2432009-12-14 18:00:49 -08001860 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08001861 vcs_assign("modified commits", $total_commits, @signers);
Joe Perchesf5492662009-09-21 17:04:13 -07001862 }
Joe Perchescb7301c2009-04-07 20:40:12 -07001863}
1864
1865sub uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001866 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001867
1868 my %saw;
1869 @parms = grep(!$saw{$_}++, @parms);
1870 return @parms;
1871}
1872
1873sub sort_and_uniq {
Joe Perchesa8af2432009-12-14 18:00:49 -08001874 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001875
1876 my %saw;
1877 @parms = sort @parms;
1878 @parms = grep(!$saw{$_}++, @parms);
1879 return @parms;
1880}
1881
Joe Perches03372db2010-03-05 13:43:00 -08001882sub clean_file_emails {
1883 my (@file_emails) = @_;
1884 my @fmt_emails = ();
1885
1886 foreach my $email (@file_emails) {
1887 $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
1888 my ($name, $address) = parse_email($email);
1889 if ($name eq '"[,\.]"') {
1890 $name = "";
1891 }
1892
1893 my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
1894 if (@nw > 2) {
1895 my $first = $nw[@nw - 3];
1896 my $middle = $nw[@nw - 2];
1897 my $last = $nw[@nw - 1];
1898
1899 if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
1900 (length($first) == 2 && substr($first, -1) eq ".")) ||
1901 (length($middle) == 1 ||
1902 (length($middle) == 2 && substr($middle, -1) eq "."))) {
1903 $name = "$first $middle $last";
1904 } else {
1905 $name = "$middle $last";
1906 }
1907 }
1908
1909 if (substr($name, -1) =~ /[,\.]/) {
1910 $name = substr($name, 0, length($name) - 1);
1911 } elsif (substr($name, -2) =~ /[,\.]"/) {
1912 $name = substr($name, 0, length($name) - 2) . '"';
1913 }
1914
1915 if (substr($name, 0, 1) =~ /[,\.]/) {
1916 $name = substr($name, 1, length($name) - 1);
1917 } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
1918 $name = '"' . substr($name, 2, length($name) - 2);
1919 }
1920
1921 my $fmt_email = format_email($name, $address, $email_usename);
1922 push(@fmt_emails, $fmt_email);
1923 }
1924 return @fmt_emails;
1925}
1926
Joe Perches3c7385b2009-12-14 18:00:46 -08001927sub merge_email {
1928 my @lines;
1929 my %saw;
1930
1931 for (@_) {
1932 my ($address, $role) = @$_;
1933 if (!$saw{$address}) {
1934 if ($output_roles) {
Joe Perches60db31a2009-12-14 18:00:50 -08001935 push(@lines, "$address ($role)");
Joe Perches3c7385b2009-12-14 18:00:46 -08001936 } else {
Joe Perches60db31a2009-12-14 18:00:50 -08001937 push(@lines, $address);
Joe Perches3c7385b2009-12-14 18:00:46 -08001938 }
1939 $saw{$address} = 1;
1940 }
1941 }
1942
1943 return @lines;
1944}
1945
Joe Perchescb7301c2009-04-07 20:40:12 -07001946sub output {
Joe Perchesa8af2432009-12-14 18:00:49 -08001947 my (@parms) = @_;
Joe Perchescb7301c2009-04-07 20:40:12 -07001948
1949 if ($output_multiline) {
1950 foreach my $line (@parms) {
1951 print("${line}\n");
1952 }
1953 } else {
1954 print(join($output_separator, @parms));
1955 print("\n");
1956 }
1957}
Joe Perches1b5e1cf2009-06-16 15:34:01 -07001958
1959my $rfc822re;
1960
1961sub make_rfc822re {
1962# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
1963# comment. We must allow for rfc822_lwsp (or comments) after each of these.
1964# This regexp will only work on addresses which have had comments stripped
1965# and replaced with rfc822_lwsp.
1966
1967 my $specials = '()<>@,;:\\\\".\\[\\]';
1968 my $controls = '\\000-\\037\\177';
1969
1970 my $dtext = "[^\\[\\]\\r\\\\]";
1971 my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
1972
1973 my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
1974
1975# Use zero-width assertion to spot the limit of an atom. A simple
1976# $rfc822_lwsp* causes the regexp engine to hang occasionally.
1977 my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
1978 my $word = "(?:$atom|$quoted_string)";
1979 my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
1980
1981 my $sub_domain = "(?:$atom|$domain_literal)";
1982 my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
1983
1984 my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
1985
1986 my $phrase = "$word*";
1987 my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
1988 my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
1989 my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
1990
1991 my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
1992 my $address = "(?:$mailbox|$group)";
1993
1994 return "$rfc822_lwsp*$address";
1995}
1996
1997sub rfc822_strip_comments {
1998 my $s = shift;
1999# Recursively remove comments, and replace with a single space. The simpler
2000# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
2001# chars in atoms, for example.
2002
2003 while ($s =~ s/^((?:[^"\\]|\\.)*
2004 (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
2005 \((?:[^()\\]|\\.)*\)/$1 /osx) {}
2006 return $s;
2007}
2008
2009# valid: returns true if the parameter is an RFC822 valid address
2010#
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002011sub rfc822_valid {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002012 my $s = rfc822_strip_comments(shift);
2013
2014 if (!$rfc822re) {
2015 $rfc822re = make_rfc822re();
2016 }
2017
2018 return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
2019}
2020
2021# validlist: In scalar context, returns true if the parameter is an RFC822
2022# valid list of addresses.
2023#
2024# In list context, returns an empty list on failure (an invalid
2025# address was found); otherwise a list whose first element is the
2026# number of addresses found and whose remaining elements are the
2027# addresses. This is needed to disambiguate failure (invalid)
2028# from success with no addresses found, because an empty string is
2029# a valid list.
2030
Stephen Hemminger22dd5b02010-03-05 13:43:06 -08002031sub rfc822_validlist {
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002032 my $s = rfc822_strip_comments(shift);
2033
2034 if (!$rfc822re) {
2035 $rfc822re = make_rfc822re();
2036 }
2037 # * null list items are valid according to the RFC
2038 # * the '1' business is to aid in distinguishing failure from no results
2039
2040 my @r;
2041 if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
2042 $s =~ m/^$rfc822_char*$/) {
Joe Perches5f2441e2009-06-16 15:34:02 -07002043 while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
Joe Perches60db31a2009-12-14 18:00:50 -08002044 push(@r, $1);
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002045 }
2046 return wantarray ? (scalar(@r), @r) : 1;
2047 }
Joe Perches60db31a2009-12-14 18:00:50 -08002048 return wantarray ? () : 0;
Joe Perches1b5e1cf2009-06-16 15:34:01 -07002049}