5 diff.pl -- Helper to diff files (rights, acl and content)
9 diff.pl -s source -d dest [-e exclude ] [--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);
33 GetOptions("src=s" => \$src, # source directory
34 "dst=s" => \$dst, # dest directory
35 "acl" => \$acl, # acl test
36 "attr" => \$attr, # attributes test
37 "wattr" => \$wattr, # windows attributes
38 "mtime-dir" => \$mtimedir, # check mtime on directories
39 "exclude=s@" => \@exclude, # exclude some files
40 "notop" => \$notop, # Exclude top directory
42 ) or pod2usage(-verbose => 1,
45 pod2usage(-verbose => 1,
50 pod2usage(-verbose => 2,
53 my $md5 = Digest::MD5->new;
57 chdir($src) or die "ERROR: Can't access to $src";
60 find(\&wanted_src, '.');
63 $src_attrib = `attrib /D /S`;
64 $src_attrib = strip_base($src_attrib, $src);
69 chdir($dst) or die "ERROR: Can't access to $dst";
71 find(\&wanted_src, '.');
74 $dest_attrib = `attrib /D /S`;
75 $dest_attrib = strip_base($dest_attrib, $dst);
77 if (lc($src_attrib) ne lc($dest_attrib)) {
79 print "diff.pl ERROR: Differences between windows attributes\n",
80 "$src_attrib\n=========\n$dest_attrib\n";
84 #print Data::Dumper::Dumper(\%src_attr);
85 #print Data::Dumper::Dumper(\%dst_attr);
87 foreach my $f (keys %src_attr)
89 if ($notop && $f eq '.') {
95 if (!defined $dst_attr{$f}) {
97 print "diff.pl ERROR: Can't find $f in dst\n";
100 compare($src_attr{$f}, $dst_attr{$f});
102 delete $src_attr{$f};
103 delete $dst_attr{$f};
106 foreach my $f (keys %dst_attr)
109 print "diff.pl ERROR: Can't find $f in src\n";
113 print "diff.pl ERROR: found $ret error(s)\n";
118 # convert \ to / and strip the path
121 my ($data, $path) = @_;
123 $data =~ s!\Q$path!!sig;
130 my ($f1, $f2) = ($h1->{file}, $h2->{file});
132 foreach my $k (keys %$h1) {
133 if (!exists $h2->{$k}) {
135 print "diff.pl ERROR: Can't find $k for dest $f2 ($k=$h1->{$k})\n";
137 if (!defined $h2->{$k}) {
139 print "diff.pl ERROR: $k not found in destination ", $h1->{file}, "\n";
140 print Data::Dumper::Dumper($h1, $h2);
141 } elsif ($h2->{$k} ne $h1->{$k}) {
143 my ($val1, $val2) = ($h1->{$k}, $h2->{$k});
146 (map { strftime('%F %T', localtime($_)) } ($val1, $val2));
150 (map { sprintf('%o', $_) } ($val1, $val2));
152 print "diff.pl ERROR: src and dst $f2 differ on $k ($val1 != $val2)\n";
157 foreach my $k (keys %attr) {
159 print "diff.pl ERROR: Found $k on dst file and not on src ($k=$h2->{$k})\n";
166 if (grep ($f, @exclude)) {
170 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
171 $atime,$mtime,$ctime,$blksize,$blocks) = lstat($f);
173 my $target = readlink($f);
174 $hash->{$File::Find::name} = {
181 file => $File::Find::name,
186 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
187 $atime,$mtime,$ctime,$blksize,$blocks) = stat($f);
190 $hash->{$File::Find::name} = {
198 file => $File::Find::name,
201 open(FILE, '<', $f) or die "ERROR: Can't open '$f': $!";
203 $hash->{$File::Find::name}->{md5} = $md5->addfile(*FILE)->hexdigest;
207 $hash->{$File::Find::name} = {
211 mtime => ($mtimedir)?$mtime:0,
213 file => $File::Find::name,
216 } elsif (-b $f or -c $f) { # dev
217 $hash->{$File::Find::name} = {
223 type => (-b $f)?'block':'char',
224 file => $File::Find::name,
227 } elsif (-p $f) { # named pipe
228 $hash->{$File::Find::name} = {
234 file => $File::Find::name,
237 } else { # other than file and directory
244 $hash->{$File::Find::name}->{acl} = `getfacl "$fe" 2>/dev/null`;
247 $hash->{$File::Find::name}->{attr} = `getfattr "$fe" 2>/dev/null`;