#!/usr/bin/perl

use strict;
use warnings;
use Date::Parse;

my @changes;
if (!@ARGV || $ARGV[0] ne '-u') {
    # Local status compares files to the CVS/Entries.
    # This doesn't try to find unknown files, though.
    my @cvsdirs = qw( CVS );
    while (@cvsdirs) {
	my $dir = shift @cvsdirs;
	open ENTRIES, "$dir/Entries" or die "Unable to open $dir/Entries: $!";
	(my $path = $dir) =~ s/CVS$//;
	while (<ENTRIES>) {
	    if (m#^D/([^/]+)/#) {
		push @cvsdirs, "$path$1/CVS";
		next;
	    }
	    next unless my($fn, $rev, $date) = (split(m#/#))[1..3];
	    next if $rev eq '';
	    my $mtime = (stat("$path$fn"))[9];
	    my $etime = str2time($date . ' UTC') || -1;
	    if ($rev eq '0') {
		push @changes, "A $path$fn\n";
	    } elsif (!defined($mtime)) {
		push @changes, "0 $path$fn\n";
	    } elsif ($date =~ /\+/) {
		push @changes, "C $path$fn\n";
	    } elsif ($mtime != $etime) {
		push @changes, "M $path$fn\n";
	    }
	}
	close ENTRIES;
    }
} else {
    # For status that includes remote changes, we'll reformat the "cvs status" output.
    my $dir = '';
    open CVS, '-|', 'cvs status 2>&1' or die "unable to fork: $!";
    while (<CVS>) {
	if (/sufficient access to |cvs \[status aborted\]/) {
	    print "ERROR: ", $_;
	    next;
	}
	if (/^\? /) {
	    push @changes, $_;
	    next;
	}
	if (/(?:status|server): Examining (.*)/) {
	    $dir = $1 eq '.' ? '' : "$1/";
	} elsif (/File: (.*?)\s+Status: (.*?)\s+\z/) {
	    my($fn, $status) = ($1, $2);
	    if ($status =~ /up-to-date/i) {
		# No output
	    } elsif ($status =~ /locally modified/i) {
		push @changes, "M $dir$fn\n";
	    } elsif ($status =~ /locally added/i) {
		push @changes, "A $dir$fn\n";
	    } elsif ($status =~ /needs patch/i) {
		push @changes, "U $dir$fn\n";
	    } elsif ($status =~ /needs merge|conflicts on merge/i) {
		push @changes, "C $dir$fn\n";
	    } elsif ($status =~ /entry invalid/i) {
		push @changes, "D $dir$fn\n";
	    } elsif ($status =~ /needs checkout/i) {
		$fn =~ s/^no file //;
		push @changes, "0 $dir$fn\n";
	    } else {
		warn "Unknown status for $dir$fn: $status\n";
	    }
	}
    }
    close CVS;
}
print sort @changes;
