#!/usr/bin/perl -w
#
# cvsdiff: Show the differences between two revisions of a file,
#          with the corresponding log entries.
#
# $dotat: scripts/cvsdiff,v 1.9 2002/08/12 10:26:49 fanf2 Exp $

use strict;

use IO::Pipe;

sub dopipe (@);
sub cvsdiff (@);

die "usage: $0 [<file> [rev1 [rev2]]]\n"
    if @ARGV > 3;

if(@ARGV < 1) {
	cvsdiff;
	exit;
}

my $file = $ARGV[0];

# work out revision numbers
#
my $rev1;
my $rev2;
if(@ARGV > 1) {
	$rev1 = $ARGV[1];
	$rev2 = $ARGV[-1];
} else {
	my $status = dopipe "cvs", "status", $file;
	# XXX maybe we should understand more about cvs status
	if($status =~ /Status: Unknown/) {
		exit;
	}
	if($status =~ /Status: Locally Modified/) {
		cvsdiff $file;
		exit $?/256;
	}
	$status =~ /Working revision:\s+([0-9.]+)/;
	$rev1 = $1;
	$status =~ /Repository revision:\s+([0-9.]+)/;
	$rev2 = $1;
}

# check revision numbers are in the right order
#
if((split /\./, $rev2)[-1] <
   (split /\./, $rev1)[-1]) {
	my $tmp = $rev2;
	$rev2 = $rev1;
	$rev1 = $tmp;
}

# if revision numbers are equal then show previous change
# first revision number for cvs log has to be one more than for cvs diff
#
my $rev1log;
my $rev1diff;
{
	my @rev = split /\./, $rev1;
	if($rev1 eq $rev2) {
		# be careful about the start of a branch
		$rev[-1] -= 1 or $#rev -= 2;
		# be careful about revision 1.1
		@rev = (1,1) unless @rev > 0;
		$rev1log = $rev1;
		$rev1diff = join '.', @rev;
	} else {
		$rev[-1] += 1;
		$rev1log = join '.', @rev;
		$rev1diff = $rev1;
	}
}

# print log entries
{
	my $log = dopipe "cvs", "log", "-r$rev1log:$rev2", $file;
	$log =~ s/\n={77}$//;
	my $sep = "-"x28 . "\n";
	my @log = split /^$sep/mo, $log;
	shift @log; # strip off rubric
	print $sep;
	print join $sep, reverse @log;
	print $sep;
}

# print diff
#
cvsdiff "-r", $rev1diff, "-r", $rev2, $file;

exit $?/256;

sub cvsdiff (@) {
	my $diff = dopipe "cvs", "diff", @_;
	$diff =~ s/^[^-+@ ][^\n]+\n//gm;
	print $diff;
}

sub dopipe (@) {
	# ensure that we don't have anything in our buffers when we fork
	flush STDOUT;
	my $pipe = new IO::Pipe;
	die "$0: pipe: $!\n" unless defined $pipe;
	$pipe->reader(@_);
	$pipe->input_record_separator(undef);
	my $text = $pipe->getline;
	$pipe->close or die "$0: close @_: $!\n";
	return $text;
}
