Digest::SHA: deprecate Digest::SHA1, bump version to 0.20120914 and copyright year...
[dupemerge] / dm6
diff --git a/dm6 b/dm6
index 8c1e3e964f6711ecc7c6f0ec4e9ad32634c7ebe8..a3c7b9ea1541662ef0f5825b21a8f599b2d5bb58 100755 (executable)
--- a/dm6
+++ b/dm6
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use warnings;
 use strict;
-use Digest::SHA1 qw(sha1 sha1_hex sha1_base64);
+use Digest::SHA qw(sha1 sha1_hex sha1_base64);
 use Fcntl qw(:DEFAULT :flock);
 use File::Compare;
 use File::Path;
@@ -9,7 +9,7 @@ use File::Temp;
 use File::stat;
 use MIME::Base64;
 
-# Copyright (C) 2010 Zygo Blaxell <dupemerge@mailtoo.hungrycats.org>
+# Copyright (C) 2010-2012 Zygo Blaxell <dupemerge@mailtoo.hungrycats.org>
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@ use MIME::Base64;
 sub digest {
        my ($filename) = (@_);
        die "'$filename' is not a plain file" if (-l $filename) || ! (-f _);
-       my $ctx = Digest::SHA1->new;
+       my $ctx = Digest::SHA->new;
        sysopen(FILE, $filename, O_RDONLY|O_NONBLOCK) or die "open: $filename: $!";
        binmode(FILE);          # FIXME:  Necessary?  Probably harmless...
        $ctx->addfile(\*FILE);
@@ -41,7 +41,7 @@ sub usage {
 Usage: $0 link-dir
 Hashes a NUL-separated list of files on stdin into link-dir.
 
-Version: 0.20100522
+Version: 0.20120914
 USAGE
 }
 
@@ -216,13 +216,44 @@ while (<STDIN>) {
 }
 
 # Garbage collection
-print STDERR "\nGarbage collection in '$link_dir'...";
+print STDERR "\nGarbage collection in '$link_dir'...\n";
 chdir($link_dir) || die "chdir: $link_dir: $!";
-print STDERR "\nRemoving files with link count < 3 and temporary links...";
-system('find . -type f \( -links -3 -o -name ".*" \) -print0 | xargs -0rt rm -f') and die "system: exit status $?";
-print STDERR "\nRemoving empty directories...";
+
+my ($last_inode) = '';
+my @last_links;
+
+sub handle_gc_file {
+       my ($line) = @_;
+       my ($inode, $link) = ($line =~ /^(\S+) (.+)\0$/os);
+       $inode ||= '';
+       if ($inode ne $last_inode) {
+               my ($dev, $ino, $links) = ($last_inode =~ /^(\d+):(\d+):(\d+)$/os);
+               if (defined($links)) {
+                       if ($links && $links == @last_links) {
+                               print STDERR "rm -f @last_links\n";
+                               for my $unlink (@last_links) {
+                                       unlink($unlink) or warn "unlink: $unlink: $!";
+                               }
+                       }
+               } else {
+                       warn "Could not parse '$last_inode' in '$line'" unless $last_inode eq '';
+               }
+               @last_links = ();
+       }
+       $last_inode = $inode;
+       push(@last_links, $link);
+}
+
+print STDERR "Removing files contained entirely in '$link_dir'...\n";
+open(FIND, "find . -type f -printf '%D:%i:%n %p\\0' | sort -z --compress-program=gzip |") or die "open: find: $!";
+while (<FIND>) {
+       handle_gc_file($_);
+}
+handle_gc_file('');
+
+print STDERR "Removing empty directories...\n";
 system("find . -type d -empty -print0 | xargs -0rt rmdir -p --ignore-fail-on-non-empty") and die "system: exit status $?";
-print STDERR "\nDone.\n";
+print STDERR "Done.\n";
 
 exit(0);