File Coverage

blib/lib/SVN/Dump/Reader.pm
Criterion Covered Total %
statement 101 102 99.0
branch 42 44 95.4
condition 27 30 90.0
subroutine 15 15 100.0
pod 5 5 100.0
total 190 196 96.9


line stmt bran cond sub pod time code
1             package SVN::Dump::Reader;
2              
3 11     11   250899 use strict;
  11         22  
  11         520  
4 11     11   64 use warnings;
  11         23  
  11         355  
5 11     11   13745 use IO::Handle;
  11         92856  
  11         614  
6 11     11   94 use Carp;
  11         23  
  11         862  
7              
8             our @ISA = qw( IO::Handle );
9              
10             # SVN::Dump elements
11 11     11   8067 use SVN::Dump::Headers;
  11         33  
  11         296  
12 11     11   6048 use SVN::Dump::Property;
  11         26  
  11         300  
13 11     11   6526 use SVN::Dump::Text;
  11         29  
  11         306  
14 11     11   7506 use SVN::Dump::Record;
  11         31  
  11         14999  
15              
16             # some useful definitions
17             my $NL = "\012";
18              
19             # prepare the digest checkers
20             my @digest = grep {
21             eval { require Digest; Digest->new(uc) }
22             } qw( md5 sha1 );
23              
24             # the object is a filehandle
25             sub new {
26 67     67 1 56662 my ($class, $fh, $args) = @_;
27 67 100 100     1604 croak 'SVN::Dump::Reader parameter is not a filehandle'
      100        
28             if !( $fh && ref $fh && ref($fh) eq 'GLOB' );
29 63 100       116 %{*$fh} = %{ $args || {} };
  63         233  
  63         503  
30 63         353 return bless $fh, $class;
31             }
32              
33             sub read_record {
34 725     725 1 1271 my ($fh) = @_;
35              
36 725         2207 my $record = SVN::Dump::Record->new();
37              
38             # first get the headers
39 725 100       1936 my $headers = $fh->read_header_block() or return;
40 679         3045 $record->set_headers_block( $headers );
41            
42             # get the property block
43 679 100 66     6142 $record->set_property_block( $fh->read_property_block() )
44             if (
45             exists $headers->{'Prop-content-length'} and
46             $headers->{'Prop-content-length'}
47             );
48              
49             # get the text block
50 679 100 100     2378 if ( exists $headers->{'Text-content-length'}
51             and $headers->{'Text-content-length'} )
52             {
53 96         294 my $text = $fh->read_text_block( $headers->{'Text-content-length'} );
54              
55             # verify checksums (but not in delta dumps)
56 96 100 66     165 if (${*$fh}{check_digest}
  96   66     508  
57             && ( !$headers->{'Text-delta'}
58             || $headers->{'Text-delta'} ne 'true' )
59             )
60             {
61 7         13 for my $algo ( grep { $headers->{"Text-content-$_"} } @digest ) {
  7         48  
62 7         30 my $digest = $text->digest($algo);
63 7 100       550 croak
64             qq{$algo checksum mismatch: got $digest, expected $headers->{"Text-content-$algo"}}
65             if $headers->{"Text-content-$algo"} ne $digest;
66             }
67             }
68              
69 95         492 $record->set_text_block($text);
70             }
71              
72             # some safety checks
73 678 100 100     11826 croak "Inconsistent record size"
      100        
      100        
74             if ( $headers->{'Prop-content-length'} || 0 )
75             + ( $headers->{'Text-content-length'} || 0 )
76             != ( $headers->{'Content-length'} || 0 );
77              
78             # if we have a delete record with a 'Node-kind' header
79             # we have to recurse for an included record
80 677 100 100     4711 if ( exists $headers->{'Node-action'}
      100        
81             && $headers->{'Node-action'} eq 'delete'
82             && exists $headers->{'Node-kind'} )
83             {
84 1         4 my $included = $fh->read_record();
85 1         4 $record->set_included_record( $included );
86             }
87              
88             # uuid and format record only contain headers
89 677         2004 return $record;
90             }
91              
92             sub read_header_block {
93 731     731 1 960 my ($fh) = @_;
94              
95 731         3176 local $/ = $NL;
96              
97             # skip empty lines
98 731         875 my $line;
99 731         4014 while(1) {
100 1459         3998 $line = <$fh>;
101 1459 100       4320 return if !defined $line;
102 1413         1641 chop $line;
103 1413 100       3701 last unless $line eq '';
104             }
105              
106 685         2335 my $headers = SVN::Dump::Headers->new();
107 685         931 while(1) {
108 2848         7908 my ($key, $value) = split /: /, $line, 2;
109 2848         6460 $headers->{$key} = $value;
110              
111 2848         5876 $line = <$fh>;
112 2848 100       6608 croak _eof() if !defined $line;
113 2847         3821 chop $line;
114 2847 100       9332 last if $line eq ''; # stop on empty line
115             }
116              
117 684 50       1896 croak "Empty line found instead of a header block line $."
118             if ! keys %$headers;
119              
120 684         3580 return $headers;
121             }
122              
123             sub read_property_block {
124 466     466 1 710 my ($fh) = @_;
125 466         1563 my $property = SVN::Dump::Property->new();
126              
127 466         1798 local $/ = $NL;
128 466         559 my @buffer;
129 466         555 while(1) {
130 1252         2295 my $line = <$fh>;
131 1252 100       3131 croak _eof() if !defined $line;
132 1250         1793 chop $line;
133              
134             # read a key/value pair
135 1250 100       6155 if( $line =~ /\AK (\d+)\z/ ) {
    100          
    100          
136 784         3525 my $key = $fh->_read_string( $1 );
137              
138 784         3769 $line = <$fh>;
139 784 100       2038 croak _eof() if !defined $line;
140 783         988 chop $line;
141            
142 783 100       2794 if( $line =~ /\AV (\d+)\z/ ) {
143 782         1588 my $value = $fh->_read_string( $1 );
144              
145 782         2944 $property->set( $key => $value );
146              
147             # FIXME what happens if we see duplicate keys?
148             }
149             else {
150 1         157 croak "Corrupted property"; # FIXME better error message
151             }
152             }
153             # or a deleted key (only with fs-format-version >= 3)
154             # FIXME shall we fail if fs-format-version < 3?
155             elsif( $line =~ /\AD (\d+)\z/ ) {
156 4         17 my $key = $fh->_read_string( $1 );
157            
158 4         19 $property->set( $key => undef ); # undef means deleted
159             }
160             # end of properties
161             elsif( $line =~ /\APROPS-END\z/ ) {
162 461         744 last;
163             }
164             # inconsistent data
165             else {
166 1         235 croak "Corrupted property"; # FIXME better error message
167             }
168             }
169              
170 461         10659 return $property;
171             }
172              
173             sub read_text_block {
174 97     97 1 171 my ($fh, $size) = @_;
175              
176 97         224 return SVN::Dump::Text->new( $fh->_read_string( $size ) );
177             }
178              
179             sub _read_string {
180              
181 1667     1667   3113 my ( $fh, $size ) = @_;
182              
183 1667         4781 local $/ = $NL;
184              
185 1667         1845 my $text;
186 1667         4107 my $characters_read = read( $fh, $text, $size );
187              
188 1667 50       3064 if ( defined($characters_read) ) {
189 1667 100       5793 if ( $characters_read != $size ) {
190 1         4 croak _eof();
191             };
192             } else {
193 0         0 croak $!;
194             };
195              
196 1666         3818 <$fh>; # clear trailing newline
197              
198 1666         7253 return $text;
199              
200             };
201              
202             # FIXME make this more explicit
203 5     5   3476 sub _eof { return "Unexpected EOF line $.", }
204              
205             __END__