File Coverage

blib/lib/SVN/Dumpfile.pm
Criterion Covered Total %
statement 147 158 93.0
branch 64 76 84.2
condition 22 24 91.6
subroutine 27 27 100.0
pod 12 12 100.0
total 272 297 91.5


line stmt bran cond sub pod time code
1             ################################################################################
2             # Copyright (c) 2008 Martin Scharrer
3             # This is open source software under the GPL v3 or later.
4             #
5             # $Id: Dumpfile.pm 107 2009-04-23 11:45:00Z martin $
6             ################################################################################
7             package SVN::Dumpfile;
8 6     6   172048 use strict;
  6         15  
  6         207  
9 6     6   33 use warnings;
  6         13  
  6         151  
10 6     6   4028 use SVN::Dumpfile::Node;
  6         20  
  6         192  
11 6     6   31 use IO::File;
  6         12  
  6         960  
12 6     6   33 use Carp;
  6         14  
  6         321  
13 6     6   32 use Readonly;
  6         10  
  6         5259  
14             Readonly my $NL => chr(10);
15              
16             our $VERSION = do { '$Rev: 107 $' =~ /\$Rev: (\d+) \$/; '0.13' . ".$1" };
17              
18              
19             sub new {
20 23     23 1 9876 my $arg = shift;
21 23 100       69 my $class = ref $arg ? ref $arg : $arg;
22              
23 23         57 my $self = bless {}, $class;
24 23         93 return $self->_process_arguments(@_);
25             }
26              
27             sub _process_arguments {
28 34     34   62 my $self = shift;
29 34         44 my @args;
30 34 100       79 if ( @_ == 1 ) {
31 15 100       102 if ( ref $_[0] eq 'HASH' ) {
    100          
    100          
    100          
32 1         2 @args = %{ $_[0] };
  1         5  
33             }
34             elsif ( ref $_[0] eq 'ARRAY' ) {
35 1         3 @args = @{ $_[0] };
  1         3  
36             }
37             elsif ( my $h = __PACKAGE__->_is_valid_fh( $_[0] ) ) {
38 5         11 $self->{fh} = $h;
39 5         10 @args = ();
40             }
41             elsif ( !ref $_[0] ) {
42 6         29 $self->{file} = $_[0];
43 6         153 @args = ();
44             }
45             else {
46 2         457 carp "Single argument must be either a hash ref or a filename!";
47 2         110 return undef;
48             }
49             }
50             else {
51 19         40 @args = @_;
52             }
53 32 100       233 croak "Final number of arguments not even"
54             if @args % 2;
55              
56 31         114 %$self = ( %$self, @args );
57              
58 31 100       82 if ( exists $self->{version} ) {
59 3         10 $self->{'SVN-fs-dump-format-version'} = $self->{version};
60 3         7 delete $self->{version};
61             }
62              
63 31         105 return $self;
64             }
65              
66             sub dump {
67 1     1 1 1426 require Data::Dumper;
68 1         9154 my $self = shift;
69 1         178 print STDERR Data::Dumper->Dump( [ \$self ], ['*self'] );
70             }
71              
72             sub uuid : lvalue {
73 7     7 1 15 my ( $self, $uuid ) = @_;
74 7 100       31 $self->{'UUID'} = $uuid
75             if defined $uuid;
76 7         32 $self->{'UUID'};
77             }
78              
79             sub version : lvalue {
80 7     7 1 661 my ( $self, $version ) = @_;
81 7 100       23 $self->{'SVN-fs-dump-format-version'} = $version
82             if defined $version;
83 7         58 $self->{'SVN-fs-dump-format-version'};
84             }
85              
86             sub read_node {
87 28     28 1 19517 my $self = shift;
88 28         122 my $node = SVN::Dumpfile::Node->new;
89 28 100       104 return unless $self->{fh};
90 27 100       82 $node = undef unless $node->read( $self->{fh} );
91              
92 27         103 return $node;
93             }
94             *get_node = \&read_node;
95             *next_node = \&read_node;
96              
97             sub write_node {
98 17     17 1 41 my $self = shift;
99 17         16 my $node = shift;
100              
101 17 100       43 return if not defined $node;
102              
103 16         48 return $node->write( $self->{fh} );
104             }
105              
106             sub create {
107 7     7 1 2710 my $self = shift;
108 7 100       20 if ( ref $self ) {
109 6 100       17 return unless $self->_process_arguments(@_);
110             }
111             else {
112 1         6 $self = $self->new(@_);
113             }
114              
115 6         12 my $fh = $self->{fh};
116 6         11 my $file = $self->{file};
117 6 100       16 if ( defined $fh ) {
    50          
118 5         6 eval { $fh->binmode };
  5         21  
119             }
120             elsif ( defined $file ) {
121 0         0 $self->{fh} = $fh = new IO::File;
122 0 0       0 $fh->open( $file, '>' ) or croak "Couldn't create file.";
123             }
124             else {
125 1         93 croak __PACKAGE__ . '::create() needs file name or handle.';
126             }
127              
128 5         68 $fh->print( $self->as_string );
129 5         45 return $self;
130             }
131              
132             sub as_string {
133 7     7 1 14 my $self = shift;
134 7         10 my $string = '';
135              
136 6     6   44 no warnings 'uninitialized';
  6         11  
  6         3105  
137 7 100       22 $self->{'SVN-fs-dump-format-version'} = 2
138             if ( $self->{'SVN-fs-dump-format-version'} < 1 );
139              
140 7         36 $string
141             = "SVN-fs-dump-format-version: "
142             . $self->{'SVN-fs-dump-format-version'}
143             . $NL x 2;
144              
145 7 100       56 if ( $self->{'SVN-fs-dump-format-version'} > 1 ) {
146 6 100       18 if ( $self->{'UUID'} eq '' ) {
147             $self->{'UUID'} = eval {
148              
149             # Use Data::GUID if available
150             require Data::GUID;
151             lc ( Data::GUID->new->as_string );
152             } || eval {
153              
154             # Use Data::UUID if available
155             require Data::UUID;
156             my $ug = new Data::UUID;
157             my $uuid_bin = $ug->create();
158             lc( $ug->to_string($uuid_bin) );
159 1   33     3 } || do {
160             my @r;
161              
162             # Otherwise just generate a random UUID
163             push( @r, rand( 2**16 - 1 ) ) for ( 1 .. 9 );
164             sprintf( "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", @r );
165             }
166             }
167              
168 6         26 $string .= "UUID: $self->{UUID}" . $NL x 2;
169             }
170              
171 7         64 return $string;
172             }
173             *to_string = \&as_string;
174              
175             sub copy {
176 4     4 1 19 my $self = shift;
177 4         12 my $new = $self->new();
178 4         9 foreach my $key (qw(SVN-fs-dump-format-version UUID)) {
179 8         24 $new->{$key} = $self->{$key};
180             }
181 4         15 return $new;
182             }
183              
184             sub DESTROY {
185 23     23   2058 shift->close;
186             }
187              
188             sub close {
189 25     25 1 94 my $self = shift;
190 25 100       180 $self->{fh}->close
191             if defined $self->{fh};
192             }
193              
194             sub version_supported {
195 23     23 1 354 my $self = shift;
196 23         29 my $version = shift;
197 23 100 100     131 if ( ref $self and not defined $version ) {
198 10         24 $version = $self->{'SVN-fs-dump-format-version'};
199             }
200              
201 23 100       70 return unless defined $version;
202              
203             # Versions 1 - 3 are supported
204 22   100     178 return ( $version >= 1 && $version <= 3 );
205             }
206              
207             sub _is_valid_fh {
208 40     40   1041 my $self = shift;
209 40         68 my $h = shift;
210 40 100       110 return if not defined $h;
211 39 100       59 return $h if eval { $h->isa('IO::Handle') };
  39         367  
212 6     6   38 no strict 'refs';
  6         12  
  6         561  
213 34 100 100     459 return *$h{IO}
      100        
      100        
214             if ref $h eq 'GLOB'
215             or ref \$h eq 'GLOB'
216             or $h eq 'STDIN'
217             or $h eq 'STDOUT';
218 15         87 return;
219             }
220              
221             sub _is_stdin {
222 6     6   77 no warnings 'uninitialized';
  6         12  
  6         536  
223 6     6   9 my $self = shift;
224 6         7 my $file = shift;
225 6   100     51 return ( $file eq '' or $file eq '-' or $file eq 'STDIN' );
226             }
227              
228             sub _is_stdout {
229 6     6   34 no warnings 'uninitialized';
  6         13  
  6         3462  
230 6     6   12 my $self = shift;
231 6         8 my $file = shift;
232 6   100     49 return ( $file eq '' or $file eq '-' or $file eq 'STDOUT' );
233             }
234              
235             sub open {
236 5     5 1 1173 my $self = shift;
237 5 50       18 if ( ref $self ) {
238 5 50       16 return unless $self->_process_arguments(@_);
239             }
240             else {
241 0         0 $self = $self->new(@_);
242             }
243 5         11 my $fh = $self->{fh};
244 5         10 my $file = $self->{file};
245 5 50       22 if ( defined $fh ) {
    50          
246 0         0 eval { $fh->binmode };
  0         0  
247             }
248             elsif ( defined $file ) {
249 5         40 $self->{fh} = $fh = new IO::File;
250 5 50       222 if ( !$fh->open( $file, '<' ) ) {
251 0         0 carp "Couldn't open dumpfile.";
252 0         0 return;
253             }
254             }
255             else {
256 0         0 croak __PACKAGE__ . '::open() needs file name or handle.';
257             }
258              
259 5         405 my $irs = IO::Handle->input_record_separator($NL);
260 5         336 my $line = $fh->getline;
261 5 50       243 if ( $line =~ /^SVN-fs-dump-format-version: (\d+)$/ ) {
262 5         16 $self->{'SVN-fs-dump-format-version'} = $1;
263 5 50       20 if ( !$self->version_supported ) {
264 0         0 carp "Warning: Found dump format version ",
265             $self->{'SVN-fs-dump-format-version'},
266             " is not supported (yet).\n",
267             "Unknown entries will be ignored. Use at your own risk.\n";
268             }
269             }
270             else {
271 0         0 carp "Error: Dumpfile looks invalid. Couldn't find valid ",
272             "'SVN-fs-dump-format-version' header.\n";
273             }
274              
275 5 100       19 if ( $self->{'SVN-fs-dump-format-version'} > 1 ) {
276 4         6 my $char;
277 4         30 while ( ( $char = $fh->getc ) eq "\012" ) { }
278 4 100       408 if ( $char eq 'U' ) {
279 3         63 $line = $char . $fh->getline;
280 3 50       91 if ( $line =~ /^UUID: (.*)$/ ) {
281 3         10 $self->{'UUID'} = $1;
282              
283             # read blank line after UUID:
284 3         10 $char = $fh->getc;
285 3 50       24 $fh->ungetc( ord $char ) if ( $char ne "\012" );
286             }
287             else {
288 0         0 carp "Error: Dumpfile looks invalid. Couldn't find valid ",
289             "'UUID' header.\n";
290             }
291             }
292             else {
293 1         188 carp "Error: Dumpfile looks invalid. Couldn't find valid ",
294             "'UUID' header.\n";
295 1         51 $fh->ungetc( ord $char );
296             }
297             }
298              
299 5         19 IO::Handle->input_record_separator($irs);
300 5         46 return $self;
301             }
302              
303             1;
304             __END__