#!/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 () { 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 () { 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;