File Coverage

blib/lib/SVN/Dump/Reader.pm
Criterion Covered Total %
statement 102 103 99.0
branch 43 46 93.4
condition 27 30 90.0
subroutine 15 15 100.0
pod 5 5 100.0
total 192 199 96.4


line stmt bran cond sub pod time code
1             package SVN::Dump::Reader;
2             $SVN::Dump::Reader::VERSION = '0.08';
3 10     10   364940 use strict;
  10         58  
  10         232  
4 10     10   43 use warnings;
  10         13  
  10         186  
5 10     10   4231 use IO::Handle;
  10         48729  
  10         366  
6 10     10   66 use Carp;
  10         15  
  10         548  
7              
8             our @ISA = qw( IO::Handle );
9              
10             # SVN::Dump elements
11 10     10   3948 use SVN::Dump::Headers;
  10         21  
  10         263  
12 10     10   3743 use SVN::Dump::Property;
  10         22  
  10         278  
13 10     10   3639 use SVN::Dump::Text;
  10         21  
  10         293  
14 10     10   3935 use SVN::Dump::Record;
  10         21  
  10         9710  
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 28141 my ($class, $fh, $args) = @_;
27             croak 'SVN::Dump::Reader parameter is not a filehandle'
28 68 100 100     105 unless eval { $fh && ref $fh && $fh->can('getline') && $fh->can('read') };
  68 100 100     1201  
29 64 100       106 %{*$fh} = %{ $args || {} };
  64         200  
  64         203  
30 64         224 return bless $fh, $class;
31             }
32              
33             sub read_record {
34 740     740 1 1002 my ($fh) = @_;
35              
36 740         1415 my $record = SVN::Dump::Record->new();
37              
38             # first get the headers
39 740 100       1069 my $headers = $fh->read_header_block() or return;
40 693         1656 $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     1992 $headers->{'Prop-content-length'}
47             );
48              
49             # get the text block
50 693 100 100     1325 if ( exists $headers->{'Text-content-length'}
51             and $headers->{'Text-content-length'} )
52             {
53 97         161 my $text = $fh->read_text_block( $headers->{'Text-content-length'} );
54              
55             # verify checksums (but not in delta dumps)
56 97 100 66     145 if (${*$fh}{check_digest}
  97   66     329  
57             && ( !$headers->{'Text-delta'}
58             || $headers->{'Text-delta'} ne 'true' )
59             )
60             {
61 7         13 for my $algo ( grep { $headers->{"Text-content-$_"} } @digest ) {
  7         28  
62 7         14 my $digest = $text->digest($algo);
63             croak
64             qq{$algo checksum mismatch: got $digest, expected $headers->{"Text-content-$algo"}}
65 7 100       373 if $headers->{"Text-content-$algo"} ne $digest;
66             }
67             }
68              
69 96         233 $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     3053 != ( $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     1569 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         2 $record->set_included_record( $included );
86             }
87              
88             # uuid and format record only contain headers
89 691         1226 return $record;
90             }
91              
92             sub read_header_block {
93 746     746 1 934 my ($fh) = @_;
94              
95 746         1879 local $/ = $NL;
96              
97             # skip empty lines
98 746         788 my $line;
99 746         798 while(1) {
100 1490         2903 $line = <$fh>;
101 1490 100       2505 return if !defined $line;
102 1443         1622 chop $line;
103 1443 100       2135 last unless $line eq '';
104             }
105              
106 699         1432 my $headers = SVN::Dump::Headers->new();
107 699         809 while(1) {
108 2901         5666 my ($key, $value) = split /: /, $line, 2;
109 2901         4771 $headers->{$key} = $value;
110              
111 2901         3729 $line = <$fh>;
112 2901 100       3954 croak _eof() if !defined $line;
113 2900         3003 chop $line;
114 2900 100       4219 last if $line eq ''; # stop on empty line
115             }
116              
117 698 50       1473 croak "Empty line found instead of a header block line $."
118             if ! keys %$headers;
119              
120 698         1856 return $headers;
121             }
122              
123             sub read_property_block {
124 476     476 1 675 my ($fh) = @_;
125 476         984 my $property = SVN::Dump::Property->new();
126              
127 476         1311 local $/ = $NL;
128 476         638 my @buffer;
129 476         603 while(1) {
130 1277         1965 my $line = <$fh>;
131 1277 100       1925 croak _eof() if !defined $line;
132 1275         1493 chop $line;
133              
134             # read a key/value pair
135 1275 100       3823 if( $line =~ /\AK (\d+)\z/ ) {
    100          
    100          
136 799         1241 my $key = $fh->_read_string( $1 );
137              
138 799         1895 $line = <$fh>;
139 799 100       1230 croak _eof() if !defined $line;
140 798         1217 chop $line;
141            
142 798 100       1990 if( $line =~ /\AV (\d+)\z/ ) {
143 797         1166 my $value = $fh->_read_string( $1 );
144              
145 797         1544 $property->set( $key => $value );
146              
147             # FIXME what happens if we see duplicate keys?
148             }
149             else {
150 1         88 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         12 $property->set( $key => undef ); # undef means deleted
159             }
160             # end of properties
161             elsif( $line =~ /\APROPS-END\z/ ) {
162 471         610 last;
163             }
164             # inconsistent data
165             else {
166 1         73 croak "Corrupted property"; # FIXME better error message
167             }
168             }
169              
170 471         1522 return $property;
171             }
172              
173             sub read_text_block {
174 98     98 1 164 my ($fh, $size) = @_;
175              
176 98         159 return SVN::Dump::Text->new( $fh->_read_string( $size ) );
177             }
178              
179             sub _read_string {
180              
181 1698     1698   3333 my ( $fh, $size ) = @_;
182              
183 1698         3387 local $/ = $NL;
184              
185 1698         1740 my $text;
186 1698         3544 my $characters_read = read( $fh, $text, $size );
187              
188 1698 50       2243 if ( defined($characters_read) ) {
189 1698 100       2547 if ( $characters_read != $size ) {
190 1         3 croak _eof();
191             };
192             } else {
193 0         0 croak $!;
194             };
195              
196 1697         2351 <$fh>; # clear trailing newline
197              
198 1697         4027 return $text;
199              
200             };
201              
202             # FIXME make this more explicit
203 5     5   516 sub _eof { return "Unexpected EOF line $.", }
204              
205             __END__