File Coverage

blib/lib/SVN/Dumpfilter.pm
Criterion Covered Total %
statement 32 271 11.8
branch 0 132 0.0
condition 0 72 0.0
subroutine 10 25 40.0
pod 0 14 0.0
total 42 514 8.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             # $Id: Dumpfilter.pm 278 2007-01-13 12:37:13Z martin $
3             # Copyright (C) 2006-2008 by Martin Scharrer
4             # This is free software under the GPL.
5              
6             package SVN::Dumpfilter;
7 1     1   8604 use strict;
  1         3  
  1         51  
8 1     1   6 use warnings;
  1         1  
  1         38  
9 1     1   16 use 5.8.1;
  1         17  
  1         49  
10 1     1   1216 use English qw( -no_match_vars );
  1         5292  
  1         7  
11              
12             BEGIN {
13 1     1   451 use Exporter ();
  1         2  
  1         132  
14 1     1   3 our ( $VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS );
15              
16             #use version; $ VERSION = qv('0.21');
17 1         1 $VERSION = '0.21';
18              
19 1         20 @ISA = qw(Exporter);
20 1         3 @EXPORT = qw(&Dumpfilter &svn_recalc_content_header
21             &svn_recalc_textcontent_header &svn_recalc_prop_header);
22 1         6 %EXPORT_TAGS = (
23             'recalc' => [
24             qw(svn_recalc_content_header svn_recalc_textcontent_header
25             svn_recalc_prop_header)
26             ],
27             'filters' => [qw(dos2unix_filter null_filter null_recalc_filter)],
28             'internal' => [
29             qw(svn_read_entry svn_print_entry svn_get_properties
30             svn_props2str svn_header_sanitycheck)
31             ],
32             );
33 1         30 @EXPORT_OK = qw(&svn_get_properties &svn_props2str &svn_header_sanitycheck
34             &svn_read_entry &svn_print_entry &dos2unix_filter
35             &null_filter &null_recalc_filter &svn_remove_entry);
36             }
37             our @EXPORT_OK;
38              
39             #use Data::Dumper; # for Debug output
40 1     1   4 use Digest::MD5 qw(md5_hex);
  1         2  
  1         101  
41              
42             our $dumpfile;
43             our $filepos;
44             our $dumpfh;
45             our $outfh;
46              
47 1     1   5 use vars qw($CR $NL);
  1         2  
  1         73  
