| 1 | 3 | ahitrov@rambler.ru | #!/usr/bin/perl | 
   
        | 2 |  |  |  | 
   
        | 3 |  |  | use strict; | 
   
        | 4 |  |  | use warnings 'all'; | 
   
        | 5 |  |  | use locale; | 
   
        | 6 |  |  |  | 
   
        | 7 |  |  | use Data::Dumper; | 
   
        | 8 |  |  | use FindBin; | 
   
        | 9 |  |  | use Getopt::Std; | 
   
        | 10 |  |  |  | 
   
        | 11 |  |  |  | 
   
        | 12 |  |  | my %opts; | 
   
        | 13 |  |  | getopts('hkn', \%opts); | 
   
        | 14 |  |  |  | 
   
        | 15 |  |  | &usage if $opts{h} || $#ARGV!=1 || ! -d $ARGV[0] || ! -f $ARGV[1]; | 
   
        | 16 |  |  | $ARGV[0]=~s/\/+$/\//; | 
   
        | 17 |  |  |  | 
   
        | 18 |  |  | my $md5; | 
   
        | 19 |  |  | if ($opts{m}) { | 
   
        | 20 |  |  | $md5 = (`which md5`=~/(.*)/)[0] || (`which md5sum`=~/(.*)/)[0] || die "No md5 binary found\n"; | 
   
        | 21 |  |  | $md5 .= ($md5=~/md5$/ ? ' -r ' : ''); | 
   
        | 22 |  |  | } | 
   
        | 23 |  |  |  | 
   
        | 24 |  |  | my %snap; | 
   
        | 25 |  |  | open SNAP, "<$ARGV[1]" or die $!; | 
   
        | 26 |  |  | $_ = <SNAP>; | 
   
        | 27 |  |  | /^STATE=(MD5|MTIME)$/ or die "Unknown STATE line: $_\n"; | 
   
        | 28 |  |  | my $stype = $1; | 
   
        | 29 |  |  | for (map {chomp; $_} <SNAP>) { | 
   
        | 30 |  |  | next unless /^(.+?)(\s+STATE=(.+))?$/; | 
   
        | 31 |  |  | if ($2) { | 
   
        | 32 |  |  | $snap{$1} = $3; | 
   
        | 33 |  |  | } else { | 
   
        | 34 |  |  | $snap{$1} = undef; | 
   
        | 35 |  |  | } | 
   
        | 36 |  |  | } | 
   
        | 37 |  |  | close SNAP; | 
   
        | 38 |  |  |  | 
   
        | 39 |  |  | for (grep {$snap{$_}} keys %snap) { | 
   
        | 40 |  |  | next unless -f "$ARGV[0]/$_"; | 
   
        | 41 |  |  |  | 
   
        | 42 |  |  | my $state; | 
   
        | 43 |  |  | if ($stype eq 'MD5') { | 
   
        | 44 |  |  | $state = (`$md5 $ARGV[0]/$_ | awk '{print \$1}'`=~/(.*)/)[0]; | 
   
        | 45 |  |  | } else { | 
   
        | 46 |  |  | $state = join(':', (stat("$ARGV[0]/$_"))[1,9]) or die $!; | 
   
        | 47 |  |  | } | 
   
        | 48 |  |  |  | 
   
        | 49 |  |  | if ($snap{$_} eq $state) { | 
   
        | 50 |  |  | unlink "$ARGV[0]/$_" unless $opts{n}; | 
   
        | 51 |  |  | print "    removing old file: $_\n"; | 
   
        | 52 |  |  | } else { | 
   
        | 53 |  |  | print "    skipping modified file: $_\n"; | 
   
        | 54 |  |  | } | 
   
        | 55 |  |  | } | 
   
        | 56 |  |  |  | 
   
        | 57 |  |  | for (sort {(length($b)<=>length($a))||($a cmp $b)} grep {!$snap{$_}} keys %snap) { | 
   
        | 58 |  |  | opendir DIR, "$ARGV[0]/$_" or die $!; | 
   
        | 59 |  |  | my @cont = readdir DIR; | 
   
        | 60 |  |  | if ($#cont==1) { | 
   
        | 61 |  |  | rmdir "$ARGV[0]/$_"; | 
   
        | 62 |  |  | print "    removing empty dir: $_\n"; | 
   
        | 63 |  |  | } else { | 
   
        | 64 |  |  | print "    skipping non-empty dir: $_\n"; | 
   
        | 65 |  |  | } | 
   
        | 66 |  |  | closedir DIR; | 
   
        | 67 |  |  | } | 
   
        | 68 |  |  |  | 
   
        | 69 |  |  | unlink $ARGV[1] unless $opts{k} || $opts{n}; | 
   
        | 70 |  |  |  | 
   
        | 71 |  |  |  | 
   
        | 72 |  |  | sub usage { | 
   
        | 73 |  |  | print <<EOM; | 
   
        | 74 |  |  | Usage: $FindBin::RealScript [options] prefix file | 
   
        | 75 |  |  |  | 
   
        | 76 |  |  | Options: | 
   
        | 77 |  |  | -h             Print this message | 
   
        | 78 |  |  | -k             Keep file | 
   
        | 79 |  |  | -n             Dry-run mode | 
   
        | 80 |  |  |  | 
   
        | 81 |  |  | EOM | 
   
        | 82 |  |  | exit; | 
   
        | 83 |  |  | } |