File Coverage

blib/lib/File/Cmp.pm
Criterion Covered Total %
statement 65 65 100.0
branch 41 48 85.4
condition 14 20 70.0
subroutine 7 7 100.0
pod 1 1 100.0
total 128 141 90.7


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # File::Cmp - compare two files character by character like cmp(1)
4              
5             package File::Cmp;
6              
7 1     1   235248 use 5.008000;
  1         4  
8 1     1   5 use strict;
  1         3  
  1         19  
9 1     1   4 use warnings;
  1         2  
  1         27  
10              
11 1     1   5 use Carp qw/croak/;
  1         5  
  1         41  
12 1     1   5 use Scalar::Util qw/reftype/;
  1         3  
  1         761  
13              
14             require Exporter;
15             our @ISA = qw(Exporter);
16             our @EXPORT_OK = qw/&fcmp/;
17              
18             our $VERSION = '1.09';
19              
20             # XXX 'skip' and 'limit' might be good parameters to add, to skip X
21             # initial bytes, limit work to Y bytes of data to check
22             sub fcmp {
23 13 100   13 1 7924 croak 'fcmp needs two files' if @_ < 2;
24 12         31 my @files = splice @_, 0, 2;
25 12 100 100     94 my $param = ( @_ == 1 and ref $_[0] eq 'HASH' ) ? $_[0] : {@_};
26              
27 12 100       39 $param->{sizecheck} = 1 unless exists $param->{sizecheck};
28 12 100       25 $param->{sizecheck} = 0 if exists $param->{tells};
29              
30 12 100       24 if ( $param->{fscheck} ) {
31 4         6 my @statbuf;
32 4         7 for my $f (@files) {
33             # stat has the handy property of chasing symlinks for us
34 7 100       217 my @devino = ( stat $f )[ 0, 1 ] or croak "could not stat: $!";
35 6         19 push @statbuf, \@devino;
36             }
37 3 100 66     19 if ( $statbuf[0][0] == $statbuf[1][0]
38             and $statbuf[0][1] == $statbuf[1][1] ) {
39 2 100       8 ${ $param->{reason} } = 'fscheck' if exists $param->{reason};
  1         2  
40 2         13 return 1; # assume files identical as both dev and inode match
41             }
42             }
43              
44             # The files are probably not identical if they differ in size;
45             # however, offer means to turn this check off if -s for some reason is
46             # incorrect (or if 'tells' is on so we need to find roughly where the
47             # difference is in the files).
48 9 100 50     131 if ( $param->{sizecheck}
      50        
      100        
49             and ( ( -s $files[0] ) // -1 ) != ( ( -s $files[1] ) // -2 ) ) {
50 4 100       14 ${ $param->{reason} } = 'size' if exists $param->{reason};
  2         8  
51 4         32 return 0;
52             }
53              
54 5         11 my @fhs;
55 5         7 for my $f (@files) {
56 8 100       27 if ( !defined reftype $f) {
57 7 100       396 open my $fh, '<', $f or croak "could not open $f: $!";
58 6         26 push @fhs, $fh;
59             } else {
60             # Assume is a GLOB or something can readline on, XXX might want to
61             # better check this
62 1         4 push @fhs, $f;
63             }
64 7 100       21 if ( exists $param->{binmode} ) {
65 1 100   1   7 binmode $fhs[-1], $param->{binmode} or croak "binmode failed: $!";
  1         2  
  1         6  
  3         57  
66             }
67             }
68              
69 3 50       8 local $/ = $param->{RS} if exists $param->{RS};
70              
71 3         4 while (1) {
72 18         100 my $eof1 = eof $fhs[0];
73 18         94 my $eof2 = eof $fhs[1];
74             # Done if both files are at EOF; otherwise assume they differ if one
75             # completes before the other (this second case would normally be
76             # optimized away by the -s test, above).
77 18 100 66     38 last if $eof1 and $eof2;
78 17 100 50     48 if ( $eof1 xor $eof2 ) {
79 1 50       9 ${ $param->{reason} } = 'eof' if exists $param->{reason};
  1         4  
80 1         4 @{ $param->{tells} } = ( tell $fhs[0], tell $fhs[1] )
81 1 50       8 if exists $param->{tells};
82 1         29 return 0;
83             }
84              
85 16         30 my $this = readline $fhs[0];
86 16 50       24 croak "error reading from first file: $!" if !defined $this;
87 16         33 my $that = readline $fhs[1];
88 16 50       29 croak "error reading from second file: $!" if !defined $that;
89              
90 16 100       30 if ( $this ne $that ) {
91 1         3 @{ $param->{tells} } = ( tell $fhs[0], tell $fhs[1] )
92 1 50       8 if exists $param->{tells};
93 1 50       5 ${ $param->{reason} } = 'diff' if exists $param->{reason};
  1         4  
94 1         28 return 0;
95             }
96             }
97              
98 1         25 return 1; # assume files identical if get this far
99             }
100              
101             1;
102             __END__