48              
49             BEGIN {
50             # "\n" and "\r" are not fully platform independend.
51             # So explicite ASCII numbers in octal are used.
52             # Variables, not constants, are used because these are used inside regexes.
53 1     1   3 $CR = "\015"; # carriage return
54 1         2911 $NL = "\012"; # new line
55             }
56              
57             sub svn_get_properties (\%\@$);
58             sub svn_props2str (\%;\@);
59             sub svn_header_sanitycheck (\%);
60             sub svn_recalc_content_header(\%);
61             sub svn_recalc_textcontent_header(\%);
62             sub svn_recalc_prop_header(\%);
63             sub svn_read_entry (*\%;$);
64             sub svn_print_entry(*\%);
65             sub svn_remove_entry (\%);
66              
67             # Filter
68             sub null_filter (\%;$);
69             sub null_recalc_filter (\%;$);
70             sub dos2unix_filter (\%;$);
71              
72             my @SVNHEADER = qw(
73             Revision-number
74             Node-path
75             Node-kind
76             Node-action
77             Node-copyfrom-rev
78             Node-copyfrom-path
79             Prop-delta
80             Prop-content-length
81             Text-delta
82             Text-content-length
83             Text-copy-source-md5
84             Text-content-md5
85             Content-length
86             );
87              
88             # "inverse array", maps name to index:
89             #my %svnhdridx = map { $SVNHEADER[$_] => $_ } (0 .. $#SVNHEADER);
90              
91             sub _supported_dump_format_version {
92 0     0     my $version = shift;
93              
94             # Versions 1 - 3 are supported
95 0   0       return ( $version >= 1 && $version <= 3 );
96             }
97              
98             ###############################################################################
99             # Dumpfilter
100             ####
101             # Awaits the dumpfile name, an output file name and a reference to a call-back
102             # function. While parsing the dumpfile the call-back function is called for
103             # every node and the result is re-assembled and written to the output file.
104              
105             sub Dumpfilter {
106 0     0 0   $dumpfile = shift;
107 0           my $outfile = shift;
108 0           my $filtersub = shift;
109              
110 0 0         if ( !defined $dumpfile ) {
111 0           $dumpfile = '-'; # Defaults to STDIN
112             }
113              
114 0 0         if ( !defined $outfile ) {
    0          
115 0           $outfile = '-'; # Defaults to STDOUT
116             }
117             elsif ( $outfile eq q{} ) {
118 0           $outfile = undef; # An empty string disables output
119             }
120              
121 0 0 0       if ( !defined $filtersub || ref $filtersub ne 'CODE' ) {
122 0           print STDERR "No filter function given!\n";
123 0           return 1;
124             }
125              
126 0           my $dumpfileerror = 0;
127              
128 0           my $SVN_fs_dump_format_version;
129             my $UUID;
130              
131 0 0         unless ( open( $dumpfh, "<$dumpfile" ) ) {
132 0           print STDERR "Couldn't open dumpfile '$dumpfile'.\n";
133 0           return 1;
134             }
135              
136 0 0         if ( defined $outfile ) {
137 0 0         unless ( open( $outfh, ">$outfile" ) ) {
138 0           print STDERR "Couldn't open output file '$outfile'.\n";
139 0           return 1;
140             }
141             }
142              
143 0           my $line;
144 0           local $INPUT_RECORD_SEPARATOR = $NL;
145 0 0 0       if ( defined( $line = <$dumpfh> )
146             && $line =~ /^SVN-fs-dump-format-version: (\d+)$/ )
147             {
148 0           $SVN_fs_dump_format_version = $1;
149 0 0         if ( !_supported_dump_format_version($SVN_fs_dump_format_version) ) {
150 0           print STDERR "Warning: Found dump format version ",
151             "($SVN_fs_dump_format_version) is not supported (yet).\n",
152             "Unknown entries will be ignored. Use at your own risk.\n";
153             }
154 0 0         print $outfh $line if defined $outfile;
155             }
156             else {
157 0           print STDERR "Error: Dumpfile looks invalid. Couldn't find valid ",
158             "'SVN-fs-dump-format-version' header.\n";
159 0           chomp($line);
160 0 0         print STDERR "Found '$line' instead.\n"
161             if defined($line);
162 0           return 1;
163             }
164              
165             # Skip empty lines
166 0   0       while ( defined( $line = <$dumpfh> ) && $line =~ /^$/ ) {
167 0 0         print $outfh $line if defined $outfile;
168             }
169 0 0         return 1 unless defined($line); # check for early EOF
170              
171 0 0         if ( $line =~ /^UUID: (.*)$/ ) # Save UUID if present
172             {
173 0           $UUID = $1;
174 0 0         print $outfh $line if defined $outfile;
175              
176             # Skip empty lines
177 0   0       while ( defined( $line = <$dumpfh> ) && $line =~ /^$/ ) {
178 0 0         print $outfh $line if defined $outfile;
179             }
180             }
181              
182 0 0         return 1 unless defined($line); # check for early EOF
183              
184 0           while (1) {
185 0           my $href = {}; # Reference to hash which will hold next entry
186              
187             # Read next entry into hash
188 0           $dumpfileerror += svn_read_entry( *$dumpfh, %$href, $line );
189              
190             # Filter code comes here
191             # We call a filter subfunction and pass everything as a hash
192 0           &{$filtersub}($href);
  0            
193              
194             # Reassemble of dump data
195 0 0         next unless ($outfile); # skip if we don't have an output file
196 0           svn_print_entry( *$outfh, %$href );
197             }
198             continue {
199              
200             # Skip empty lines
201 0   0       while ( defined( $line = <$dumpfh> ) && $line =~ /^$/ ) {
202 0 0         print $outfh $line if defined $outfile;
203             }
204 0 0         last unless defined($line);
205             }
206              
207 0           close($dumpfh);
208 0 0         close($outfh) if defined $outfile;
209 0           return $dumpfileerror;
210             }
211              
212             #################
213             # Null filter - does nothing
214             # For before-after self-checks
215              
216 0     0 0   sub null_filter (\%;$) {
217             }
218              
219             #################
220             # Null Recalc filter - does nothing except recalculation of headers
221             # For before-after self-checks
222              
223             sub null_recalc_filter (\%;$) {
224 0     0 0   my $href = shift;
225 0   0       my $recalc = shift || 1;
226              
227 0 0         if ($recalc) {
228 0           svn_recalc_prop_header(%$href);
229 0           svn_recalc_textcontent_header(%$href);
230             }
231             }
232              
233             #################
234             # Dos to Unix filter - changes end-of-line sequences
235              
236             sub dos2unix_filter (\%;$) {
237 0     0 0   my $href = shift;
238 0   0       my $recalc = shift || 1;
239              
240 0           my $header = $href->{'header'};
241 0           my $prop = $href->{'properties'};
242              
243             # return when no content present
244 0 0         return unless exists $header->{'Text-content-length'};
245              
246             # skip all files which have a mime-type set to something other than 'text/*'
247             return
248 0 0 0       if exists $prop->{'svn:mime-type'}
249             and $prop->{'svn:mime-type'} !~ m{^text/};
250 0 0         return if exists $prop->{'svn:eol-style'}; # skip if eol-style is set
251             # Skip when text is saved as deltas
252             return
253 0 0 0       if exists $header->{'Text-delta'}
254             and lc( $header->{'Text-delta'} ) eq 'true';
255              
256 0           ${ $href->{'content'} } =~ s/$CR$NL/$NL/mog;
  0            
257              
258             # Set eol-style:
259 0           push( @{ $href->{'properties_order'} }, 'svn:eol-style' );
  0            
260 0           $prop->{'svn:eol-style'} = 'native';
261              
262 0 0         if ($recalc) {
263 0           svn_recalc_prop_header(%$href);
264 0           svn_recalc_textcontent_header(%$href);
265             }
266             }
267              
268             #################
269             # new_scalar_ref - Creates and returns a new scalar reference
270              
271             sub new_scalar_ref () {
272 0     0 0   my $new;
273 0           return \$new; # For C-Programmers: Yes, this works under Perl!
274             }
275              
276             #################
277             # svn_read_entry - Read node entry from filehandle
278              
279             sub svn_read_entry (*\%;$) {
280 0     0 0   my $infh = shift; # Filehandle to read
281 0           my $href = shift; # (Empty) Hash (as reference) to write node
282 0           my $line = shift; # Optional: First line (already read before)
283 0           my $error = 0;
284              
285             # Init hash
286 0           my $header = ( $href->{'header'} = {} );
287 0           my $prop = ( $href->{'properties'} = {} );
288 0           my $prop_order = ( $href->{'properties_order'} = [] );
289 0           my $content = ( $href->{'content'} = new_scalar_ref );
290              
291 0           $filepos = tell($infh);
292              
293 0           local $INPUT_RECORD_SEPARATOR = $NL; # New line
294              
295 0 0         $line = <$dumpfh>
296             unless defined $line;
297              
298             # Should be 'Node-path: ' or 'Revision-number: ' now
299 0 0         if ( $line !~ /^(Node-path|Revision-number): / ) {
300 0           chomp($line);
301 0           print STDERR
302             "Read error in dumpfile '$dumpfile' at line '$.'. Skipping line: '$line'\n";
303 0           $error++;
304             }
305              
306             # Read headers
307 0   0       do {
308 0 0         if ( $line =~ /^([^:]+):\s*(.*)$/ ) {
309 0           $header->{$1} = $2;
310             }
311             else {
312 0           print STDERR "Error in header at input line $.\n";
313 0           $error++;
314             }
315             } while ( defined( $line = <$infh> ) && $line !~ /^$/ );
316 0 0         last unless defined($line); # Safety check for EOF
317              
318             # Get properties when they exist (but then they can be empty also!)
319 0 0         if ( exists $header->{'Prop-content-length'} ) {
320 0           my $prop_lines;
321 0           read $infh, $prop_lines, $header->{'Prop-content-length'};
322 0           $.++ while ( $prop_lines =~ /$NL/go ); # Count lines
323              
324 0 0         if ( not $prop_lines =~ s/PROPS-END$NL\Z//o ) {
325 0           print STDERR
326             "Didn't found 'PROPS-END' where it was expected at input line $.\n";
327 0           $error++;
328             }
329              
330             # Parse lines and extract properties:
331 0 0         unless ( svn_get_properties( %$prop, @$prop_order, $prop_lines ) ) {
332 0           $error++;
333             }
334             }
335              
336             # Get content
337 0 0         if ( exists( $header->{'Text-content-length'} ) ) {
338 0           read $infh, $$content, $header->{'Text-content-length'};
339 0           $.++ while ( $$content =~ /$NL/go ); # Count lines
340             # TODO: check number of bytes returned
341             }
342              
343             # Some sanity checks:
344 0           $error += svn_header_sanitycheck(%$header);
345              
346 0           return $error;
347             }
348              
349             #################
350             ## svn_print_entry - Write node entry to filehandle
351              
352             sub svn_print_entry (*\%) {
353 0     0 0   my $fh = shift; # Filehandle to write to
354 0           my $href = shift; # Hash (as reference) with node to be written
355 0           my $header = $href->{'header'};
356 0           my $prop = $href->{'properties'};
357              
358 0 0         return unless ( keys %$header ); # skip if there are no header
359              
360             # Header
361             # We try to print all header in the original order.
362             {
363              
364             # Generate hash to check if all header are printed
365 0           my %header_notprinted = map { $_ => 0 } keys %$header;
  0            
  0            
366              
367             # Print header in the standard order given by @SVNHEADER
368 0           foreach my $head (@SVNHEADER) {
369 0 0         if ( exists $header->{$head} ) {
370 0           print $fh "$head: $header->{$head}" . $NL;
371 0           delete $header_notprinted{$head}; # delete from check-hash
372             }
373             }
374              
375             # Print all remaining (non-standard?) header
376 0           foreach my $head ( sort keys %header_notprinted ) {
377 0           print $fh "$head: $header->{$head}" . $NL;
378 0           print STDERR "Info: header '$head' unknown by script.\n";
379             }
380 0           print $fh $NL; # delimiter
381             }
382              
383             # Properties
384 0 0 0       if ( exists $header->{'Prop-content-length'}
385             and $header->{'Prop-content-length'} > 0 )
386             {
387 0 0         if ( exists $href->{'properties_order'} ) {
388 0           print $fh svn_props2str( %$prop, @{ $href->{'properties_order'} } );
  0            
389             }
390 0           else { print $fh svn_props2str(%$prop) }
391 0           print $fh "PROPS-END" . $NL;
392             }
393              
394             # Content
395 0 0 0       if ( exists $header->{'Text-content-length'}
      0        
396             and $header->{'Text-content-length'} > 0
397             and exists $href->{'content'} )
398             {
399 0           print $fh ${ $href->{'content'} };
  0            
400             }
401             }
402              
403             #################
404             ## svn_recalc_content_header - Recalculate 'Content-length' header
405             #####
406             # Depends on correct values in other headers.
407             # Will be called by other recalc-functions.
408              
409             sub svn_recalc_content_header(\%) {
410 0     0 0   my $href = shift;
411 0           my $header = $href->{'header'};
412 1     1   7 no warnings 'uninitialized';
  1         2  
  1         2696  
413              
414 0           my $header_existed = exists $header->{'Content-length'};
415              
416 0           $header->{'Content-length'} =
417             $header->{'Text-content-length'} + $header->{'Prop-content-length'};
418              
419 0 0 0       if ( $header->{'Content-length'} == 0 && !$header_existed ) {
420 0           delete $header->{'Content-length'};
421             }
422             }
423              
424             #################
425             ## svn_recalc_textcontent_header - Recalculate 'Text-content'* and dependend headers
426             #####
427              
428             sub svn_recalc_textcontent_header(\%) {
429 0     0 0   my $href = shift;
430 0           my $header = $href->{'header'};
431              
432 0           my $header_existed = exists $header->{'Text-content-length'};
433              
434 0           my $length =
435             defined $href->{'content'}
436 0 0         ? length ${ $href->{'content'} }
437             : 0;
438              
439 0 0 0       if ( $length == 0 and !$header_existed ) {
440 0           delete $header->{'Text-content-length'};
441 0           delete $header->{'Text-content-md5'};
442             }
443             else {
444 0           $header->{'Text-content-length'} = $length;
445 0           $header->{'Text-content-md5'} = md5_hex( ${ $href->{'content'} } );
  0            
446             }
447              
448 0           svn_recalc_content_header(%$href);
449             }
450              
451             #################
452             ## svn_recalc_prop_header - Recalculate 'Prop-content-length' and dependend headers
453             #####
454              
455             sub svn_recalc_prop_header(\%) {
456 0     0 0   my $href = shift;
457 0           my $header = $href->{'header'};
458 0           my $prop = $href->{'properties'};
459              
460 0 0         return unless keys %$prop; # do nothing when no properties are present
461              
462             # Correct properties length:
463 0           $header->{'Prop-content-length'} = 10 # for the "PROPS-END$NL" string
464 0           + length( svn_props2str( %{ $href->{'properties'} } ) );
465 0           svn_recalc_content_header(%$href);
466             }
467              
468             #################
469             ## svn_get_properties - Extracts properties from a formatted string
470             #####
471             # Opposite of 'svn_props2str'
472             # Could also be called 'svn_str2props'
473              
474             sub svn_get_properties (\%\@$) {
475 0     0 0   my $prophash = shift; # Hash reference to store properties
476 0           my $proporder = shift; # Array ref. to store order of properties
477 0           my $props = shift; # String in SVN property format to parse
478              
479             # Parse string
480 0           while ( defined($props) ) {
481              
482             # Look for Keyword
483 0 0         ( $props =~ s/^K (\d+)$NL//o ) or last;
484 0           my $key = substr( $props, 0, $1, '' ); # get key with length given by
485             # above line and replace it with an null-string
486 0           $props =~ s/^$NL//o; # delete trailing new-line
487              
488             # Look for Value
489 0 0         ( $props =~ s/^V (\d+)$NL//o ) or last;
490 0           my $value =
491             substr( $props, 0, $1, '' ); # get value with length given by
492             # above line and replace it with an null-string
493 0           $props =~ s/^$NL//o; # delete trailing new-line
494              
495             # Save
496 0           push( @$proporder, $key );
497 0           $prophash->{$key} = $value;
498             }
499              
500             # Deleted properties
501 0           while ( defined($props) ) {
502 0 0         ( $props =~ s/^D (\d+)$NL//o ) or last;
503 0           my $key = substr( $props, 0, $1, '' ); # get key with length given by
504             # above line and replace it with an null-string
505 0           $props =~ s/^($NL)//o; # delete trailing new-line
506 0 0         $prophash->{__DELETED_PROPERTIES__} .= $key . ( defined($1) ? $1 : '' );
507             }
508              
509             # Read unkown but valid looking entries
510 0           while ( defined($props) ) {
511 0 0         ( $props =~ s/^([A-Z] (\d+)$NL)//o ) or last;
512 0           my $head = $1;
513 0           my $key = substr( $props, 0, $2, '' ); # get key with length given by
514             # above line and replace it with an null-string
515 0           $props =~ s/^($NL)//o; # delete trailing new-line
516 0           print STDERR "Error: Found unknown entry in property field:\n------\n",
517             $head, $key, "\n";
518 0 0         $prophash->{__UNKNOWN_PROPERTY_ENTRY__} .=
519             $head . $key . ( defined($1) ? $1 : '' );
520             }
521              
522             # Debug output
523             #print Data::Dumper->Dump([\$prophash, \$proporder], ['prophash',
524             # 'proporder']) if @$proporder;
525              
526 0 0         if ( length($props) != 0 ) # parse errors
527             {
528 0           print STDERR "Error at parsing properties at input line $.:",
529             "Couldn't understand '$props'.\n";
530 0           return 0;
531             }
532              
533 0           return 1;
534             }
535              
536             #################
537             ## svn_props2str - Converts properties to a formatted string
538             #####
539             # Opposite of 'svn_get_properties';
540             # Returns formatted string in SVN property format
541              
542             sub svn_props2str (\%;\@) {
543 0     0 0   my $prophash = shift; # Hash ref. with properties
544 0   0       my $proporder = shift || []; # Array ref. with properties order
545 0           my $props = ''; # Return string
546              
547             # Create check-hash
548 0           my %prop_notprinted = map { $_ => 0 } ( keys %$prophash );
  0            
549              
550             # Print properties by given order
551 0           foreach my $key (@$proporder) {
552 0           $props .= 'K '
553             . length($key)
554             . $NL
555             . $key
556             . $NL . 'V '
557             . length( $prophash->{$key} )
558             . $NL
559             . $prophash->{$key}
560             . $NL;
561 0           delete $prop_notprinted{$key}; # printed so delete from check-hash
562             }
563              
564             # Print now all remaining properties (if any)
565 0           foreach my $key ( sort keys %prop_notprinted ) {
566 0           $props .= 'K '
567             . length($key)
568             . $NL
569             . $key
570             . $NL . 'V '
571             . length( $prophash->{$key} )
572             . $NL
573             . $prophash->{$key}
574             . $NL;
575             }
576              
577             # Print list of deleted properties
578 0 0         if ( exists $prophash->{__DELETED_PROPERTIES__} ) {
579 0           my $value = $prophash->{__DELETED_PROPERTIES__};
580 0           $props .= 'D ' . length($value) . $NL . $value . $NL;
581             }
582              
583             # Print unknown entries
584 0 0         if ( exists $prophash->{__UNKNOWN_PROPERTY_ENTRY__} ) {
585 0           $props .= $prophash->{__UNKNOWN_PROPERTY_ENTRY__};
586             }
587              
588 0           return $props;
589             }
590              
591             #################
592             ## svn_header_sanitycheck - Checks if needed header exists and belong to each other
593             #####
594              
595             sub svn_header_sanitycheck (\%) {
596 0     0 0   my $header = shift;
597 0           my $error = 0;
598              
599             # Revision entry needs also 'Prop-content-length' and 'Content-length'
600 0 0         if ( exists $header->{'Revision-number'} ) {
    0          
601 0 0 0       if ( !exists $header->{'Prop-content-length'}
602             || !exists $header->{'Content-length'} )
603             {
604 0           print STDERR
605             "Missing needed header(s) after 'Revision-number' at line $..\n";
606 0           $error++;
607             }
608             }
609              
610             # if ( exists $header->{'Node-path'} ) # Must have 'Node-path' yet because of
611             # above tests (see begin of while loop)
612             # Nodes need 'Node-action' at minimum.
613             elsif ( !exists $header->{'Node-action'} ) {
614 0           print STDERR
615             "Missing needed header 'Node-action' after 'Node-path' at line $..\n";
616 0           $error++;
617             }
618             else # 'Node-action' exists:
619             {
620 0           my $action = $header->{'Node-action'}; # buffer
621 0 0 0       if ( $action eq 'delete' ) {
    0          
    0          
622 0 0         my $num_headers_expected =
623             ( exists $header->{'Node-kind'} ) ? 3 : 2;
624              
625 0 0         if ( keys %$header != $num_headers_expected ) {
626 0           print STDERR
627             "Two much headers for 'Node-action: delete' at line $.:\n";
628 0           local $, = "\n";
629              
630 0           while ( my ( $key, $value ) = each %$header ) {
631 0           print STDERR "$key: $value\n";
632             }
633 0           $error++;
634             }
635             }
636             elsif ( $action eq 'add' or $action eq 'replace' ) {
637 0 0         if ( !exists $header->{'Node-kind'} ) {
    0          
    0          
638 0           print STDERR
639             "Missing header 'Node-kind' for 'Node-action: add' at line $..\n";
640 0           $error++;
641             }
642             elsif ( $header->{'Node-kind'} eq 'file' ) {
643 0 0 0       unless ( # This two header both exist
      0        
      0        
      0        
      0        
644             (
645             exists $header->{'Text-content-length'}
646             && exists $header->{'Text-content-md5'}
647             && !( # and this two both exist or both non-exist
648             exists $header->{
649             'Node-copyfrom-rev'} ^ #\ xor+negation
650             exists $header->{
651             'Node-copyfrom-path'} #/ = equivalence
652             )
653             )
654             || ( # This two header both exist
655             exists $header->{'Node-copyfrom-rev'}
656             && exists $header->{'Node-copyfrom-path'}
657             && !( # and this two both exist or both non-exist
658             exists $header->{
659             'Text-content-length'} ^ #\ xor+negation
660             exists $header->{
661             'Text-content-md5'} #/ = equivalence
662             )
663             )
664             )
665             { # then there is something wrong
666 0           print STDERR
667             "Missing/wrong header(s) for 'Node-action: add'/'Node-kind: ",
668             "file' ", "at line $..\n";
669 0           $error++;
670             }
671             }
672             elsif ( $header->{'Node-kind'} eq 'dir' ) {
673 0 0 0       if ( exists $header->{'Text-content-length'}
674             || exists $header->{'Text-content-md5'} )
675             {
676 0           print STDERR
677             "To much header(s) for 'Node-action: add'/'Node-kind: dir' ",
678             "at line $..\n";
679 0           $error++;
680             }
681             }
682             else {
683 0           print STDERR "Invalid value '", $header->{'Node-kind'},
684             "' for 'Node-kind' ", "at line $..\n";
685 0           $error++;
686             }
687             }
688             elsif ( $action eq 'change' ) {
689              
690             }
691             else {
692              
693             }
694             } # end of else path of "if ( !exists $header->{'Node-action'} )"
695              
696             #print STDERR Data::Dumper->Dump([$header], ['%header']) if $error;
697 0           return $error;
698             }
699              
700             #################
701             ## svn_remove_entry - Removes given entry, i.e. cleans entry hash, so that
702             ## this entry is not part of the output dump file.
703             #####
704             sub svn_remove_entry (\%) {
705 0     0 0   my $href = shift;
706              
707 0           %$href = ();
708             }
709              
710             1;
711             __END__