| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 1 | #!/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 | # | 
 | 8 | # usage: perl scripts/get_maintainers.pl [OPTIONS] <patch> | 
 | 9 | #        perl scripts/get_maintainers.pl [OPTIONS] -f <file> | 
 | 10 | # | 
 | 11 | # Licensed under the terms of the GNU GPL License version 2 | 
 | 12 |  | 
 | 13 | use strict; | 
 | 14 |  | 
 | 15 | my $P = $0; | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 16 | my $V = '0.15'; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 17 |  | 
 | 18 | use Getopt::Long qw(:config no_auto_abbrev); | 
 | 19 |  | 
 | 20 | my $lk_path = "./"; | 
 | 21 | my $email = 1; | 
 | 22 | my $email_usename = 1; | 
 | 23 | my $email_maintainer = 1; | 
 | 24 | my $email_list = 1; | 
 | 25 | my $email_subscriber_list = 0; | 
 | 26 | my $email_git = 1; | 
 | 27 | my $email_git_penguin_chiefs = 0; | 
 | 28 | my $email_git_min_signatures = 1; | 
 | 29 | my $email_git_max_maintainers = 5; | 
 | 30 | my $email_git_since = "1-year-ago"; | 
 | 31 | my $output_multiline = 1; | 
 | 32 | my $output_separator = ", "; | 
 | 33 | my $scm = 0; | 
 | 34 | my $web = 0; | 
 | 35 | my $subsystem = 0; | 
 | 36 | my $status = 0; | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 37 | my $from_filename = 0; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 38 | my $version = 0; | 
 | 39 | my $help = 0; | 
 | 40 |  | 
 | 41 | my $exit = 0; | 
 | 42 |  | 
 | 43 | my @penguin_chief = (); | 
 | 44 | push(@penguin_chief,"Linus Torvalds:torvalds\@linux-foundation.org"); | 
 | 45 | #Andrew wants in on most everything - 2009/01/14 | 
 | 46 | #push(@penguin_chief,"Andrew Morton:akpm\@linux-foundation.org"); | 
 | 47 |  | 
 | 48 | my @penguin_chief_names = (); | 
 | 49 | foreach my $chief (@penguin_chief) { | 
 | 50 |     if ($chief =~ m/^(.*):(.*)/) { | 
 | 51 | 	my $chief_name = $1; | 
 | 52 | 	my $chief_addr = $2; | 
 | 53 | 	push(@penguin_chief_names, $chief_name); | 
 | 54 |     } | 
 | 55 | } | 
 | 56 | my $penguin_chiefs = "\(" . join("|",@penguin_chief_names) . "\)"; | 
 | 57 |  | 
 | 58 | if (!GetOptions( | 
 | 59 | 		'email!' => \$email, | 
 | 60 | 		'git!' => \$email_git, | 
 | 61 | 		'git-chief-penguins!' => \$email_git_penguin_chiefs, | 
 | 62 | 		'git-min-signatures=i' => \$email_git_min_signatures, | 
 | 63 | 		'git-max-maintainers=i' => \$email_git_max_maintainers, | 
 | 64 | 		'git-since=s' => \$email_git_since, | 
 | 65 | 		'm!' => \$email_maintainer, | 
 | 66 | 		'n!' => \$email_usename, | 
 | 67 | 		'l!' => \$email_list, | 
 | 68 | 		's!' => \$email_subscriber_list, | 
 | 69 | 		'multiline!' => \$output_multiline, | 
 | 70 | 		'separator=s' => \$output_separator, | 
 | 71 | 		'subsystem!' => \$subsystem, | 
 | 72 | 		'status!' => \$status, | 
 | 73 | 		'scm!' => \$scm, | 
 | 74 | 		'web!' => \$web, | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 75 | 		'f|file' => \$from_filename, | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 76 | 		'v|version' => \$version, | 
 | 77 | 		'h|help' => \$help, | 
 | 78 | 		)) { | 
 | 79 |     usage(); | 
 | 80 |     die "$P: invalid argument\n"; | 
 | 81 | } | 
 | 82 |  | 
 | 83 | if ($help != 0) { | 
 | 84 |     usage(); | 
 | 85 |     exit 0; | 
 | 86 | } | 
 | 87 |  | 
 | 88 | if ($version != 0) { | 
 | 89 |     print("${P} ${V}\n"); | 
 | 90 |     exit 0; | 
 | 91 | } | 
 | 92 |  | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 93 | if ($#ARGV < 0) { | 
 | 94 |     usage(); | 
 | 95 |     die "$P: argument missing: patchfile or -f file please\n"; | 
 | 96 | } | 
 | 97 |  | 
 | 98 | my $selections = $email + $scm + $status + $subsystem + $web; | 
 | 99 | if ($selections == 0) { | 
 | 100 |     usage(); | 
 | 101 |     die "$P:  Missing required option: email, scm, status, subsystem or web\n"; | 
 | 102 | } | 
 | 103 |  | 
 | 104 | if ($email && ($email_maintainer + $email_list + $email_subscriber_list | 
 | 105 | 	       + $email_git + $email_git_penguin_chiefs) == 0) { | 
 | 106 |     usage(); | 
 | 107 |     die "$P: Please select at least 1 email option\n"; | 
 | 108 | } | 
 | 109 |  | 
 | 110 | if (!top_of_kernel_tree($lk_path)) { | 
 | 111 |     die "$P: The current directory does not appear to be " | 
 | 112 | 	. "a linux kernel source tree.\n"; | 
 | 113 | } | 
 | 114 |  | 
 | 115 | ## Read MAINTAINERS for type/value pairs | 
 | 116 |  | 
 | 117 | my @typevalue = (); | 
 | 118 | open(MAINT, "<${lk_path}MAINTAINERS") || die "$P: Can't open MAINTAINERS\n"; | 
 | 119 | while (<MAINT>) { | 
 | 120 |     my $line = $_; | 
 | 121 |  | 
 | 122 |     if ($line =~ m/^(\C):\s*(.*)/) { | 
 | 123 | 	my $type = $1; | 
 | 124 | 	my $value = $2; | 
 | 125 |  | 
 | 126 | 	##Filename pattern matching | 
 | 127 | 	if ($type eq "F" || $type eq "X") { | 
 | 128 | 	    $value =~ s@\.@\\\.@g;       ##Convert . to \. | 
 | 129 | 	    $value =~ s/\*/\.\*/g;       ##Convert * to .* | 
 | 130 | 	    $value =~ s/\?/\./g;         ##Convert ? to . | 
 | 131 | 	} | 
 | 132 | 	push(@typevalue, "$type:$value"); | 
 | 133 |     } elsif (!/^(\s)*$/) { | 
 | 134 | 	$line =~ s/\n$//g; | 
 | 135 | 	push(@typevalue, $line); | 
 | 136 |     } | 
 | 137 | } | 
 | 138 | close(MAINT); | 
 | 139 |  | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 140 | ## use the filenames on the command line or find the filenames in the patchfiles | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 141 |  | 
 | 142 | my @files = (); | 
 | 143 |  | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 144 | foreach my $file (@ARGV) { | 
 | 145 |     next if ((-d $file)); | 
 | 146 |     if (!(-f $file)) { | 
 | 147 | 	die "$P: file '${file}' not found\n"; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 148 |     } | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 149 |     if ($from_filename) { | 
 | 150 | 	push(@files, $file); | 
 | 151 |     } else { | 
 | 152 | 	my $file_cnt = @files; | 
 | 153 | 	open(PATCH, "<$file") or die "$P: Can't open ${file}\n"; | 
 | 154 | 	while (<PATCH>) { | 
 | 155 | 	    if (m/^\+\+\+\s+(\S+)/) { | 
 | 156 | 		my $filename = $1; | 
 | 157 | 		$filename =~ s@^[^/]*/@@; | 
 | 158 | 		$filename =~ s@\n@@; | 
 | 159 | 		push(@files, $filename); | 
 | 160 | 	    } | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 161 | 	} | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 162 | 	close(PATCH); | 
 | 163 | 	if ($file_cnt == @files) { | 
 | 164 | 	    die "$P: file '${file}' doesn't appear to be a patch.  " | 
 | 165 | 		. "Add -f to options?\n"; | 
 | 166 | 	} | 
 | 167 | 	@files = sort_and_uniq(@files); | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 168 |     } | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 169 | } | 
 | 170 |  | 
 | 171 | my @email_to = (); | 
 | 172 | my @scm = (); | 
 | 173 | my @web = (); | 
 | 174 | my @subsystem = (); | 
 | 175 | my @status = (); | 
 | 176 |  | 
 | 177 | # Find responsible parties | 
 | 178 |  | 
 | 179 | foreach my $file (@files) { | 
 | 180 |  | 
 | 181 | #Do not match excluded file patterns | 
 | 182 |  | 
 | 183 |     my $exclude = 0; | 
 | 184 |     foreach my $line (@typevalue) { | 
 | 185 | 	if ($line =~ m/^(\C):(.*)/) { | 
 | 186 | 	    my $type = $1; | 
 | 187 | 	    my $value = $2; | 
 | 188 | 	    if ($type eq 'X') { | 
 | 189 | 		if (file_match_pattern($file, $value)) { | 
 | 190 | 		    $exclude = 1; | 
 | 191 | 		} | 
 | 192 | 	    } | 
 | 193 | 	} | 
 | 194 |     } | 
 | 195 |  | 
 | 196 |     if (!$exclude) { | 
 | 197 | 	my $tvi = 0; | 
 | 198 | 	foreach my $line (@typevalue) { | 
 | 199 | 	    if ($line =~ m/^(\C):(.*)/) { | 
 | 200 | 		my $type = $1; | 
 | 201 | 		my $value = $2; | 
 | 202 | 		if ($type eq 'F') { | 
 | 203 | 		    if (file_match_pattern($file, $value)) { | 
 | 204 | 			add_categories($tvi); | 
 | 205 | 		    } | 
 | 206 | 		} | 
 | 207 | 	    } | 
 | 208 | 	    $tvi++; | 
 | 209 | 	} | 
 | 210 |     } | 
 | 211 |  | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 212 |     if ($email && $email_git) { | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 213 | 	recent_git_signoffs($file); | 
 | 214 |     } | 
 | 215 |  | 
 | 216 | } | 
 | 217 |  | 
 | 218 | if ($email_git_penguin_chiefs) { | 
 | 219 |     foreach my $chief (@penguin_chief) { | 
 | 220 | 	if ($chief =~ m/^(.*):(.*)/) { | 
 | 221 | 	    my $chief_name = $1; | 
 | 222 | 	    my $chief_addr = $2; | 
 | 223 | 	    if ($email_usename) { | 
 | 224 | 		push(@email_to, format_email($chief_name, $chief_addr)); | 
 | 225 | 	    } else { | 
 | 226 | 		push(@email_to, $chief_addr); | 
 | 227 | 	    } | 
 | 228 | 	} | 
 | 229 |     } | 
 | 230 | } | 
 | 231 |  | 
 | 232 | if ($email) { | 
 | 233 |     my $address_cnt = @email_to; | 
 | 234 |     if ($address_cnt == 0 && $email_list) { | 
 | 235 | 	push(@email_to, "linux-kernel\@vger.kernel.org"); | 
 | 236 |     } | 
 | 237 |  | 
 | 238 | #Don't sort email address list, but do remove duplicates | 
 | 239 |     @email_to = uniq(@email_to); | 
 | 240 |     output(@email_to); | 
 | 241 | } | 
 | 242 |  | 
 | 243 | if ($scm) { | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 244 |     @scm = sort_and_uniq(@scm); | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 245 |     output(@scm); | 
 | 246 | } | 
 | 247 |  | 
 | 248 | if ($status) { | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 249 |     @status = sort_and_uniq(@status); | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 250 |     output(@status); | 
 | 251 | } | 
 | 252 |  | 
 | 253 | if ($subsystem) { | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 254 |     @subsystem = sort_and_uniq(@subsystem); | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 255 |     output(@subsystem); | 
 | 256 | } | 
 | 257 |  | 
 | 258 | if ($web) { | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 259 |     @web = sort_and_uniq(@web); | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 260 |     output(@web); | 
 | 261 | } | 
 | 262 |  | 
 | 263 | exit($exit); | 
 | 264 |  | 
 | 265 | sub file_match_pattern { | 
 | 266 |     my ($file, $pattern) = @_; | 
 | 267 |     if (substr($pattern, -1) eq "/") { | 
 | 268 | 	if ($file =~ m@^$pattern@) { | 
 | 269 | 	    return 1; | 
 | 270 | 	} | 
 | 271 |     } else { | 
 | 272 | 	if ($file =~ m@^$pattern@) { | 
 | 273 | 	    my $s1 = ($file =~ tr@/@@); | 
 | 274 | 	    my $s2 = ($pattern =~ tr@/@@); | 
 | 275 | 	    if ($s1 == $s2) { | 
 | 276 | 		return 1; | 
 | 277 | 	    } | 
 | 278 | 	} | 
 | 279 |     } | 
 | 280 |     return 0; | 
 | 281 | } | 
 | 282 |  | 
 | 283 | sub usage { | 
 | 284 |     print <<EOT; | 
 | 285 | usage: $P [options] patchfile | 
 | 286 |        $P [options] -f file | 
 | 287 | version: $V | 
 | 288 |  | 
 | 289 | MAINTAINER field selection options: | 
 | 290 |   --email => print email address(es) if any | 
 | 291 |     --git => include recent git \*-by: signers | 
 | 292 |     --git-chief-penguins => include ${penguin_chiefs} | 
 | 293 |     --git-min-signatures => number of signatures required (default: 1) | 
 | 294 |     --git-max-maintainers => maximum maintainers to add (default: 5) | 
 | 295 |     --git-since => git history to use (default: 1-year-ago) | 
 | 296 |     --m => include maintainer(s) if any | 
 | 297 |     --n => include name 'Full Name <addr\@domain.tld>' | 
 | 298 |     --l => include list(s) if any | 
 | 299 |     --s => include subscriber only list(s) if any | 
 | 300 |   --scm => print SCM tree(s) if any | 
 | 301 |   --status => print status if any | 
 | 302 |   --subsystem => print subsystem name if any | 
 | 303 |   --web => print website(s) if any | 
 | 304 |  | 
 | 305 | Output type options: | 
 | 306 |   --separator [, ] => separator for multiple entries on 1 line | 
 | 307 |   --multiline => print 1 entry per line | 
 | 308 |  | 
 | 309 | Default options: | 
 | 310 |   [--email --git --m --l --multiline] | 
 | 311 |  | 
 | 312 | Other options: | 
 | 313 |   --version -> show version | 
 | 314 |   --help => show this help information | 
 | 315 |  | 
 | 316 | EOT | 
 | 317 | } | 
 | 318 |  | 
 | 319 | sub top_of_kernel_tree { | 
 | 320 | 	my ($lk_path) = @_; | 
 | 321 |  | 
 | 322 | 	if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") { | 
 | 323 | 	    $lk_path .= "/"; | 
 | 324 | 	} | 
 | 325 | 	if (   (-f "${lk_path}COPYING") | 
 | 326 | 	    && (-f "${lk_path}CREDITS") | 
 | 327 | 	    && (-f "${lk_path}Kbuild") | 
 | 328 | 	    && (-f "${lk_path}MAINTAINERS") | 
 | 329 | 	    && (-f "${lk_path}Makefile") | 
 | 330 | 	    && (-f "${lk_path}README") | 
 | 331 | 	    && (-d "${lk_path}Documentation") | 
 | 332 | 	    && (-d "${lk_path}arch") | 
 | 333 | 	    && (-d "${lk_path}include") | 
 | 334 | 	    && (-d "${lk_path}drivers") | 
 | 335 | 	    && (-d "${lk_path}fs") | 
 | 336 | 	    && (-d "${lk_path}init") | 
 | 337 | 	    && (-d "${lk_path}ipc") | 
 | 338 | 	    && (-d "${lk_path}kernel") | 
 | 339 | 	    && (-d "${lk_path}lib") | 
 | 340 | 	    && (-d "${lk_path}scripts")) { | 
 | 341 | 		return 1; | 
 | 342 | 	} | 
 | 343 | 	return 0; | 
 | 344 | } | 
 | 345 |  | 
 | 346 | sub format_email { | 
 | 347 |     my ($name, $email) = @_; | 
 | 348 |  | 
 | 349 |     $name =~ s/^\s+|\s+$//g; | 
 | 350 |     $email =~ s/^\s+|\s+$//g; | 
 | 351 |  | 
 | 352 |     my $formatted_email = ""; | 
 | 353 |  | 
 | 354 |     if ($name =~ /[^a-z0-9 \.\-]/i) {    ##has "must quote" chars | 
 | 355 | 	$name =~ s/(?<!\\)"/\\"/g;       ##escape quotes | 
 | 356 | 	$formatted_email = "\"${name}\"\ \<${email}\>"; | 
 | 357 |     } else { | 
 | 358 | 	$formatted_email = "${name} \<${email}\>"; | 
 | 359 |     } | 
 | 360 |     return $formatted_email; | 
 | 361 | } | 
 | 362 |  | 
 | 363 | sub add_categories { | 
 | 364 |     my ($index) = @_; | 
 | 365 |  | 
 | 366 |     $index = $index - 1; | 
 | 367 |     while ($index >= 0) { | 
 | 368 | 	my $tv = $typevalue[$index]; | 
 | 369 | 	if ($tv =~ m/^(\C):(.*)/) { | 
 | 370 | 	    my $ptype = $1; | 
 | 371 | 	    my $pvalue = $2; | 
 | 372 | 	    if ($ptype eq "L") { | 
 | 373 | 		my $subscr = $pvalue; | 
 | 374 | 		if ($subscr =~ m/\s*\(subscribers-only\)/) { | 
 | 375 | 		    if ($email_subscriber_list) { | 
 | 376 | 			$subscr =~ s/\s*\(subscribers-only\)//g; | 
 | 377 | 			push(@email_to, $subscr); | 
 | 378 | 		    } | 
 | 379 | 		} else { | 
 | 380 | 		    if ($email_list) { | 
 | 381 | 			push(@email_to, $pvalue); | 
 | 382 | 		    } | 
 | 383 | 		} | 
 | 384 | 	    } elsif ($ptype eq "M") { | 
 | 385 | 		if ($email_maintainer) { | 
 | 386 | 		    if ($index >= 0) { | 
 | 387 | 			my $tv = $typevalue[$index - 1]; | 
 | 388 | 			if ($tv =~ m/^(\C):(.*)/) { | 
 | 389 | 			    if ($1 eq "P" && $email_usename) { | 
 | 390 | 				push(@email_to, format_email($2, $pvalue)); | 
 | 391 | 			    } else { | 
 | 392 | 				push(@email_to, $pvalue); | 
 | 393 | 			    } | 
 | 394 | 			} | 
 | 395 | 		    } else { | 
 | 396 | 			push(@email_to, $pvalue); | 
 | 397 | 		    } | 
 | 398 | 		} | 
 | 399 | 	    } elsif ($ptype eq "T") { | 
 | 400 | 		push(@scm, $pvalue); | 
 | 401 | 	    } elsif ($ptype eq "W") { | 
 | 402 | 		push(@web, $pvalue); | 
 | 403 | 	    } elsif ($ptype eq "S") { | 
 | 404 | 		push(@status, $pvalue); | 
 | 405 | 	    } | 
 | 406 |  | 
 | 407 | 	    $index--; | 
 | 408 | 	} else { | 
 | 409 | 	    push(@subsystem,$tv); | 
 | 410 | 	    $index = -1; | 
 | 411 | 	} | 
 | 412 |     } | 
 | 413 | } | 
 | 414 |  | 
 | 415 | sub which { | 
 | 416 |     my ($bin) = @_; | 
 | 417 |  | 
 | 418 |     foreach my $path (split /:/, $ENV{PATH}) { | 
 | 419 | 	if (-e "$path/$bin") { | 
 | 420 | 	    return "$path/$bin"; | 
 | 421 | 	} | 
 | 422 |     } | 
 | 423 |  | 
 | 424 |     return ""; | 
 | 425 | } | 
 | 426 |  | 
 | 427 | sub recent_git_signoffs { | 
 | 428 |     my ($file) = @_; | 
 | 429 |  | 
 | 430 |     my $sign_offs = ""; | 
 | 431 |     my $cmd = ""; | 
 | 432 |     my $output = ""; | 
 | 433 |     my $count = 0; | 
 | 434 |     my @lines = (); | 
 | 435 |  | 
 | 436 |     if (which("git") eq "") { | 
 | 437 | 	die("$P: git not found.  Add --nogit to options?\n"); | 
 | 438 |     } | 
 | 439 |  | 
 | 440 |     $cmd = "git log --since=${email_git_since} -- ${file}"; | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 441 |     $cmd .= " | grep -Pi \"^[-_ 	a-z]+by:.*\\\@\""; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 442 |     if (!$email_git_penguin_chiefs) { | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 443 | 	$cmd .= " | grep -Pv \"${penguin_chiefs}\""; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 444 |     } | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 445 |     $cmd .= " | cut -f2- -d\":\""; | 
 | 446 |     $cmd .= " | sed -e \"s/^\\s+//g\""; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 447 |     $cmd .= " | sort | uniq -c | sort -rn"; | 
 | 448 |  | 
 | 449 |     $output = `${cmd}`; | 
 | 450 |     $output =~ s/^\s*//gm; | 
 | 451 |  | 
 | 452 |     @lines = split("\n", $output); | 
 | 453 |     foreach my $line (@lines) { | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 454 | 	if ($line =~ m/([0-9]+)\s+(.*)/) { | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 455 | 	    my $sign_offs = $1; | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 456 | 	    $line = $2; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 457 | 	    $count++; | 
 | 458 | 	    if ($sign_offs < $email_git_min_signatures || | 
 | 459 | 	        $count > $email_git_max_maintainers) { | 
 | 460 | 		last; | 
 | 461 | 	    } | 
 | 462 | 	} else { | 
 | 463 | 	    die("$P: Unexpected git output: ${line}\n"); | 
 | 464 | 	} | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 465 | 	if ($line =~ m/(.+)<(.+)>/) { | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 466 | 	    my $git_name = $1; | 
 | 467 | 	    my $git_addr = $2; | 
 | 468 | 	    $git_name =~ tr/^\"//; | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 469 | 	    $git_name =~ tr/^\\s*//; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 470 | 	    $git_name =~ tr/\"$//; | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 471 | 	    $git_name =~ tr/\\s*$//; | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 472 | 	    if ($email_usename) { | 
 | 473 | 		push(@email_to, format_email($git_name, $git_addr)); | 
 | 474 | 	    } else { | 
 | 475 | 		push(@email_to, $git_addr); | 
 | 476 | 	    } | 
| Joe Perches | 4a7fdb5 | 2009-04-10 12:28:57 -0700 | [diff] [blame] | 477 | 	} elsif ($line =~ m/<(.+)>/) { | 
| Joe Perches | cb7301c | 2009-04-07 20:40:12 -0700 | [diff] [blame] | 478 | 	    my $git_addr = $1; | 
 | 479 | 	    push(@email_to, $git_addr); | 
 | 480 | 	} else { | 
 | 481 | 	    push(@email_to, $line); | 
 | 482 | 	} | 
 | 483 |     } | 
 | 484 |     return $output; | 
 | 485 | } | 
 | 486 |  | 
 | 487 | sub uniq { | 
 | 488 |     my @parms = @_; | 
 | 489 |  | 
 | 490 |     my %saw; | 
 | 491 |     @parms = grep(!$saw{$_}++, @parms); | 
 | 492 |     return @parms; | 
 | 493 | } | 
 | 494 |  | 
 | 495 | sub sort_and_uniq { | 
 | 496 |     my @parms = @_; | 
 | 497 |  | 
 | 498 |     my %saw; | 
 | 499 |     @parms = sort @parms; | 
 | 500 |     @parms = grep(!$saw{$_}++, @parms); | 
 | 501 |     return @parms; | 
 | 502 | } | 
 | 503 |  | 
 | 504 | sub output { | 
 | 505 |     my @parms = @_; | 
 | 506 |  | 
 | 507 |     if ($output_multiline) { | 
 | 508 | 	foreach my $line (@parms) { | 
 | 509 | 	    print("${line}\n"); | 
 | 510 | 	} | 
 | 511 |     } else { | 
 | 512 | 	print(join($output_separator, @parms)); | 
 | 513 | 	print("\n"); | 
 | 514 |     } | 
 | 515 | } |