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);
32 GetOptions("src=s" => \$src, # source directory
33 "dst=s" => \$dst, # dest directory
34 "acl" => \$acl, # acl test
35 "attr" => \$attr, # attributes test
36 "wattr" => \$wattr, # windows attributes
37 "mtime-dir" => \$mtimedir, # check mtime on directories
38 "exclude=s@" => \@exclude, # exclude some files
40 ) or pod2usage(-verbose => 1,
43 pod2usage(-verbose => 1,
48 pod2usage(-verbose => 2,
51 my $md5 = Digest::MD5->new;
55 chdir($src) or die "ERROR: Can't access to $src";
57 find(\&wanted_src, '.');
60 $src_attrib = `attrib /D /S`;
61 $src_attrib = strip_base($src_attrib, $src);
66 chdir($dst) or die "ERROR: Can't access to $dst";
68 find(\&wanted_src, '.');
71 $dest_attrib = `attrib /D /S`;
72 $dest_attrib = strip_base($dest_attrib, $dst);
74 if (lc($src_attrib) ne lc($dest_attrib)) {
76 print "diff.pl ERROR: Differences between windows attributes\n",
77 "$src_attrib\n=========\n$dest_attrib\n";
81 #print Data::Dumper::Dumper(\%src_attr);
82 #print Data::Dumper::Dumper(\%dst_attr);
84 foreach my $f (keys %src_attr)
86 if (!defined $dst_attr{$f}) {
88 print "diff.pl ERROR: Can't find $f in dst\n";
91 compare($src_attr{$f}, $dst_attr{$f});
97 foreach my $f (keys %dst_attr)
100 print "diff.pl ERROR: Can't find $f in src\n";
104 print "diff.pl ERROR: found $ret error(s)\n";
109 # convert \ to / and strip the path
112 my ($data, $path) = @_;
114 $data =~ s!\Q$path!!sig;
121 my ($f1, $f2) = ($h1->{file}, $h2->{file});
123 foreach my $k (keys %$h1) {
124 if (!exists $h2->{$k}) {
126 print "diff.pl ERROR: Can't find $k for dest $f2 ($k=$h1->{$k})\n";
128 if (!defined $h2->{$k}) {
130 print "diff.pl ERROR: $k not found in destination ", $h1->{file}, "\n";
131 print Data::Dumper::Dumper($h1, $h2);
132 } elsif ($h2->{$k} ne $h1->{$k}) {
134 my ($val1, $val2) = ($h1->{$k}, $h2->{$k});
137 (map { strftime('%F %T', localtime($_)) } ($val1, $val2));
139 print "diff.pl ERROR: src and dst $f2 differ on $k ($val1 != $val2)\n";
144 foreach my $k (keys %attr) {
146 print "diff.pl ERROR: Found $k on dst file and not on src ($k=$h2->{$k})\n";
153 if (grep ($f, @exclude)) {
157 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
158 $atime,$mtime,$ctime,$blksize,$blocks) = lstat($f);
160 my $target = readlink($f);
161 $hash->{$File::Find::name} = {
168 file => $File::Find::name,
173 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
174 $atime,$mtime,$ctime,$blksize,$blocks) = stat($f);
177 $hash->{$File::Find::name} = {
185 file => $File::Find::name,
188 open(FILE, '<', $f) or die "ERROR: Can't open '$f': $!";
190 $hash->{$File::Find::name}->{md5} = $md5->addfile(*FILE)->hexdigest;
194 $hash->{$File::Find::name} = {
198 mtime => ($mtimedir)?$mtime:0,
200 file => $File::Find::name,
203 } elsif (-b $f or -c $f) { # dev
204 $hash->{$File::Find::name} = {
210 type => (-b $f)?'block':'char',
211 file => $File::Find::name,
214 } elsif (-p $f) { # named pipe
215 $hash->{$File::Find::name} = {
221 file => $File::Find::name,
224 } else { # other than file and directory
231 $hash->{$File::Find::name}->{acl} = `getfacl "$fe" 2>/dev/null`;
234 $hash->{$File::Find::name}->{attr} = `getfattr "$fe" 2>/dev/null`;