---- old/support/rsyncsums
-+++ new/support/rsyncsums
-@@ -0,0 +1,175 @@
-+#!/usr/bin/perl -w
-+use strict;
-+
-+use Getopt::Long;
-+use Cwd qw(abs_path cwd);
-+use Digest::MD4;
-+use Digest::MD5;
-+
-+our $SUMS_FILE = '.rsyncsums';
-+
-+our($recurse_opt, $help_opt);
-+our $verbosity = 0;
-+
-+&Getopt::Long::Configure('bundling');
-+&usage if !&GetOptions(
-+ 'recurse|r' => \$recurse_opt,
-+ 'verbose|v+' => \$verbosity,
-+ 'help|h' => \$help_opt,
-+) || $help_opt;
-+
-+my $start_dir = cwd();
-+
-+my @dirs = @ARGV;
-+@dirs = '.' unless @dirs;
-+foreach (@dirs) {
-+ $_ = abs_path($_);
-+}
-+
-+$| = 1;
-+
-+my $md4 = Digest::MD4->new;
-+my $md5 = Digest::MD5->new;
-+
-+while (@dirs) {
-+ my $dir = shift @dirs;
-+
-+ if (!chdir($dir)) {
-+ warn "Unable to chdir to $dir: $!\n";
-+ next;
-+ }
-+ if (!opendir(DP, '.')) {
-+ warn "Unable to opendir $dir: $!\n";
-+ next;
-+ }
-+
-+ if ($verbosity) {
-+ my $reldir = $dir;
-+ $reldir =~ s#^$start_dir(/|$)# $1 ? '' : '.' #eo;
-+ print "$reldir ... ";
-+ }
-+
-+ my $sums_mtime = (stat($SUMS_FILE))[9];
-+ my %cache;
-+ my @subdirs;
-+ my $cnt = 0;
-+ while (defined(my $fn = readdir(DP))) {
-+ next if $fn =~ /^\.\.?$/ || $fn =~ /^\Q$SUMS_FILE\E$/o || -l $fn;
-+ if (-d _) {
-+ push(@subdirs, "$dir/$fn");
-+ next;
-+ }
-+ next unless -f _;
-+
-+ my($size,$mtime,$ctime,$inode) = (stat(_))[7,9,10,1];
-+ next if $size == 0;
-+
-+ $cache{$fn} = [ $size, $mtime, $ctime & 0xFFFFFFFF, $inode & 0xFFFFFFFF ];
-+ $cnt++;
-+ }
-+
-+ closedir DP;
-+
-+ unshift(@dirs, sort @subdirs) if $recurse_opt;
-+
-+ if (!$cnt) {
-+ if (defined $sums_mtime) {
-+ print "(removed $SUMS_FILE) " if $verbosity;
-+ unlink($SUMS_FILE);
-+ }
-+ print "empty\n" if $verbosity;
-+ next;
-+ }
-+
-+ if (open(FP, '+<', $SUMS_FILE)) {
-+ while (<FP>) {
-+ chomp;
-+ my($sum4, $sum5, $size, $mtime, $ctime, $inode, $fn) = split(' ', $_, 7);
-+ my $ref = $cache{$fn};
-+ if (defined $ref) {
-+ if ($$ref[0] == $size
-+ && $$ref[1] == $mtime
-+ && $$ref[2] == $ctime
-+ && $$ref[3] == $inode
-+ && $sum4 !~ /=/ && $sum5 !~ /=/) {
-+ $$ref[4] = $sum4;
-+ $$ref[5] = $sum5;
-+ $cnt--;
-+ } else {
-+ $$ref[4] = $$ref[5] = undef;
-+ }
-+ } else {
-+ $cnt = -1; # Force rewrite due to removed line.
-+ }
-+ }
-+ } else {
-+ open(FP, '>', $SUMS_FILE) or die "Unable to write $dir/$SUMS_FILE: $!\n";
-+ $cnt = -1;
-+ }
-+
-+ if ($cnt) {
-+ print "UPDATING\n" if $verbosity;
-+ while (my($fn, $ref) = each %cache) {
-+ next if defined $$ref[3] && defined $$ref[4];
-+ if (!open(IN, $fn)) {
-+ print STDERR "Unable to read $fn: $!\n";
-+ delete $cache{$fn};
-+ next;
-+ }
-+
-+ my($size,$mtime,$ctime,$inode) = (stat(IN))[7,9,10,1];
-+ if ($size == 0) {
-+ close IN;
-+ next;
-+ }
-+
-+ my($sum4, $sum5);
-+ while (1) {
-+ while (sysread(IN, $_, 64*1024)) {
-+ $md4->add($_);
-+ $md5->add($_);
-+ }
-+ $sum4 = $md4->hexdigest;
-+ $sum5 = $md5->hexdigest;
-+ print " $sum4 $sum5" if $verbosity > 2;
-+ print " $fn\n" if $verbosity > 1;
-+ my($size2,$mtime2,$ctime2,$inode2) = (stat(IN))[7,9,10,1];
-+ last if $size == $size2 && $mtime == $mtime2
-+ && $ctime == $ctime2 && $inode == $inode2;
-+ $size = $size2;
-+ $mtime = $mtime2;
-+ $ctime = $ctime2;
-+ $inode = $inode2;
-+ sysseek(IN, 0, 0);
-+ }
-+
-+ close IN;
-+
-+ $cache{$fn} = [ $size, $mtime, $ctime, $inode, $sum4, $sum5 ];
-+ }
-+
-+ seek(FP, 0, 0);
-+ foreach my $fn (sort keys %cache) {
-+ my $ref = $cache{$fn};
-+ my($size, $mtime, $ctime, $inode, $sum4, $sum5) = @$ref;
-+ printf FP '%s %s %10d %10d %10d %10d %s' . "\n", $sum4, $sum5, $size, $mtime, $ctime, $inode, $fn;
-+ }
-+ truncate(FP, tell(FP));
-+ } else {
-+ print "ok\n" if $verbosity;
-+ }
-+
-+ close FP;
-+}
-+
-+sub usage
-+{
-+ die <<EOT;
-+Usage: rsyncsums [OPTIONS] [DIRS]
-+
-+Options:
-+ -r, --recurse Update $SUMS_FILE files in subdirectories too.
-+ -v, --verbose Mention what we're doing. Repeat for more info.
-+ -h, --help Display this help message.
-+EOT
-+}