#!/usr/bin/perl -w

use strict;

use Getopt::Std;
use vars qw($opt_h $opt_f $opt_t $opt_l $opt_O $opt_r);

getopts("hfOr");

if ($opt_h) {
	print <<End;
usage: cvs log foo | $0 [-f]
-f  display headings
End
	exit;
}

my $last;
my $current;

sub read_line {
	$last = $current;
	$current = <>;
	defined $current or
		return undef;
	chomp $current;
	return $current;
}

sub write_row {
	my ($row) = @_;
	for (@$row) {
		if (defined $_) {
			s/\\/\\\\/g;
			s/\t/\\t/g;
			s/\n/\\n/g;
			s/\0/\\0/g;
		} else {
			$_ = "\0";
		}
	}
	print OUT join "\t", @$row;
	print OUT "\n";
}

if ($opt_f) {
	write_row([qw(1 working_file head rcs_file branch locks access_list keyword_substitution total_revisions selected_revisions description)]);
	write_row([qw(2 working_file revision tag)]);
	write_row([qw(3 working_file revision date author state lines message)]);
}

my $temp_out_file;

if (!$opt_O) {
	chomp ($temp_out_file = `tempfile`);
	open OUT, "> $temp_out_file";
} else {
	open OUT, ">&STDOUT";
}

my $line;

sub complain {
	die "$0: @_\n  $last\n> $current\n  ".(read_line()||"")."\n";
}

while (defined ($line=read_line())) {
	my ( $rcs_file, $working_file, $head, $branch, $locks, $access_list,
	     $keyword_substitution, $total_revisions, $selected_revisions, $description,
	     @tags );
	$line eq "" or
		complain "expected blank line";
	($rcs_file = read_line()) =~ s/^RCS file: // or
		complain "expected 'RCS file:' line";
	($working_file = read_line()) =~ s/^Working file: // or
		complain "expected 'Working file:' line";
	($head = read_line()) =~ s/^head: ?// or
		complain "expected 'head:' line";
	($branch = read_line()) =~ s/^branch: ?// or
		complain "expected 'branch:' line";
	($locks = read_line()) =~ s/^locks: ?// or
		complain "expected 'locks:' line";
	($access_list = read_line()) =~ s/^access list: ?// or
		complain "expected 'access list:' line";
	
	read_line() eq "symbolic names:" or
		complain "expected 'symbolic names:' line";
	while (($line = read_line()) =~ s/^\t//) {
		my ($tag, $revision) = $line =~ /^(.+): (.+)$/;
		push @tags, [$revision, $tag];
	}
	
	($keyword_substitution = $line) =~ s/^keyword substitution: ?// or
		complain "expected 'keyword substitution:' line";
	
	($total_revisions, $selected_revisions) = read_line() =~ /^total revisions: (\d+);\tselected revisions: (\d+)$/ or
		complain "expected 'total revisions:  selected revisions:' line";
	
	read_line() eq "description:" or
		complain "expected 'description:' line";
	$description = '';
	while (($line = read_line()), ($line ne '----------------------------' && $line ne '=============================================================================')) {
		$description .= "$line\n";
	}
	
	write_row([1, $working_file, $head, $rcs_file, $branch, $locks, $access_list, $keyword_substitution, $total_revisions, $selected_revisions, $description]);
	
	for (@tags) {
		write_row([2, $working_file, @$_]);
	}
	
	while ($line ne "=============================================================================") {
		my ($revision, $date, $author, $state, $lines, $message);
		($revision = read_line()) =~ s/^revision // or
			complain "expected 'revision' line";
		($date, $author, $state, $lines) = read_line() =~ /^date: ([^;]*);  author: ([^;]*);  state: ([^;]*);(?:  lines: (.*))?$/ or
			complain "expected 'date: author: state: lines:' line";
		$lines ||= '';
		$message = '';
		while (($line = read_line()), ($line ne '----------------------------' && $line ne '=============================================================================')) {
			$message .= "$line\n";
		}
		write_row([3, $working_file, $revision, $date, $author, $state, $lines, $message]);
	}
}

if (!$opt_O) {
	$opt_r = $opt_r ? "" : "r";
	system "cvs-revision-order $temp_out_file 3 $opt_r | order 2 1";
	unlink $temp_out_file;
}
