5 diff.pl -- Helper to diff files (rights, acl and content)
9 diff.pl -s source -d dest [--acl | --attr | --wattr]
16 no warnings 'File::Find';
22 use POSIX qw/strftime/;
24 my ($src, $dst, $help, $acl, $attr, $wattr,
25 $dest_attrib, $src_attrib, $mtimedir);
31 GetOptions("src=s" => \$src, # source directory
32 "dst=s" => \$dst, # dest directory
33 "acl" => \$acl, # acl test
34 "attr" => \$attr, # attributes test
35 "wattr" => \$wattr, # windows attributes
36 "mtime-dir" => \$mtimedir, # check mtime on directories
38 ) or pod2usage(-verbose => 1,
41 pod2usage(-verbose => 1,
46 pod2usage(-verbose => 2,
49 my $md5 = Digest::MD5->new;
53 chdir($src) or die "ERROR: Can't access to $src";
55 find(\&wanted_src, '.');
58 $src_attrib = `attrib /D /S`;
59 $src_attrib = strip_base($src_attrib, $src);
64 chdir($dst) or die "ERROR: Can't access to $dst";
66 find(\&wanted_src, '.');
69 $dest_attrib = `attrib /D /S`;
70 $dest_attrib = strip_base($dest_attrib, $dst);
72 if (lc($src_attrib) ne lc($dest_attrib)) {
74 print "diff.pl ERROR: Differences between windows attributes\n",
75 "$src_attrib\n=========\n$dest_attrib\n";
79 #print Data::Dumper::Dumper(\%src_attr);
80 #print Data::Dumper::Dumper(\%dst_attr);
82 foreach my $f (keys %src_attr)
84 if (!defined $dst_attr{$f}) {
86 print "diff.pl ERROR: Can't find $f in dst\n";
89 compare($src_attr{$f}, $dst_attr{$f});
95 foreach my $f (keys %dst_attr)
98 print "diff.pl ERROR: Can't find $f in src\n";
102 print "diff.pl ERROR: found $ret error(s)\n";
107 # convert \ to / and strip the path
110 my ($data, $path) = @_;
112 $data =~ s!\Q$path!!sig;
119 my ($f1, $f2) = ($h1->{file}, $h2->{file});
121 foreach my $k (keys %$h1) {
122 if (!exists $h2->{$k}) {
124 print "diff.pl ERROR: Can't find $k for dest $f2 ($k=$h1->{$k})\n";
126 if (!defined $h2->{$k}) {
128 print "diff.pl ERROR: $k not found in destination ", $h1->{file}, "\n";
129 print Data::Dumper::Dumper($h1, $h2);
130 } elsif ($h2->{$k} ne $h1->{$k}) {
132 my ($val1, $val2) = ($h1->{$k}, $h2->{$k});
135 (map { strftime('%F %T', localtime($_)) } ($val1, $val2));
137 print "diff.pl ERROR: src and dst $f2 differ on $k ($val1 != $val2)\n";
142 foreach my $k (keys %attr) {
144 print "diff.pl ERROR: Found $k on dst file and not on src ($k=$h2->{$k})\n";
153 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
154 $atime,$mtime,$ctime,$blksize,$blocks) = lstat($f);
156 my $target = readlink($f);
157 $hash->{$File::Find::name} = {
164 file => $File::Find::name,
169 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
170 $atime,$mtime,$ctime,$blksize,$blocks) = stat($f);
173 $hash->{$File::Find::name} = {
181 file => $File::Find::name,
184 open(FILE, '<', $f) or die "ERROR: Can't open '$f': $!";
186 $hash->{$File::Find::name}->{md5} = $md5->addfile(*FILE)->hexdigest;
190 $hash->{$File::Find::name} = {
194 mtime => ($mtimedir)?$mtime:0,
196 file => $File::Find::name,
199 } elsif (-b $f or -c $f) { # dev
200 $hash->{$File::Find::name} = {
206 type => (-b $f)?'block':'char',
207 file => $File::Find::name,
210 } elsif (-p $f) { # named pipe
211 $hash->{$File::Find::name} = {
217 file => $File::Find::name,
220 } else { # other than file and directory
227 $hash->{$File::Find::name}->{acl} = `getfacl "$fe" 2>/dev/null`;
230 $hash->{$File::Find::name}->{attr} = `getfattr "$fe" 2>/dev/null`;