#! /usr/bin/env perl
#
# David Leonard <david.leonard@csee.uq.edu.au>, 2000. Public domain.
#
# This script converts context diffs (diff -c) into
# unified diffs (diff -u).
#
# Must supply context diff on stdin, and unified diff will be sent to
# stdout, with non-diff text preserved.
#
# This was written to convert Dan Johnston's assignment summaries
# into a more compact form for marking.
#

$debug = 1 if defined($ENV{'DEBUG'});

while (<STDIN>) {
	if    (m/^\Q***\E (.*)/) { print "--- $1\n"; }
	elsif (m/^\Q---\E (.*)/) { print "+++ $1\n"; }
	elsif (m/^\Q***************\E\s*$/) {


		print STDERR "\n" if $debug;

		#-- Read the 'before' hunk header
		$_ = <STDIN>;
		m/^\*\*\*\s*(\d+),(\d+)\s*\*\*\*\*/ 
			|| warn "$ARGV:$.: bad a range: $_\n";
		$astart = $1;
		$astop = $2;
		print STDERR "arange $astart,$astop: $_" if $debug;
		$afirst = undef;
		@a = ();
		$chgcount = 0;
		$subcount = 0;

		#-- Read the 'before' hunk, with - and ! lines
		for $i ($astart..$astop) {
			$_ = <STDIN>;
			if ($i == $astart && m/^---/) {
				print STDERR "(a is empty)\n" if $debug;
				last;
			}
			push(@a, $_);
			$afirst = $i if !defined($afirst) && m/^[-!]/;
			$alast = $i if m/^[-!]/;
			$chgcount++ if m/^[!]/;
			$subcount++ if m/^[-]/;
		}

		#-- Read the 'after' hunk header
		$_ = <STDIN> unless !@a;
		m/^---\s*(\d+),(\d+)\s*----/
			|| warn "$ARGV:$.: bad b range: $_\n";
		$bstart = $1;
		$bstop = $2;
		print STDERR "brange $bstart,$bstop: $_" if $debug;
		$bfirst = undef;
		@b = ();

		#-- Read the 'after' hunk, with + and ! lines
		if (!@a || $chgcount || 
		     (($bstop - $bstart) !=  ($astop - $astart) - $subcount))
		{
			for $i ($bstart..$bstop) {
				$_ = <STDIN>;
				if ($i == $bstart && !m/^[ +!]/) { last; }
				push(@b, $_);
				$bfirst = $i if !defined($bfirst) && m/^[+!]/;
				$blast = $i if m/^[+!]/;
			}
		} 

		#-- after hunk omitted but we can derive it
		if (!@b) {
			print STDERR "(filling b with a $#a)\n" if $debug;
			for (@a) { push(@b, $_) unless m/^-/; }
			$bfirst = $bstart + ($afirst - $astart);
		}

		#-- before hunk omitted but we can derive it
		if (!@a) {
			for (@b) { push(@a, $_) unless m/^[+]/; }
			$afirst = $astart + ($bfirst - $bstart);
		}

		if ($debug) { 
			print STDERR "#a = $#a #b=$#b\n";
			if (!@a) { print STDERR "\@a is empty!\n"; }
			for (@a) { print STDERR "a: $_"; }
			if (!@b) { print STDERR "\@b is empty!\n"; }
			for (@b) { print STDERR "b: $_"; }
		}

		#-- fake the unidiff header
		printf("@@ -%d,%d +%d,%d @@\n", $afirst, $alast,
			$bfirst, $blast);

		#-- merge the before and after hunks into a unidiff
		$ia = $ib = 0;
		while (1) {
			if ($ia <= $#a){ $a = $a[$ia]; $ax = substr($a, 0, 1); }
			else	       { $a = $ax = '.'; }
			if ($ib <= $#b){ $b = $b[$ib]; $bx = substr($b, 0, 1); }
			else	       { $b = $bx = '.'; }

			if ($ax eq '.' && $bx eq '.') {
				last;
			}
			elsif ($ax eq ' ' && $bx eq ' ') {
				#if ($a ne $b) {
					#warn "bad diff a=$a != b=$b"
				#}
				print $a;
				$ia++; $ib++;
			} 
			elsif ($ax eq '!' && $bx eq '!') {
				# consume from @a
				while ($ia <= $#a && $a[$ia] =~ m/^!/) {
					print "-".substr($a[$ia], 1, -1)."\n";
					$ia++;
				}
				# consume from @b
				while ($ib <= $#b && $b[$ib] =~ m/^!/) {
					print "+".substr($b[$ib], 1, -1)."\n";
					$ib++;
				}
			}
			elsif ($ax eq '-') {
				print $a;
				$ia++;
			}
			elsif ($bx eq '+') {
				print $b;
				$ib++;
			}
			else {
				warn "bad diff: a=$a b=$b\n";
				#-- abort the merge
				last;
			}
		}
	}
	else {
		#-- pass non-diff lines through without change
		print;
	}
}

