#!/usr/bin/env perl

sub usage() {
    print STDERR "extract-hunks regex patch.diff [out_with.diff [out_without.diff]]
    
Extract hunks from diff based on regex.
  regex            Regular expression for matching
  patch.diff       Patch that will be separated
  out_with.diff    Output patch that will contain the hunks matching regex
  out_without.diff Output patch with non-mathing hunks\n";

    exit 1;
}

$regex = shift;
$patch = shift;
$out_with = shift;
$out_without = shift;

usage() if ( ! defined( $regex ) || ! defined( $patch ) );
$out_with = "with-" . $patch if ( ! defined( $out_with ) );
$out_without = "without-" . $patch if ( ! defined( $out_without ) );
usage() if ( $out_with eq $patch || $out_without eq $patch );

# open the files
my $PATCH;
my $WITH;
my $WITHOUT;

if ( ! open( $PATCH, "$patch" ) ) {
    print STDERR "Unable to open patch '$patch'.";
    exit 1;
}
if ( ! open( $WITH, ">$out_with" ) ) {
    print STDERR "Unable to open '$out_with'.";
    exit 1;
}
if ( ! open( $WITHOUT, ">$out_without" ) ) {
    print STDERR "Unable to open '$out_without'.";
    exit 1;
}

# do the job

my $hunk = "";
my $header_with = "";
my $header_without = "";

my $regex_found = 0;
my $reset_header = 0;
my $plus_lines = 0;
my $minus_lines = 0;

my $line;
my $lineno = 0;

while ( $line = <$PATCH>) {
    ++$lineno;
    
    # beginning of a hunk
    if ( $line =~ /^@@ [^,]*,([^ ]*) [^,]*,([^ ]*)/ ) {
	$hunk = $line;
	$minus_lines = $1;
	$plus_lines = $2;
	$regex_found = 0;

	next;
    }

    # inside a hunk
    if ( $plus_lines > 0 || $minus_lines > 0 ) {
	$hunk .= $line;
	if ( $line =~ $regex ) {
	    $regex_found = 1;
	}

	if ( $line =~ /[^ \+\-]/ ) {
	    --$plus_lines  if ( $line =~ /^[ \+]/ );
	    --$minus_lines if ( $line =~ /^[ \-]/ );
	}
	else {
	    print STDERR "Cannot handle line $lineno: $line\n";
	    close( $PATCH );
	    close( $WITH );
	    close( $WITHOUT );
	    exit 1;
	}

	# end of the hunk
	if ( $plus_lines == 0 && $minus_lines == 0 ) {
	    if ( $regex_found ) {
		print $WITH $header_with . $hunk;
		$header_with = "";
	    }
	    else {
		print $WITHOUT $header_without . $hunk;
		$header_without = "";
	    }
	    $reset_header = 1;
	}
    }
    # inside a header
    else {
	if ( $reset_header ) {
	    $header_with = "";
	    $header_without = "";
	    $reset_header = 0;
	}
	$header_with .= $line;
	$header_without .= $line;
    }
}
close( $PATCH );
close( $WITH );
close( $WITHOUT );
