File Coverage

blib/lib/Parse/DebControl.pm
Criterion Covered Total %
statement 157 196 80.1
branch 64 88 72.7
condition 12 20 60.0
subroutine 20 21 95.2
pod 7 7 100.0
total 260 332 78.3


line stmt bran cond sub pod time code
1             package Parse::DebControl;
2              
3             ###########################################################
4             # Parse::DebControl - Parse debian-style control
5             # files (and other colon key-value fields)
6             #
7             # Copyright 2003 - Jay Bonci
8             # Licensed under the same terms as perl itself
9             #
10             ###########################################################
11              
12 11     11   44925 use strict;
  11         28  
  11         425  
13 11     11   8907 use IO::Scalar;
  11         120096  
  11         538  
14 10     10   8401 use Compress::Zlib;
  10         518240  
  10         4224  
15 10     10   12417 use LWP::UserAgent;
  10         660345  
  10         599  
16              
17 10     10   108 use vars qw($VERSION);
  10         21  
  10         26320  
18             $VERSION = '2.005';
19              
20             sub new {
21 14     14 1 7060 my ($class, $debug) = @_;
22 14         329 my $this = {};
23              
24 14         41 my $obj = bless $this, $class;
25 14 100       65 if($debug)
26             {
27 5         18 $obj->DEBUG();
28             }
29 14         163 return $obj;
30             };
31              
32             sub parse_file {
33 5     5 1 13 my ($this, $filename, $options) = @_;
34 5 100       17 unless($filename)
35             {
36 1         3 $this->_dowarn("parse_file failed because no filename parameter was given");
37 1         3 return;
38             }
39              
40 4         3 my $fh;
41 4 50       204 unless(open($fh,"$filename"))
42             {
43 0         0 $this->_dowarn("parse_file failed because $filename could not be opened for reading");
44 0         0 return;
45             }
46            
47 4         17 return $this->_parseDataHandle($fh, $options);
48             };
49              
50             sub parse_mem {
51 26     26 1 51682 my ($this, $data, $options) = @_;
52              
53 26 100       86 unless($data)
54             {
55 1         5 $this->_dowarn("parse_mem failed because no data was given");
56 1         4 return;
57             }
58              
59 25         204 my $IOS = new IO::Scalar \$data;
60              
61 25 50       1561 unless($IOS)
62             {
63 0         0 $this->_dowarn("parse_mem failed because IO::Scalar creation failed.");
64 0         0 return;
65             }
66              
67 25         181 return $this->_parseDataHandle($IOS, $options);
68              
69             };
70              
71             sub parse_web {
72 0     0 1 0 my ($this, $url, $options) = @_;
73              
74 0 0       0 unless($url)
75             {
76 0         0 $this->_dowarn("No url given, thus no data to parse");
77 0         0 return;
78             }
79              
80 0         0 my $ua = LWP::UserAgent->new;
81              
82 0         0 my $request = HTTP::Request->new(GET => $url);
83              
84 0 0       0 unless($request)
85             {
86 0         0 $this->_dowarn("Failed to instantiate HTTP Request object");
87 0         0 return;
88             }
89              
90 0         0 my $response = $ua->request($request);
91              
92 0 0       0 if ($response->is_success) {
93 0         0 return $this->parse_mem($response->content(), $options);
94             } else {
95 0         0 $this->_dowarn("Failed to fetch $url from the web");
96 0         0 return;
97             }
98             }
99              
100             sub write_file {
101 8     8 1 699 my ($this, $filenameorhandle, $dataorarrayref, $options) = @_;
102              
103 8 100       25 unless($filenameorhandle)
104             {
105 1         3 $this->_dowarn("write_file failed because no filename or filehandle was given");
106 1         4 return;
107             }
108              
109 7 100       17 unless($dataorarrayref)
110             {
111 1         4 $this->_dowarn("write_file failed because no data was given");
112 1         3 return;
113             }
114              
115 6         21 my $handle = $this->_getValidHandle($filenameorhandle, $options);
116              
117 6 50       16 unless($handle)
118             {
119 0         0 $this->_dowarn("write_file failed because we couldn't negotiate a valid handle");
120 0         0 return;
121             }
122              
123 6         28 my $string = $this->write_mem($dataorarrayref, $options);
124 6   50     15 $string ||= "";
125            
126 6         59 print $handle $string;
127 6         345 close $handle;
128              
129 6         51 return length($string);
130             }
131              
132             sub write_mem {
133 18     18 1 1162 my ($this, $dataorarrayref, $options) = @_;
134              
135 18 100       55 unless($dataorarrayref)
136             {
137 2         8 $this->_dowarn("write_mem failed because no data was given");
138 2         7 return;
139             }
140              
141 16         43 my $arrayref = $this->_makeArrayref($dataorarrayref);
142              
143 16         42 my $string = $this->_makeControl($arrayref);
144              
145 16 100       65 $string .= "\n" if $options->{addNewline};
146              
147 16 100       47 $string = Compress::Zlib::memGzip($string) if $options->{gzip};
148              
149 16         1165 return $string;
150             }
151              
152             sub DEBUG
153             {
154 5     5 1 12 my($this, $verbose) = @_;
155 5 50 33     45 $verbose = 1 unless(defined($verbose) and int($verbose) == 0);
156 5         13 $this->{_verbose} = $verbose;
157 5         12 return;
158              
159             }
160              
161             sub _getValidHandle {
162 6     6   12 my($this, $filenameorhandle, $options) = @_;
163              
164 6 50       15 if(ref $filenameorhandle eq "GLOB")
165             {
166 0 0       0 unless($filenameorhandle->opened())
167             {
168 0         0 $this->_dowarn("Can't get a valid filehandle to write to, because that is closed");
169 0         0 return;
170             }
171              
172 0         0 return $filenameorhandle;
173             }else
174             {
175 6         10 my $openmode = ">>";
176 6 100       18 $openmode=">" if $options->{clobberFile};
177 6 50       15 $openmode=">>" if $options->{appendFile};
178              
179 6         6 my $handle;
180              
181 6 50       68255 unless(open $handle,"$openmode$filenameorhandle")
182             {
183 0         0 $this->_dowarn("Couldn't open file: $openmode$filenameorhandle for writing");
184 0         0 return;
185             }
186              
187 6         37 return $handle;
188             }
189             }
190              
191             sub _makeArrayref {
192 16     16   25 my ($this, $dataorarrayref) = @_;
193              
194 16 100       53 if(ref $dataorarrayref eq "ARRAY")
195             {
196 5         11 return $dataorarrayref;
197             }else{
198 11         41 return [$dataorarrayref];
199             }
200             }
201              
202             sub _makeControl
203             {
204 16     16   35 my ($this, $dataorarrayref) = @_;
205            
206 16         24 my $str = "";
207              
208 16         40 foreach my $stanza(@$dataorarrayref)
209             {
210 16         54 foreach my $key(keys %$stanza)
211             {
212 27   100     192 $stanza->{$key} ||= "";
213              
214 27         159 my @lines = split("\n", $stanza->{$key});
215 27 100       189 if (@lines) {
216 25         72 $str.="$key\: ".(shift @lines)."\n";
217             } else {
218 2         5 $str.="$key\:\n";
219             }
220              
221 27         112 foreach(@lines)
222             {
223 8 100       16 if($_ eq "")
224             {
225 2         4 $str.=" .\n";
226             }
227             else{
228 6         15 $str.=" $_\n";
229             }
230             }
231              
232             }
233              
234 16   100     56 $str ||= "";
235 16         36 $str.="\n";
236             }
237              
238 16         27 chomp($str);
239 16         39 return $str;
240            
241             }
242              
243             sub _parseDataHandle
244             {
245 29     29   52 my ($this, $handle, $options) = @_;
246              
247 29         44 my $structs;
248              
249 29 50       70 unless($handle)
250             {
251 0         0 $this->_dowarn("_parseDataHandle failed because no handle was given. This is likely a bug in the module");
252 0         0 return;
253             }
254              
255 29 100       183 if($options->{tryGzip})
256             {
257 3 50       9 if(my $gunzipped = $this->_tryGzipInflate($handle))
258             {
259 3         314 $handle = new IO::Scalar \$gunzipped
260             }
261             }
262              
263 29         289 my $data = $this->_getReadyHash($options);
264              
265 29         44 my $linenum = 0;
266 29         46 my $lastfield = "";
267              
268 29         174 foreach my $line (<$handle>)
269             {
270             #Sometimes with IO::Scalar, lines may have a newline at the end
271              
272             #$line =~ s/\r??\n??$//; #CRLF fix, but chomp seems to clean it
273 92         12264 chomp $line;
274            
275              
276 92 100       203 if($options->{stripComments}){
277 16 100       62 next if $line =~ /^\s*\#[^\#]/;
278 12         20 $line =~ s/\#$//;
279 12         22 $line =~ s/(?<=[^\#])\#[^\#].*//;
280 12         20 $line =~ s/\#\#/\#/;
281             }
282              
283 88         86 $linenum++;
284 88 100       353 if($line =~ /^[^\t\s]/)
    100          
    50          
285             {
286             #we have a valid key-value pair
287 64 50       361 if($line =~ /(.*?)\s*\:\s*(.*)$/)
288             {
289 64         157 my $key = $1;
290 64         121 my $value = $2;
291              
292 64 100       146 if($options->{discardCase})
293             {
294 3         7 $key = lc($key);
295             }
296              
297 64 100       132 unless($options->{verbMultiLine})
298             {
299 61         145 $value =~ s/[\s\t]+$//;
300             }
301              
302 64         193 $data->{$key} = $value;
303              
304              
305 64 50 50     562 if ($options->{verbMultiLine}
      66        
306             && (($data->{$lastfield} || "") =~ /\n/o)){
307 0         0 $data->{$lastfield} .= "\n";
308             }
309              
310 64         140 $lastfield = $key;
311             }else{
312 0         0 $this->_dowarn("Parse error on line $linenum of data; invalid key/value stanza");
313 0         0 return $structs;
314             }
315              
316             }elsif($line =~ /^([\t\s])(.*)/)
317             {
318             #appends to previous line
319              
320 18 50       57 unless($lastfield)
321             {
322 0         0 $this->_dowarn("Parse error on line $linenum of data; indented entry without previous line");
323 0         0 return $structs;
324             }
325 18 100       57 if($options->{verbMultiLine}){
    100          
326 6         26 $data->{$lastfield}.="\n$1$2";
327             }elsif($2 eq "." ){
328 3         13 $data->{$lastfield}.="\n";
329             }else{
330 9         17 my $val = $2;
331 9         18 $val =~ s/[\s\t]+$//;
332 9         38 $data->{$lastfield}.="\n$val";
333             }
334              
335             }elsif($line =~ /^[\s\t]*$/){
336 6 50 33     26 if ($options->{verbMultiLine}
337             && ($data->{$lastfield} =~ /\n/o)) {
338 0         0 $data->{$lastfield} .= "\n";
339             }
340 6 100       35 if(keys %$data > 0){
341 5         53 push @$structs, $data;
342             }
343 6         18 $data = $this->_getReadyHash($options);
344 6         17 $lastfield = "";
345             }else{
346 0         0 $this->_dowarn("Parse error on line $linenum of data; unidentified line structure");
347 0         0 return $structs;
348             }
349              
350             }
351              
352 29 100       122 if(keys %$data > 0)
353             {
354 27         108 push @$structs, $data;
355             }
356              
357 29         210 return $structs;
358             }
359              
360             sub _tryGzipInflate
361             {
362 3     3   4 my ($this, $handle) = @_;
363              
364 3         5 my $buffer;
365             {
366 3         3 local $/ = undef;
  3         11  
367 3         33 $buffer = <$handle>;
368             }
369 3   66     51 return Compress::Zlib::memGunzip($buffer) || $buffer;
370             }
371              
372             sub _getReadyHash
373             {
374 35     35   49 my ($this, $options) = @_;
375 35         42 my $data;
376              
377 35 100       84 if($options->{useTieIxHash})
378             {
379 6     2   432 eval("use Tie::IxHash");
  2     2   17  
  2         4  
  2         35  
  2         12  
  2         3  
  2         24  
380 6 50       129 if($@)
381             {
382 0         0 $this->_dowarn("Can't use Tie::IxHash. You need to install it to have this functionality");
383 0         0 return;
384             }
385 6         29 tie(%$data, "Tie::IxHash");
386 6         82 return $data;
387             }
388              
389 29         138 return {};
390             }
391              
392             sub _dowarn
393             {
394 6     6   12 my ($this, $warning) = @_;
395              
396 6 50       31 if($this->{_verbose})
397             {
398 0         0 warn "DEBUG: $warning";
399             }
400              
401 6         15 return;
402             }
403              
404              
405             1;
406              
407             __END__