File Coverage

blib/lib/Directory/Diff.pm
Criterion Covered Total %
statement 84 105 80.0
branch 35 58 60.3
condition 7 15 46.6
subroutine 9 9 100.0
pod 4 4 100.0
total 139 191 72.7


line stmt bran cond sub pod time code
1             package Directory::Diff;
2             require Exporter;
3             @ISA = qw(Exporter);
4             @EXPORT_OK = qw/ls_dir get_only get_diff directory_diff/;
5             %EXPORT_TAGS = (
6             all => \@EXPORT_OK,
7             );
8 2     2   91015 use warnings;
  2         3  
  2         98  
9 2     2   9 use strict;
  2         2  
  2         84  
10             our $VERSION = '0.09';
11 2     2   12 use Carp qw/carp croak/;
  2         2  
  2         85  
12 2     2   6 use Cwd 'getcwd';
  2         4  
  2         99  
13 2     2   784 use File::Compare 'compare';
  2         1544  
  2         1832  
14              
15             sub ls_dir
16             {
17 12     12 1 6363 my ($dir, $verbose) = @_;
18 12 50 33     136 if (! defined $dir || ! -d $dir) {
19 0         0 croak "No such directory '$dir'";
20             }
21 12         16 my %ls;
22 12 50       22 if (! wantarray) {
23 0         0 die "bad call with ignored value";
24             }
25 12         72 my $original_dir = getcwd ();
26 12 50       59 chdir ($dir) or die "chdir ($dir) failed: $!";
27 12 50       254 opendir (my $dh, ".") or die "opendir for $dir failed: $!";
28 12         189 my @files = readdir ($dh);
29 12         26 for my $file (@files) {
30 40 100 100     155 if ($file eq '.' || $file eq '..') {
31 24         32 next;
32             }
33 16 100       132 if (-f $file) {
    50          
34 12         36 $ls{"$file"} = 1;
35             }
36             elsif (-d $file) {
37 4         12 my %subdir = ls_dir ($file, $verbose);
38 4         11 for my $subdir_file (keys %subdir) {
39 4         11 $ls{"$file/$subdir_file"} = 1;
40             }
41 4         14 $ls{"$file/"} = 1;
42             }
43             else {
44 0         0 warn "Skipping unknown type of file $file.\n";
45             }
46             }
47 12 50       84 closedir ($dh) or die "closedir for $dir failed: $!";
48 12 50       72 chdir ($original_dir) or die "chdir for $original_dir failed: $!";
49 12 50       24 if ($verbose) {
50 0         0 for my $k (keys %ls) {
51 0         0 print "$k $ls{$k}\n";
52             }
53             }
54 12         71 return %ls;
55             }
56              
57             sub get_only
58             {
59 5     5 1 146974 my ($ls_dir1_ref, $ls_dir2_ref, $verbose) = @_;
60              
61 5 50 33     54 if (ref ($ls_dir1_ref) ne "HASH" ||
62             ref ($ls_dir2_ref) ne "HASH") {
63 0         0 croak "get_only requires hash references as arguments";
64             }
65 5         9 my %only;
66              
67             # d1e = directory one entry
68            
69 5         17 for my $d1e (keys %$ls_dir1_ref) {
70 13 100       53 if (! $ls_dir2_ref->{$d1e}) {
71 5         10 $only{$d1e} = 1;
72 5 50       13 if ($verbose) {
73 0         0 print "$d1e is only in first directory.\n";
74             }
75             }
76             }
77 5 50       16 if (! wantarray) {
78 0         0 croak "bad call";
79             }
80 5         16 return %only;
81             }
82              
83             sub get_diff
84             {
85 4     4 1 19 my ($dir1, $ls_dir1_ref, $dir2, $ls_dir2_ref) = @_;
86 4 50 33     31 if (ref ($ls_dir1_ref) ne "HASH" ||
87             ref ($ls_dir2_ref) ne "HASH") {
88 0         0 croak "get_diff requires hash references as arguments 2 and 4";
89             }
90 4         4 my %different;
91 4         11 for my $file (keys %$ls_dir1_ref) {
92 8         159 my $d1file = "$dir1/$file";
93 8 50       16 if ($ls_dir2_ref->{$file}) {
94 8 100       96 if (! -f $d1file) {
95             # croak "Bad file / directory combination $d1file";
96 2         6 next;
97             }
98 6         11 my $d2file = "$dir2/$file";
99 6 100       18 if (compare ($d1file, $d2file) != 0) {
100 4         442 $different{$file} = 1;
101             }
102             }
103             }
104 4 50       139 if (! wantarray) {
105 0         0 croak "Bad call";
106             }
107 4         19 return %different;
108             }
109              
110             sub directory_diff
111             {
112 2     2 1 579 my ($dir1, $dir2, $callback_ref, $verbose) = @_;
113 2 50 33     20 if (! $dir1 || ! $dir2) {
114 0         0 croak "directory_diff requires two directory names";
115             }
116 2 50       43 if (! -d $dir1) {
117 0         0 croak "directory_diff: first directory '$dir1' does not exist";
118             }
119 2 50       22 if (! -d $dir2) {
120 0         0 croak "directory_diff: second directory '$dir2' does not exist";
121             }
122 2 50       7 if ($verbose) {
123 0         0 print "Directory diff of $dir1 and $dir2 in progress ...\n";
124             }
125 2 50       8 if (! $callback_ref) {
126 0         0 croak "directory_diff: no callbacks supplied";
127             }
128 2 50       8 if (ref $callback_ref ne "HASH") {
129 0         0 croak "directory_diff: callback not hash reference";
130             }
131 2         11 my %ls_dir1 = ls_dir ($dir1, $verbose);
132 2         7 my %ls_dir2 = ls_dir ($dir2, $verbose);
133             # Data to pass to called back functions.
134 2         6 my $data = $callback_ref->{data};
135             # Call back a function on each file which is only in directory 1.
136 2         6 my $d1cb = $callback_ref->{dir1_only};
137 2 100       19 if ($d1cb) {
138             # Files which are only in directory 1.
139 1         6 my %dir1_only = get_only (\%ls_dir1, \%ls_dir2, $verbose);
140 1         5 for my $file (keys %dir1_only) {
141 0         0 &{$d1cb} ($data, $dir1, $file, $verbose);
  0         0  
142             }
143             }
144             # Call back a function on each file which is only in directory 2.
145 2         6 my $d2cb = $callback_ref->{dir2_only};
146 2 50       7 if ($d2cb) {
147             # Files which are only in directory 2.
148 2         7 my %dir2_only = get_only (\%ls_dir2, \%ls_dir1, $verbose);
149 2         6 for my $file (keys %dir2_only) {
150 0         0 &{$d2cb} ($data, $dir2, $file, $verbose);
  0         0  
151             }
152             }
153             # Call back a function on each file which is in both directories
154             # but different.
155 2         4 my $diff_cb = $callback_ref->{diff};
156 2 50       11 if ($diff_cb) {
157             # Files which are in both directories but are different.
158 2         8 my %diff_files = get_diff ($dir1, \%ls_dir1, $dir2, \%ls_dir2, $verbose);
159 2         10 for my $file (keys %diff_files) {
160 2         4 &{$diff_cb} ($data, $dir1, $dir2, $file, $verbose);
  2         8  
161             }
162             }
163 2 50       13 if (defined wantarray) {
164 0         0 carp "directory_diff does not return a meaningful value";
165             }
166 2         7 return;
167             }
168              
169             1;
170