#! /usr/bin/perl
#
# selectPatch reads patch files and does 1 of 3 actions:
# - 'list' the files being patched (like 'lsdiff')
# - select ('keep') files being patched into a new file
# - exclude ('drop') files being patches and keep everything else
#
# usage:
# selectPatch patchfile {keep|drop|list} regex...
# regex (list) is optional for list; required for keep or drop;

if (scalar(@ARGV) < 2) {	# need more for keep|drop
	usage();
}

my $debugging = 0;
my $filename = shift;	# $ARGV[0];
my $whichcmd = shift;	# $ARGV[1];
my $args_rem = scalar(@ARGV);
##my $line;
my $eatline;

# goto begin;

sub usage
{
	print "usage: selectPatch patchfile {list|keep|drop} regex...\n";
	exit 1;
}

sub bad_file
{
	printf STDERR "malformed diff file found at this line:\n";
	printf STDERR $_[0];
	exit 1;
}

sub print_stats
{
	#my ($files, $keep, $drop) = $_[0], $_[1], $_[2];
	printf STDERR "== $_[0] diffed files: kept $_[1], dropped $_[2]\n";
}

sub eat_line
{
	$eatline = <FILE>;
}

sub match_regex
{
    my $ix;		# for looking at each @ARGV[$ix] regex;
    my $matchline = $_[0];

    ##print "trying to match :$matchline: to $args_rem args:\n";
    for ($ix = 0; $ix < $args_rem; $ix++) {
	##print "$ix:$ARGV[$ix]:\n";
	if (($ARGV[$ix] =~ $matchline) || ($matchline =~ $ARGV[$ix])) {
		return (1);
	}
    }
    return (0);
}

sub skip_to_diff
{
    if (substr($line, 0, 4) eq "diff") {
	return;
    }
    ##if (substr($line, 0, 4) eq "--- ") {
	##return;
    ##}
    while ($line = <FILE>) {
	##print "skipline=$line";
	my $chmpline = $line;
	chomp ($chmpline);
	##print "chmpline=$chmpline\n";
	last if substr($line, 0, 4) eq "diff";
	next if $chmpline eq "---";
	##last if substr($line, 0, 4) eq "--- ";
    }
}

sub copy_til_diff
{
    print $line;

    while ($line = <FILE>) {
	last if substr($line, 0, 4) eq "diff";
	##last if substr($line, 0, 4) eq "--- ";
	print $line;
    }
}

sub list_diffs
{
    my $diff_files = 0;
    my $selected = 0;

    skip_to_diff();		# skip header lines
    ##if (eof FILE) {print "Teof\n";} else {print "Feof\n";}
    ##print "list:line:$line";

    while (1) {
	if (substr($line, 0, 4) eq "diff") {
		$diff_files++;
		$eatline = "";
		# eat/skip cvs/git/etc metadata lines
		while (substr($eatline, 0, 4) ne "+++ ") {
			eat_line();
		}
		if (($args_rem == 0) || match_regex($line)) {
			my @diffsplit = split /\s+/, $line;
			print "diff: $diffsplit[scalar(@diffsplit) - 1]\n";
			$selected++;
		}
		$line = $eatline;
	}
	elsif (substr($line, 0, 4) eq "--- ") {
		$diff_files++;
		$newfileline = <FILE>;
		while (substr($newfileline, 0, 4) ne "+++ ") {
			$newfileline = <FILE>;
		}
		if (eof FILE) {
			bad_file($newfileline);
			last;
		}
		my @oldsplit = split /\s+/, $line;
		my @newsplit = split /\s+/, $newfileline;
		$checkline = "diff " . $oldsplit[1] . $newsplit[1];
		if (($args_rem == 0) || match_regex($checkline)) {
			print $newfileline;
			$selected++;
		}
		$line = $newfileline;
	}

	skip_to_diff();
	last if eof FILE;
    }

    print "== $selected of $diff_files diff files.\n";
}

sub select_diffs	# for "keep"
{
    # Initialize variables for this file.
    my $diff_files = 0;
    my $keep_files = 0;
    my $drop_files = 0;
    #my $keep_lines = 0;
    #my $drop_lines =  0;

    while (1) {
	skip_to_diff();
	last if eof FILE;
	$diff_files++;
	##print "seldiffline:$line";
	if (match_regex($line)) {
		##print "copy_this_one:$line";
		copy_til_diff();
		$keep_files++;
	}
	else {
		$drop_files++;
		eat_line();		# skip the diff line
		$line = "";
	}
	last if eof FILE;
    }

    print_stats($diff_files, $keep_files, $drop_files);
}

sub drop_diffs		# for "drop"
{
    # Initialize variables for this file.
    my $diff_files = 0;
    my $keep_files = 0;
    my $drop_files = 0;
    #my $keep_lines = 0;
    #my $drop_lines =  0;

    while (1) {
	skip_to_diff();
	last if eof FILE;
	$diff_files++;
	if (!match_regex($line)) {
		copy_til_diff();
		$keep_files++;
	}
	else {
		$drop_files++;
		eat_line();		# skip the diff line
		$line = "";
	}
	last if eof FILE;
    }

    print_stats($diff_files, $keep_files, $drop_files);
}

# begin:

##print "fn:$filename: cmd:$whichcmd:\n";
##print "$args_rem args remaining\n";

if (($whichcmd ne "list") && ($whichcmd ne "keep") && ($whichcmd ne "drop")) {
	usage();
}
if (($args_rem == 0) && ($whichcmd ne "list")) {
	usage();
}

# Open the patch file.
open(FILE, $filename) || die "Cannot open $filename: $!\n";

if ($whichcmd eq "keep") {
	select_diffs();
}
elsif ($whichcmd eq "drop") {
	drop_diffs();
}
else {
	list_diffs();
}

close(FILE);

# end;
