File Coverage

lib/PatchReader/Raw.pm
Criterion Covered Total %
statement 87 128 67.9
branch 43 66 65.1
condition 5 12 41.6
subroutine 11 13 84.6
pod 0 7 0.0
total 146 226 64.6


line stmt bran cond sub pod time code
1             package PatchReader::Raw;
2            
3             #
4             # USAGE:
5             # use PatchReader::Raw;
6             # my $parser = new PatchReader::Raw();
7             # $parser->sends_data_to($my_target);
8             # $parser->start_lines();
9             # open FILE, "mypatch.patch";
10             # while () {
11             # $parser->next_line($_);
12             # }
13             # $parser->end_lines();
14             #
15            
16 8     8   11260 use strict;
  8         16  
  8         311  
17            
18 8     8   2978 use PatchReader::Base;
  8         21  
  8         15826  
19            
20             @PatchReader::Raw::ISA = qw(PatchReader::Base);
21            
22             sub new {
23 8     8 0 15611 my $class = shift;
24 8   33     104 $class = ref($class) || $class;
25 8         79 my $this = $class->SUPER::new();
26 8         19 bless $this, $class;
27            
28 8         27 return $this;
29             }
30            
31             # We send these notifications:
32             # start_patch({ patchname })
33             # start_file({ filename, rcs_filename, old_revision, old_date_str, new_revision, new_date_str, is_add, is_remove })
34             # next_section({ old_start, new_start, old_lines, new_lines, @lines })
35             # end_patch
36             # end_file
37             sub next_line {
38 127     127 0 136 my $this = shift;
39 127         243 my ($line) = @_;
40            
41 127 50       248 return if $line =~ /^\?/;
42            
43             # patch header parsing
44 127 100 66     1121 if ($line =~ /^---\s*([\S ]+)\s*\t([^\t\r\n]*)\s*(\S*)/) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
45 3         7 $this->_maybe_end_file();
46            
47 3 50       29 if ($1 eq "/dev/null") {
48 0         0 $this->{FILE_STATE}{is_add} = 1;
49             } else {
50 3         10 $this->{FILE_STATE}{filename} = $1;
51             }
52 3         9 $this->{FILE_STATE}{old_date_str} = $2;
53 3 100       10 $this->{FILE_STATE}{old_revision} = $3 if $3;
54            
55 3         5 $this->{IN_HEADER} = 1;
56            
57             } elsif ($line =~ /^\+\+\+\s*([\S ]+)\s*\t([^\t\r\n]*)(\S*)/) {
58 3 50       11 if ($1 eq "/dev/null") {
59 0         0 $this->{FILE_STATE}{is_remove} = 1;
60             }
61 3         8 $this->{FILE_STATE}{new_date_str} = $2;
62 3 50       32 $this->{FILE_STATE}{new_revision} = $3 if $3;
63            
64 3         6 $this->{IN_HEADER} = 1;
65            
66             } elsif ($line =~ /^RCS file: ([\S ]+)/) {
67 6         32 $this->{FILE_STATE}{rcs_filename} = $1;
68            
69 6         17 $this->{IN_HEADER} = 1;
70            
71             } elsif ($line =~ /^retrieving revision (\S+)/) {
72 6         27 $this->{FILE_STATE}{old_revision} = $1;
73            
74 6         22 $this->{IN_HEADER} = 1;
75            
76             } elsif ($line =~ /^Index:\s*([\S ]+)/) {
77 6         23 $this->_maybe_end_file();
78            
79 6         27 $this->{FILE_STATE}{filename} = $1;
80            
81 6         18 $this->{IN_HEADER} = 1;
82            
83             } elsif ($line =~ /^diff\s*(-\S+\s*)*(\S+)\s*(\S*)/ && $3) {
84             # Simple diff
85 1         3 $this->_maybe_end_file();
86 1         3 $this->{FILE_STATE}{filename} = $2;
87            
88 1         2 $this->{IN_HEADER} = 1;
89            
90             # section parsing
91             } elsif ($line =~ /^@@\s*-(\d+),?(\d*)\s*\+(\d+),?(\d*)\s*(?:@@\s*(.*))?/) {
92 8         18 $this->{IN_HEADER} = 0;
93            
94 8         25 $this->_maybe_start_file();
95 8         27 $this->_maybe_end_section();
96 8 50       33 $2 = 0 if !defined($2);
97 8 50       27 $4 = 0 if !defined($4);
98 8         79 $this->{SECTION_STATE} = { old_start => $1, old_lines => $2,
99             new_start => $3, new_lines => $4,
100             func_info => $5,
101             minus_lines => 0, plus_lines => 0,
102             };
103            
104             } elsif ($line =~ /^(\d+),?(\d*)([acd])(\d+),?(\d*)/) {
105             # Non-universal diff. Calculate as though it were universal.
106 0         0 $this->{IN_HEADER} = 0;
107            
108 0         0 $this->_maybe_start_file();
109 0         0 $this->_maybe_end_section();
110            
111 0         0 my $old_start;
112             my $old_lines;
113 0         0 my $new_start;
114 0         0 my $new_lines;
115 0 0       0 if ($3 eq 'a') {
116             # 'a' has the old number one off from diff -u ("insert after this line"
117             # vs. "insert at this line")
118 0         0 $old_start = $1 + 1;
119 0         0 $old_lines = 0;
120             } else {
121 0         0 $old_start = $1;
122 0 0       0 $old_lines = $2 ? ($2 - $1 + 1) : 1;
123             }
124 0 0       0 if ($3 eq 'd') {
125             # 'd' has the new number one off from diff -u ("delete after this line"
126             # vs. "delete at this line")
127 0         0 $new_start = $4 + 1;
128 0         0 $new_lines = 0;
129             } else {
130 0         0 $new_start = $4;
131 0 0       0 $new_lines = $5 ? ($5 - $4 + 1) : 1;
132             }
133            
134 0         0 $this->{SECTION_STATE} = { old_start => $old_start, old_lines => $old_lines,
135             new_start => $new_start, new_lines => $new_lines,
136             minus_lines => 0, plus_lines => 0
137             };
138             }
139            
140             # line parsing (only when inside a section)
141 127 100       448 return if $this->{IN_HEADER};
142 81 100       351 if ($line =~ /^ /) {
    100          
    100          
    50          
    50          
143 44         153 push @{$this->{SECTION_STATE}{lines}}, $line;
  44         297  
144             } elsif ($line =~ /^-/) {
145 8         17 $this->{SECTION_STATE}{minus_lines}++;
146 8         11 push @{$this->{SECTION_STATE}{lines}}, $line;
  8         50  
147             } elsif ($line =~ /^\+/) {
148 14         28 $this->{SECTION_STATE}{plus_lines}++;
149 14         18 push @{$this->{SECTION_STATE}{lines}}, $line;
  14         76  
150             } elsif ($line =~ /^< /) {
151 0         0 $this->{SECTION_STATE}{minus_lines}++;
152 0         0 push @{$this->{SECTION_STATE}{lines}}, "-" . substr($line, 2);
  0         0  
153             } elsif ($line =~ /^> /) {
154 0         0 $this->{SECTION_STATE}{plus_lines}++;
155 0         0 push @{$this->{SECTION_STATE}{lines}}, "+" . substr($line, 2);
  0         0  
156             }
157             }
158            
159             sub start_lines {
160 7     7 0 25 my $this = shift;
161 7 50       37 die "No target specified: call sends_data_to!" if !$this->{TARGET};
162 7         18 delete $this->{FILE_STARTED};
163 7         14 delete $this->{FILE_STATE};
164 7         13 delete $this->{SECTION_STATE};
165 7         22 $this->{FILE_NEVER_STARTED} = 1;
166            
167 7         61 $this->{TARGET}->start_patch(@_);
168             }
169            
170             sub end_lines {
171 7     7 0 37 my $this = shift;
172 7         46 $this->_maybe_end_file();
173 7         42 $this->{TARGET}->end_patch(@_);
174             }
175            
176             sub _maybe_start_file {
177 8     8   13 my $this = shift;
178 8 50 33     88 if (exists($this->{FILE_STATE}) && !$this->{FILE_STARTED} ||
      33        
179             $this->{FILE_NEVER_STARTED}) {
180 8         59 $this->_start_file();
181             }
182             }
183            
184             sub _maybe_end_file {
185 17     17   26 my $this = shift;
186 17 100       54 return if $this->{IN_HEADER};
187            
188 15         36 $this->_maybe_end_section();
189 15 100       50 if (exists($this->{FILE_STATE})) {
190             # Handle empty patch sections (if the file has not been started and we're
191             # already trying to end it, start it first!)
192 8 50       33 if (!$this->{FILE_STARTED}) {
193 0         0 $this->_start_file();
194             }
195            
196             # Send end notification and set state
197 8         49 $this->{TARGET}->end_file($this->{FILE_STATE});
198 8         24 delete $this->{FILE_STATE};
199 8         19 delete $this->{FILE_STARTED};
200             }
201             }
202            
203             sub _start_file {
204 8     8   14 my $this = shift;
205            
206             # Send start notification and set state
207 8 50       39 if (!$this->{FILE_STATE}) {
208 0         0 $this->{FILE_STATE} = { filename => "file_not_specified_in_diff" };
209             }
210            
211             # Send start notification and set state
212 8         37 $this->{TARGET}->start_file($this->{FILE_STATE});
213 8         18 $this->{FILE_STARTED} = 1;
214 8         24 delete $this->{FILE_NEVER_STARTED};
215             }
216            
217             sub _maybe_end_section {
218 23     23   33 my $this = shift;
219 23 100       116 if (exists($this->{SECTION_STATE})) {
220 8         52 $this->{TARGET}->next_section($this->{SECTION_STATE});
221 8         42 delete $this->{SECTION_STATE};
222             }
223             }
224            
225             sub iterate_file {
226 0     0 0 0 my $this = shift;
227 0         0 my ($filename) = @_;
228            
229 0 0       0 open FILE, $filename or die "Could not open $filename: $!";
230 0         0 $this->start_lines($filename);
231 0         0 while () {
232 0         0 $this->next_line($_);
233             }
234 0         0 $this->end_lines($filename);
235 0         0 close FILE;
236             }
237            
238             sub iterate_fh {
239 0     0 0 0 my $this = shift;
240 0         0 my ($fh, $filename) = @_;
241            
242 0         0 $this->start_lines($filename);
243 0         0 while (<$fh>) {
244 0         0 $this->next_line($_);
245             }
246 0         0 $this->end_lines($filename);
247             }
248            
249             sub iterate_string {
250 7     7 0 39 my $this = shift;
251 7         27 my ($id, $data) = @_;
252            
253 7         56 $this->start_lines($id);
254 7         52 while ($data =~ /([^\n]*(\n|$))/g) {
255 127         283 $this->next_line($1);
256             }
257 7         32 $this->end_lines($id);
258             }
259            
260             1