summaryrefslogtreecommitdiff
path: root/lib/File/Compare.pm
blob: a76eb1ff59363500e85769a5f3cca731e81c91af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package File::Compare;

use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $Too_Big *FROM *TO);

require Exporter;
use Carp;
use UNIVERSAL qw(isa);

$VERSION = '1.1001';
@ISA = qw(Exporter);
@EXPORT = qw(compare);
@EXPORT_OK = qw(cmp);

$Too_Big = 1024 * 1024 * 2;

sub VERSION {
    # Version of File::Compare
    return $File::Compare::VERSION;
}

sub compare {
    croak("Usage: compare( file1, file2 [, buffersize]) ")
      unless(@_ == 2 || @_ == 3);

    my $from = shift;
    my $to = shift;
    my $closefrom=0;
    my $closeto=0;
    my ($size, $fromsize, $status, $fr, $tr, $fbuf, $tbuf);
    local(*FROM, *TO);
    local($\) = '';

    croak("from undefined") unless (defined $from);
    croak("to undefined") unless (defined $to);

    if (ref($from) && (isa($from,'GLOB') || isa($from,'IO::Handle'))) {
	*FROM = *$from;
    } elsif (ref(\$from) eq 'GLOB') {
	*FROM = $from;
    } else {
	open(FROM,"<$from") or goto fail_open1;
	binmode FROM;
	$closefrom = 1;
	$fromsize = -s FROM;
    }

    if (ref($to) && (isa($to,'GLOB') || isa($to,'IO::Handle'))) {
	*TO = *$to;
    } elsif (ref(\$to) eq 'GLOB') {
	*TO = $to;
    } else {
	open(TO,"<$to") or goto fail_open2;
	binmode TO;
	$closeto = 1;
    }

    if ($closefrom && $closeto) {
	# If both are opened files we know they differ if their size differ
	goto fail_inner if $fromsize != -s TO;
    }

    if (@_) {
	$size = shift(@_) + 0;
	croak("Bad buffer size for compare: $size\n") unless ($size > 0);
    } else {
	$size = $fromsize;
	$size = 1024 if ($size < 512);
	$size = $Too_Big if ($size > $Too_Big);
    }

    $fbuf = '';
    $tbuf = '';
    while(defined($fr = read(FROM,$fbuf,$size)) && $fr > 0) {
	unless (defined($tr = read(TO,$tbuf,$fr)) and $tbuf eq $fbuf) {
            goto fail_inner;
	}
    }
    goto fail_inner if (defined($tr = read(TO,$tbuf,$size)) && $tr > 0);

    close(TO) || goto fail_open2 if $closeto;
    close(FROM) || goto fail_open1 if $closefrom;

    return 0;
    
  # All of these contortions try to preserve error messages...
  fail_inner:
    close(TO) || goto fail_open2 if $closeto;
    close(FROM) || goto fail_open1 if $closefrom;

    return 1;

  fail_open2:
    if ($closefrom) {
	$status = $!;
	$! = 0;
	close FROM;
	$! = $status unless $!;
    }
  fail_open1:
    return -1;
}

*cmp = \&compare;

1;

__END__

=head1 NAME

File::Compare - Compare files or filehandles

=head1 SYNOPSIS

  	use File::Compare;

	if (compare("file1","file2") == 0) {
	    print "They're equal\n";
	}

=head1 DESCRIPTION

The File::Compare::compare function compares the contents of two
sources, each of which can be a file or a file handle.  It is exported
from File::Compare by default.

File::Compare::cmp is a synonym for File::Compare::compare.  It is
exported from File::Compare only by request.

=head1 RETURN

File::Compare::compare return 0 if the files are equal, 1 if the
files are unequal, or -1 if an error was encountered.

=head1 AUTHOR

File::Compare was written by Nick Ing-Simmons.
Its original documentation was written by Chip Salzenberg.

=cut