File Coverage

blib/lib/SVN/Dump/Reader.pm
Criterion Covered Total %
statement 101 102 99.0
branch 41 44 93.1
condition 27 30 90.0
subroutine 15 15 100.0
pod 5 5 100.0
total 189 196 96.4


line stmt bran cond sub pod time code
1             package SVN::Dump::Reader;
2             $SVN::Dump::Reader::VERSION = '0.07';
3 10     10   424430 use strict;
  10         71  
  10         275  
4 10     10   47 use warnings;
  10         17  
  10         224  
5 10     10   5072 use IO::Handle;
  10         54743  
  10         415  
6 10     10   81 use Carp;
  10         18  
  10         647  
7              
8             our @ISA = qw( IO::Handle );
9              
10             # SVN::Dump elements
11 10     10   4317 use SVN::Dump::Headers;
  10         25  
  10         311  
12 10     10   4179 use SVN::Dump::Property;
  10         26  
  10         316  
13 10     10   3974 use SVN::Dump::Text;
  10         28  
  10         344  
14 10     10   4317 use SVN::Dump::Record;
  10         22  
  10         11039  
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 68     68 1 34354 my ($class, $fh, $args) = @_;
27 68 100 100     1043 croak 'SVN::Dump::Reader parameter is not a filehandle'
      100        
28             if !( $fh && ref $fh && ref($fh) eq 'GLOB' );
29 64 100       115 %{*$fh} = %{ $args || {} };
  64         231  
  64         260  
30 64         322 return bless $fh, $class;
31             }
32              
33             sub read_record {
34 740     740 1 1264 my ($fh) = @_;
35              
36 740         1621 my $record = SVN::Dump::Record->new();
37              
38             # first get the headers
39 740 100       1374 my $headers = $fh->read_header_block() or return;
40 693         1882 $record->set_headers_block( $headers );
41            
42             # get the property block
43             $record->set_property_block( $fh->read_property_block() )
44             if (
45             exists $headers->{'Prop-content-length'} and
46 693 50 66     2321 $headers->{'Prop-content-length'}
47             );
48              
49             # get the text block
50 693 100 100     1654 if ( exists $headers->{'Text-content-length'}
51             and $headers->{'Text-content-length'} )
52             {
53 97         200 my $text = $fh->read_text_block( $headers->{'Text-content-length'} );
54              
55             # verify checksums (but not in delta dumps)
56 97 100 66     168 if (${*$fh}{check_digest}
  97   66     396  
57             && ( !$headers->{'Text-delta'}
58             || $headers->{'Text-delta'} ne 'true' )
59             )
60             {
61 7         19 for my $algo ( grep { $headers->{"Text-content-$_"} } @digest ) {
  7         27  
62 7         35 my $digest = $text->digest($algo);
63             croak
64             qq{$algo checksum mismatch: got $digest, expected $headers->{"Text-content-$algo"}}
65 7 100       401 if $headers->{"Text-content-$algo"} ne $digest;
66             }
67             }
68              
69 96         265 $record->set_text_block($text);
70             }
71              
72             # some safety checks
73             croak "Inconsistent record size"
74             if ( $headers->{'Prop-content-length'} || 0 )
75             + ( $headers->{'Text-content-length'} || 0 )
76 692 100 100     3771 != ( $headers->{'Content-length'} || 0 );
      100        
      100        
77              
78             # if we have a delete record with a 'Node-kind' header
79             # we have to recurse for an included record
80 691 100 100     1998 if ( exists $headers->{'Node-action'}
      100        
81             && $headers->{'Node-action'} eq 'delete'
82             && exists $headers->{'Node-kind'} )
83             {
84 1         10 my $included = $fh->read_record();
85 1         4 $record->set_included_record( $included );
86             }
87              
88             # uuid and format record only contain headers
89 691         1458 return $record;
90             }
91              
92             sub read_header_block {
93 746     746 1 1109 my ($fh) = @_;
94              
95 746         2202 local $/ = $NL;
96              
97             # skip empty lines
98 746         965 my $line;
99 746         961 while(1) {
100 1490         3443 $line = <$fh>;
101 1490 100       3060 return if !defined $line;
102 1443         1902 chop $line;
103 1443 100       2639 last unless $line eq '';
104             }
105              
106 699         1784 my $headers = SVN::Dump::Headers->new();
107 699         925 while(1) {
108 2901         6598 my ($key, $value) = split /: /, $line, 2;
109 2901         6151 $headers->{$key} = $value;
110              
111 2901         4525 $line = <$fh>;
112 2901 100       4785 croak _eof() if !defined $line;
113 2900         3571 chop $line;
114 2900 100       4961 last if $line eq ''; # stop on empty line
115             }
116              
117 698 50       1545 croak "Empty line found instead of a header block line $."
118             if ! keys %$headers;
119              
120 698         2286 return $headers;
121             }
122              
123             sub read_property_block {
124 476     476 1 804 my ($fh) = @_;
125 476         1155 my $property = SVN::Dump::Property->new();
126              
127 476         1308 local $/ = $NL;
128 476         680 my @buffer;
129 476         569 while(1) {
130 1277         2452 my $line = <$fh>;
131 1277 100       2247 croak _eof() if !defined $line;
132 1275         1715 chop $line;
133              
134             # read a key/value pair
135 1275 100       4565 if( $line =~ /\AK (\d+)\z/ ) {
    100          
    100          
136 799         1516 my $key = $fh->_read_string( $1 );
137              
138 799         2193 $line = <$fh>;
139 799 100       1489 croak _eof() if !defined $line;
140 798         1070 chop $line;
141            
142 798 100       2409 if( $line =~ /\AV (\d+)\z/ ) {
143 797         1369 my $value = $fh->_read_string( $1 );
144              
145 797         2175 $property->set( $key => $value );
146              
147             # FIXME what happens if we see duplicate keys?
148             }
149             else {
150 1         93 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         14 $property->set( $key => undef ); # undef means deleted
159             }
160             # end of properties
161             elsif( $line =~ /\APROPS-END\z/ ) {
162 471         798 last;
163             }
164             # inconsistent data
165             else {
166 1         93 croak "Corrupted property"; # FIXME better error message
167             }
168             }
169              
170 471         1848 return $property;
171             }
172              
173             sub read_text_block {
174 98     98 1 187 my ($fh, $size) = @_;
175              
176 98         228 return SVN::Dump::Text->new( $fh->_read_string( $size ) );
177             }
178              
179             sub _read_string {
180              
181 1698     1698   3933 my ( $fh, $size ) = @_;
182              
183 1698         4159 local $/ = $NL;
184              
185 1698         2178 my $text;
186 1698         4064 my $characters_read = read( $fh, $text, $size );
187              
188 1698 50       2651 if ( defined($characters_read) ) {
189 1698 100       3080 if ( $characters_read != $size ) {
190 1         4 croak _eof();
191             };
192             } else {
193 0         0 croak $!;
194             };
195              
196 1697         2917 <$fh>; # clear trailing newline
197              
198 1697         4452 return $text;
199              
200             };
201              
202             # FIXME make this more explicit
203 5     5   607 sub _eof { return "Unexpected EOF line $.", }
204              
205             __END__