File Coverage

blib/lib/File/Cmp.pm
Criterion Covered Total %
statement 63 63 100.0
branch 33 48 68.7
condition 9 16 56.2
subroutine 6 6 100.0
pod 0 1 0.0
total 111 134 82.8


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