Revision 3 (by ahitrov@rambler.ru, 2010/03/24 15:19:32) The CORE
#!/usr/bin/perl

use strict;
use warnings 'all';
use locale;

use Getopt::Std;
use FindBin;


my %opts;
getopts('hd:km', \%opts);

&usage if $opts{h} || $#ARGV || ! -d $ARGV[0];
$ARGV[0]=~s/\/+$/\//;

my $md5;
if ($opts{m}) {
	$md5 = (`which md5`=~/(.*)/)[0] || (`which md5sum`=~/(.*)/)[0] || die "No md5 binary found\n";
	$md5 .= ($md5=~/md5$/ ? ' -r ' : '');
}

my %snap;
for (grep {$_} map {chomp; s/^$ARGV[0]\///; $_} `find $ARGV[0]`) {
	unless (-f "$ARGV[0]/$_") {
		$snap{$_} = undef;
		next;
	}

	if ($opts{m}) {
		$snap{$_} = (`$md5 $ARGV[0]/$_ | awk '{print \$1}'`=~/(.*)/)[0];
	} else {
		$snap{$_} = join(':', (stat("$ARGV[0]/$_"))[1,9]) or die $!;
	}
}

unless ($opts{d}) {
	print 'STATE='.($opts{m} ? 'MD5' : 'MTIME')."\n";
	print $_.($snap{$_} ? ' STATE='.$snap{$_} : '')."\n" for sort keys %snap;
	exit;
}

my %base;
open BASE, "<$opts{d}" or die $!;
$_ = <BASE>;
/^STATE=(MD5|MTIME)$/ or die "Unknown STATE line: $_\n";
die "Incompatible mode: $1\n" unless ($opts{m} && $1 eq 'MD5') || (!$opts{m} && $1 eq 'MTIME');
for (map {chomp; $_} <BASE>) {
	if (/^(.+)\s+STATE=(.+)$/) {
		$base{$1} = $2;
		next;
	}
	$base{$_} = undef;
}
close BASE;

print 'STATE='.($opts{m} ? 'MD5' : 'MTIME')."\n";
for (sort keys %snap) {
	next if exists $base{$_} && ($base{$_}||'') eq ($snap{$_}||'');
	print $_.($snap{$_} ? ' STATE='.$snap{$_} : '')."\n";
}

unlink $opts{d} unless $opts{k};

sub usage {
	print <<EOM;
Usage: $FindBin::RealScript [options] directory

Options:
    -d file        Deltify relative to file content
    -h             Print this message
    -k             Keep file specified with -d option
    -m             Use MD5 sum instead mtime checking

EOM
	exit;
}