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   19141 use 5.008000;
  1         4  
  1         50  
8 1     1   7 use strict;
  1         1  
  1         36  
9 1     1   4 use warnings;
  1         13  
  1         30  
10              
11 1     1   3 use Carp qw/croak/;
  1         1  
  1         68  
12 1     1   3 use Scalar::Util qw/reftype/;
  1         2  
  1         639  
13              
14             require Exporter;
15             our @ISA = qw(Exporter);
16             our @EXPORT_OK = qw/&fcmp/;
17              
18             our $VERSION = '1.07';
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 22 croak 'fcmp needs two files' if @_ < 2;
24 6         15 my @files = splice @_, 0, 2;
25 6 50 33     26 my $param = ( @_ == 1 and ref $_[0] eq 'HASH' ) ? $_[0] : {@_};
26              
27 6 100       14 $param->{sizecheck} = 1 unless exists $param->{sizecheck};
28 6 100       11 $param->{sizecheck} = 0 if exists $param->{tells};
29              
30 6 100       10 if ( $param->{fscheck} ) {
31 1         1 my @statbuf;
32 1         2 for my $f (@files) {
33             # stat has the handy property of chasing symlinks for us
34 2 50       20 my @devino = ( stat $f )[ 0, 1 ] or croak "could not stat: $!";
35 2         4 push @statbuf, \@devino;
36             }
37 1 50 33     9 if ( $statbuf[0][0] == $statbuf[1][0]
38             and $statbuf[0][1] == $statbuf[1][1] ) {
39 1 50       3 ${ $param->{reason} } = 'fscheck' if exists $param->{reason};
  1         3  
40 1         5 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     68 if ( $param->{sizecheck} and -s $files[0] != -s $files[1] ) {
49 2 50       6 ${ $param->{reason} } = 'size' if exists $param->{reason};
  2         3  
50 2         9 return 0;
51             }
52              
53 3         4 my @fhs;
54 3         4 for my $f (@files) {
55 6 100       21 if ( !defined reftype $f) {
56 5 50       106 open my $fh, '<', $f or croak "could not open $f: $!";
57 5         10 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       13 if ( exists $param->{binmode} ) {
64 2 50       11 binmode $fhs[-1], $param->{binmode} or croak "binmode failed: $!";
65             }
66             }
67              
68 3 50       10 local $/ = $param->{RS} if exists $param->{RS};
69              
70 3         4 while (1) {
71 17         45 my $eof1 = eof $fhs[0];
72 17         30 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     31 last if $eof1 and $eof2;
77 16 100 50     46 if ( $eof1 xor $eof2 ) {
78 1 50       4 ${ $param->{reason} } = 'eof' if exists $param->{reason};
  1         2  
79 1 50       7 @{ $param->{tells} } = ( tell $fhs[0], tell $fhs[1] )
  1         2  
80             if exists $param->{tells};
81 1         14 return 0;
82             }
83              
84 15         15 my $this = readline $fhs[0];
85 15 50       20 croak "error reading from first file: $!" if !defined $this;
86 15         19 my $that = readline $fhs[1];
87 15 50       20 croak "error reading from second file: $!" if !defined $that;
88              
89 15 100       21 if ( $this ne $that ) {
90 1 50       3 @{ $param->{tells} } = ( tell $fhs[0], tell $fhs[1] )
  1         3  
91             if exists $param->{tells};
92 1 50       3 ${ $param->{reason} } = 'diff' if exists $param->{reason};
  1         3  
93 1         13 return 0;
94             }
95             }
96              
97 1         13 return 1; # assume files identical if get this far
98             }
99              
100             1;
101             __END__