File Coverage

blib/lib/File/DirCompare.pm
Criterion Covered Total %
statement 64 65 98.4
branch 40 52 76.9
condition 15 21 71.4
subroutine 11 11 100.0
pod 0 2 0.0
total 130 151 86.0


line stmt bran cond sub pod time code
1             package File::DirCompare;
2              
3 8     8   282427 use 5.005;
  8         34  
  8         337  
4 8     8   79 use strict;
  8         15  
  8         245  
5              
6 8     8   46 use File::Basename;
  8         18  
  8         694  
7 8     8   46 use File::Spec::Functions;
  8         14  
  8         858  
8 8     8   9644 use File::Compare ();
  8         16311  
  8         195  
9 8     8   55 use File::Glob qw(bsd_glob);
  8         15  
  8         1413  
10 8     8   64 use Carp;
  8         16  
  8         708  
11              
12 8     8   48 use vars qw($VERSION);
  8         16  
  8         7290  
13              
14             $VERSION = '0.7';
15              
16             # ----------------------------------------------------------------------------
17             # Private methods
18              
19             sub _dir_compare
20             {
21 11     11   26 my $self = shift;
22 11         28 my ($dir1, $dir2, $sub, $opts) = @_;
23              
24             # Glob $dir1 and $dir2
25 11         21 my (%d1, %d2);
26 11         1770 $d1{basename $_} = 1 foreach bsd_glob(catfile($dir1, ".*"));
27 11         2236 $d1{basename $_} = 1 foreach bsd_glob(catfile($dir1, "*"));
28 11         2426 $d2{basename $_} = 1 foreach bsd_glob(catfile($dir2, ".*"));
29 11         2810 $d2{basename $_} = 1 foreach bsd_glob(catfile($dir2, "*"));
30              
31             # Prune dot dirs
32 11 50       81 delete $d1{curdir()} if $d1{curdir()};
33 11 50       51 delete $d1{updir()} if $d1{updir()};
34 11 50       59 delete $d2{curdir()} if $d2{curdir()};
35 11 50       41 delete $d2{updir()} if $d2{updir()};
36              
37             # Setup cmp and matches subs
38 11 100 66     81 my $cmp = $opts->{cmp} && ref $opts->{cmp} eq 'CODE' ? $opts->{cmp} : \&File::Compare::compare;
39 11 100 66     60 my $matches = $opts->{matches} if $opts->{matches} && ref $opts->{matches} eq 'CODE';
40              
41             # Iterate over sorted and uniquified file list
42 11         18 my %u;
43 11 100       125 for my $f (map { $u{$_}++ == 0 ? $_ : () } sort(keys(%d1), keys(%d2))) {
  107         284  
44 69         3869 my $f1 = catfile($dir1, $f);
45 69         245 my $f2 = catfile($dir2, $f);
46             # Only in $dir1
47 69 100       238 if (! $d2{$f}) {
    100          
48 13 100       691 $sub->($f1, undef) unless $opts->{ignore_unique};
49             }
50             # Only in $dir2
51             elsif (! $d1{$f}) {
52 18 100       108 $sub->(undef, $f2) unless $opts->{ignore_unique};
53             }
54             # Item exists in both directories
55             else {
56             # Both symlinks
57 38 100 66     3077 if (-l $f1 && -l $f2) {
    50 33        
    100 100        
    100 100        
58 4 50       43 my $t1 = readlink $f1 or croak "Cannot read symlink $f1: $!";
59 4 50       42 my $t2 = readlink $f2 or croak "Cannot read symlink $f2: $!";
60 4 100       18 $sub->($f1, $f2) if $t1 ne $t2;
61             }
62             # One symlink (i.e. different)
63             elsif (-l $f1 || -l $f2) {
64 0         0 $sub->($f1, $f2);
65             }
66             # Both directories
67             elsif (-d $f1 && -d $f2) {
68 3         31 $self->_dir_compare($f1, $f2, $sub, $opts);
69             }
70             # One directory (i.e. different)
71             elsif (-d $f1 || -d $f2) {
72 12         756 $sub->($f1, $f2);
73             }
74             # Both files - check if different
75             else {
76 19 100       99 if ($opts->{ignore_cmp}) {
    100          
    100          
77 2         37 $sub->($f1, $f2);
78             }
79             elsif ($cmp->($f1, $f2) != 0) {
80 9         3266 $sub->($f1, $f2);
81             }
82             elsif ($matches) {
83 1         4095 $matches->($f1, $f2);
84             }
85             }
86             }
87             }
88             }
89              
90             # ----------------------------------------------------------------------------
91             # Public methods
92              
93             sub compare
94             {
95 8     8 0 2851 my $self = shift;
96 8         26 my ($dir1, $dir2, $sub, $opts) = @_;
97            
98 8 50       203 croak "Not a directory: '$dir1'" unless -d $dir1;
99 8 50       129 croak "Not a directory: '$dir2'" unless -d $dir2;
100 8 50       63 croak "Not a subroutine: '$sub'" unless ref $sub eq 'CODE';
101 8 50 66     63 croak "Not a hashref: '$opts'" if $opts && ref $opts ne 'HASH';
102              
103 8 50       59 $self = $self->new unless ref $self;
104 8         46 $self->_dir_compare(@_);
105             }
106              
107             # ----------------------------------------------------------------------------
108             # Constructors
109              
110 8     8 0 36 sub new { bless {}, shift }
111              
112             # ----------------------------------------------------------------------------
113              
114             1;
115              
116             __END__