This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
new perldelta
[perl5.git] / lib / File / Compare.pm
CommitLineData
abf0a31f 1package File::Compare 1.1008;
5f05dabc 2
59f2a7f5 3use v5.12;
b395063c 4use warnings;
5f05dabc 5
59f2a7f5 6use Exporter 'import';
5f05dabc 7
59f2a7f5
JR
8our @EXPORT = qw(compare);
9our @EXPORT_OK = qw(cmp compare_text);
5f05dabc 10
59f2a7f5 11our $Too_Big = 1024 * 1024 * 2;
5f05dabc 12
8878f897
T
13sub croak {
14 require Carp;
15 goto &Carp::croak;
16}
17
5f05dabc 18sub compare {
19 croak("Usage: compare( file1, file2 [, buffersize]) ")
20 unless(@_ == 2 || @_ == 3);
21
abf0a31f 22 my ($from, $to, $size) = @_;
167e09eb 23 my $text_mode = defined($size) && (ref($size) eq 'CODE' || $size < 0);
3724d6f4 24
abf0a31f 25 my ($fromsize, $closefrom, $closeto);
3724d6f4 26 local (*FROM, *TO);
5f05dabc 27
28 croak("from undefined") unless (defined $from);
29 croak("to undefined") unless (defined $to);
30
d704f39a 31 if (ref($from) &&
abf0a31f 32 (UNIVERSAL::isa($from, 'GLOB') || UNIVERSAL::isa($from, 'IO::Handle'))) {
5f05dabc 33 *FROM = *$from;
34 } elsif (ref(\$from) eq 'GLOB') {
35 *FROM = $from;
36 } else {
abf0a31f 37 open(FROM, '<', $from) or goto fail_open1;
3724d6f4
JD
38 unless ($text_mode) {
39 binmode FROM;
40 $fromsize = -s FROM;
41 }
5f05dabc 42 $closefrom = 1;
43 }
44
d704f39a 45 if (ref($to) &&
abf0a31f 46 (UNIVERSAL::isa($to, 'GLOB') || UNIVERSAL::isa($to, 'IO::Handle'))) {
5f05dabc 47 *TO = *$to;
48 } elsif (ref(\$to) eq 'GLOB') {
49 *TO = $to;
50 } else {
abf0a31f 51 open(TO, '<', $to) or goto fail_open2;
3724d6f4 52 binmode TO unless $text_mode;
5f05dabc 53 $closeto = 1;
54 }
55
3724d6f4 56 if (!$text_mode && $closefrom && $closeto) {
387d8d95
CS
57 # If both are opened files we know they differ if their size differ
58 goto fail_inner if $fromsize != -s TO;
59 }
60
3724d6f4
JD
61 if ($text_mode) {
62 local $/ = "\n";
abf0a31f 63 my ($fline, $tline);
3724d6f4 64 while (defined($fline = <FROM>)) {
167e09eb
JD
65 goto fail_inner unless defined($tline = <TO>);
66 if (ref $size) {
67 # $size contains ref to comparison function
68 goto fail_inner if &$size($fline, $tline);
69 } else {
70 goto fail_inner if $fline ne $tline;
3724d6f4
JD
71 }
72 }
73 goto fail_inner if defined($tline = <TO>);
5f05dabc 74 }
3724d6f4
JD
75 else {
76 unless (defined($size) && $size > 0) {
5c99b74d 77 $size = $fromsize || -s TO || 0;
3724d6f4
JD
78 $size = 1024 if $size < 512;
79 $size = $Too_Big if $size > $Too_Big;
80 }
5f05dabc 81
abf0a31f 82 my ($fr, $tr, $fbuf, $tbuf);
3724d6f4 83 $fbuf = $tbuf = '';
abf0a31f
EA
84 while(defined($fr = read(FROM, $fbuf, $size)) && $fr > 0) {
85 unless (defined($tr = read(TO, $tbuf, $fr)) && $tbuf eq $fbuf) {
3724d6f4
JD
86 goto fail_inner;
87 }
5f05dabc 88 }
abf0a31f 89 goto fail_inner if defined($tr = read(TO, $tbuf, $size)) && $tr > 0;
5f05dabc 90 }
5f05dabc 91
92 close(TO) || goto fail_open2 if $closeto;
93 close(FROM) || goto fail_open1 if $closefrom;
94
95 return 0;
96
97 # All of these contortions try to preserve error messages...
98 fail_inner:
99 close(TO) || goto fail_open2 if $closeto;
100 close(FROM) || goto fail_open1 if $closefrom;
101
102 return 1;
103
104 fail_open2:
105 if ($closefrom) {
3724d6f4 106 my $status = $!;
5f05dabc 107 $! = 0;
108 close FROM;
109 $! = $status unless $!;
110 }
111 fail_open1:
112 return -1;
113}
114
17f410f9 115sub cmp;
5f05dabc 116*cmp = \&compare;
117
167e09eb 118sub compare_text {
abf0a31f 119 my ($from, $to, $cmp) = @_;
167e09eb
JD
120 croak("Usage: compare_text( file1, file2 [, cmp-function])")
121 unless @_ == 2 || @_ == 3;
122 croak("Third arg to compare_text() function must be a code reference")
123 if @_ == 3 && ref($cmp) ne 'CODE';
124
125 # Using a negative buffer size puts compare into text_mode too
59f2a7f5 126 compare($from, $to, $cmp // -1);
167e09eb 127}
3724d6f4 128
5f05dabc 1291;
130
131__END__
132
133=head1 NAME
134
135File::Compare - Compare files or filehandles
136
137=head1 SYNOPSIS
138
139 use File::Compare;
140
abf0a31f 141 if (compare("file1", "file2") == 0) {
5f05dabc 142 print "They're equal\n";
143 }
144
145=head1 DESCRIPTION
146
abf0a31f 147The C<File::Compare::compare> function compares the contents of two
5f05dabc 148sources, each of which can be a file or a file handle. It is exported
abf0a31f 149from C<File::Compare> by default.
5f05dabc 150
abf0a31f
EA
151C<File::Compare::cmp> is a synonym for C<File::Compare::compare>. It is
152exported from C<File::Compare> only by request.
5f05dabc 153
abf0a31f
EA
154C<File::Compare::compare_text> does a line by line comparison of the two
155files. It stops as soon as a difference is detected. C<compare_text()>
167e09eb 156accepts an optional third argument: This must be a CODE reference to
abf0a31f 157a line comparison function, which returns C<0> when both lines are considered
167e09eb
JD
158equal. For example:
159
160 compare_text($file1, $file2)
161
162is basically equivalent to
163
164 compare_text($file1, $file2, sub {$_[0] ne $_[1]} )
3724d6f4 165
5f05dabc 166=head1 RETURN
167
abf0a31f
EA
168C<File::Compare::compare> and its sibling functions return C<0> if the files
169are equal, C<1> if the files are unequal, or C<-1> if an error was encountered.
5f05dabc 170
171=head1 AUTHOR
172
abf0a31f 173C<File::Compare> was written by Nick Ing-Simmons.
5f05dabc 174Its original documentation was written by Chip Salzenberg.