File Coverage

blib/lib/MDV/Packdrakeng.pm
Criterion Covered Total %
statement 268 386 69.4
branch 115 178 64.6
condition 35 65 53.8
subroutine 25 32 78.1
pod 9 23 39.1
total 452 684 66.0


line stmt bran cond sub pod time code
1             ##- Nanar
2             ##-
3             ##- This program is free software; you can redistribute it and/or modify
4             ##- it under the terms of the GNU General Public License as published by
5             ##- the Free Software Foundation; either version 2, or (at your option)
6             ##- any later version.
7             ##-
8             ##- This program is distributed in the hope that it will be useful,
9             ##- but WITHOUT ANY WARRANTY; without even the implied warranty of
10             ##- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             ##- GNU General Public License for more details.
12             ##-
13             ##- You should have received a copy of the GNU General Public License
14             ##- along with this program; if not, write to the Free Software
15             ##- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16              
17             # $Id: Packdrakeng.pm 225631 2007-08-09 11:45:44Z nanardon $
18              
19             package MDV::Packdrakeng;
20              
21 2     2   1724 use strict;
  2         4  
  2         74  
22 2     2   1643 use POSIX qw(O_WRONLY O_TRUNC O_CREAT O_RDONLY O_APPEND);
  2         15335  
  2         14  
23 2     2   2071 use File::Path qw(mkpath);
  2         4  
  2         10210  
24              
25             our $VERSION = '1.13';
26              
27             my ($toc_header, $toc_footer) =
28             ('cz[0', '0]cz');
29              
30             # File::Temp qw(tempfile) hack to not require it
31             sub tempfile {
32 7     7 0 1122 my ($count, $fname, $handle) = (0, undef, undef);
33 7         28 do {
34 7 50       26 ++$count > 10 and do {
35 0         0 warn "Can't create temporary file ($fname)";
36 0         0 return (undef, undef);
37             };
38 35         123 $fname = sprintf("%s/packdrakeng.%s.%s",
39             $ENV{TMPDIR} || '/tmp',
40             $$,
41             # Generating an random name
42 7 100 50     101 join("", map { $_=rand(51); $_ += $_ > 25 && $_ < 32 ? 91 : 65 ; chr($_) } (0 .. 4)));
  35   100     140  
  35         1293  
43             } while !sysopen($handle, $fname, O_WRONLY | O_APPEND | O_CREAT);
44 7         58 return ($handle, $fname);
45             }
46              
47 10     10 0 94 sub method_info { "external $_[0]->{compress_method}/$_[0]->{uncompress_method} $VERSION" }
48              
49             sub _new {
50 16     16   97 my ($class, %options) = @_;
51              
52             my $pack = {
53             filename => $options{archive},
54              
55             compress_method => $options{compress},
56             uncompress_method => $options{uncompress},
57             force_extern => $options{extern} || 0, # Don't use perl-zlib
58             noargs => $options{noargs},
59              
60             # compression level, aka -X gzip or bzip option
61             level => defined($options{comp_level}) ? $options{comp_level} : 6,
62              
63             # A compressed block will contain 400k of compressed data
64             block_size => defined($options{block_size}) ? $options{block_size} : 400 * 1024,
65             bufsize => $options{bufsize} || 65536, # Arbitrary buffer size to read files
66              
67             # Internal data
68             handle => undef, # Archive handle
69              
70             # Toc information
71             files => {}, # filename => { off, size, coff, csize }
72             dir => {}, # dir => no matter what value
73             'symlink' => {}, # file => link
74              
75             coff => 0, # end of current compressed data
76              
77             # Data we need keep in memory to achieve the storage
78             current_block_files => {}, # Files in pending compressed block
79             current_block_csize => 0, # Actual size in pending compressed block
80             current_block_coff => 0, # The block block location (offset)
81             current_block_off => 0, # Actual uncompressed file offset within the pending block
82              
83             cstream_data => undef, # Wrapper data we need to keep in memory (compression)
84             ustream_data => undef, # Wrapper data we need to keep in memory (uncompression)
85              
86             # log and verbose function:
87             log => $options{quiet}
88 0     0   0 ? sub { our $error = "$_[0]\n" }
89 0     0   0 : sub { our $error = "$_[0]\n"; warn $error },
  0         0  
90             debug => $options{debug}
91 0     0   0 ? sub { my @w = @_; $w[0] = "Debug: $w[0]\n"; printf STDERR @w }
  0         0  
  0         0  
92 76     76   255 : sub {},
93 16 50 100     1533 };
    50 50        
    50          
    50          
94              
95 16         112 bless($pack, $class)
96             }
97              
98             sub new {
99 8     8 1 1526667 my ($class, %options) = @_;
100 8         86 my $pack = _new($class, %options);
101 8 50       1145 sysopen($pack->{handle}, $pack->{filename}, O_WRONLY | O_TRUNC | O_CREAT) or do {
102 0         0 $pack->{log}("Can't open $pack->{filename}: $!");
103 0         0 return undef;
104             };
105 8         75 $pack->choose_compression_method();
106 8         40 $pack->{need_build_toc} = 1;
107 8         60 $pack->{debug}(
108             "Creating new archive with %s.",
109             $pack->method_info(),
110             );
111 8         112 $pack
112             }
113              
114             sub open {
115 8     8 1 170 my ($class, %options) = @_;
116 8         69 my $pack = _new($class, %options);
117 8 50       546 sysopen($pack->{handle}, $pack->{filename}, O_RDONLY) or do {
118 0         0 $pack->{log}("Can't open $pack->{filename}: $!");
119 0         0 return undef;
120             };
121 8 50       75 $pack->read_toc() or return undef;
122 8         54 $pack->{debug}("Opening archive with %s.",
123             $pack->method_info(),
124             );
125 8         94 $pack
126             }
127              
128             # look $pack->{(un)compressed_method} and setup functions/commands to use
129             # Have some facility about detecting we want gzip/bzip
130             sub choose_compression_method {
131 16     16 0 37 my ($pack) = @_;
132              
133 16 100 66     149 (!defined($pack->{compress_method}) && !defined($pack->{uncompress_method}))
134             and $pack->{compress_method} = "gzip";
135 16   66     89 my $test_method = $pack->{compress_method} || $pack->{uncompress_method};
136              
137 16 100       119 $test_method =~ m/^bzip2|^bunzip2/ and do {
138 2   50     22 $pack->{compress_method} ||= "bzip2";
139             };
140 16 100       193 $test_method =~ m/^gzip|^gunzip/ and do {
141 12   100     55 $pack->{compress_method} ||= "gzip";
142 12 100       40 if (!$pack->{force_extern}) {
143 8         26 eval {
144 8         1605 require Compress::Zlib; #- need this to ensure that Packdrakeng::zlib will load properly
145 8         97733 require MDV::Packdrakeng::zlib;
146              
147 8         51 bless($pack, 'MDV::Packdrakeng::zlib');
148             };
149             }
150             };
151 16 100       123 if (!$pack->{noargs}) {
152 12   66     106 $pack->{uncompress_method} ||= "$pack->{compress_method} -d";
153 12 50       108 $pack->{compress_method} = $pack->{compress_method} ? "$pack->{compress_method} -$pack->{level}" : "";
154             }
155             }
156              
157             sub DESTROY {
158 16     16   4077 my ($pack) = @_;
159 16 50       79 $pack->{destroyed} and return; #- allow calling DESTROY
160 16         74 $pack->{destroyed} = 1;
161              
162 16         94 $pack->uncompress_handle(undef, undef);
163 16 50       115 $pack->build_toc() == 1 or die "Can't write toc into archive\n";
164 16 50       600 close($pack->{handle}) if $pack->{handle};
165 16 50       1011 close($pack->{ustream_data}{handle}) if $pack->{ustream_data}{handle};
166             }
167              
168             # Flush current compressed block
169             # Write
170             sub build_toc {
171 16     16 0 33 my ($pack) = @_;
172 16 100       110 $pack->{need_build_toc} or return 1;
173 8         61 $pack->end_block();
174 8 50       62 $pack->end_seek() or do {
175 0         0 $pack->{log}("Can't seek into archive");
176 0         0 return 0;
177             };
178 8         22 my ($toc_length, $cf, $cd, $cl) = (0, 0, 0, 0);
179              
180 8         14 foreach my $entry (keys %{$pack->{'dir'}}) {
  8         60  
181 2         8 $cd++;
182 2 50       33 my $w = syswrite($pack->{handle}, $entry . "\n") or do {
183 0         0 $pack->{log}("Can't write toc into archive");
184 0         0 return 0;
185             };
186 2         5 $toc_length += $w;
187             }
188 8         27 foreach my $entry (keys %{$pack->{'symlink'}}) {
  8         30  
189 2         3 $cl++;
190 2 50       36 my $w = syswrite($pack->{handle}, sprintf("%s\n%s\n", $entry, $pack->{'symlink'}{$entry})) or do {
191 0         0 $pack->{log}("Can't write toc into archive");
192 0         0 return 0;
193             };
194 2         4 $toc_length += $w
195             }
196 8         22 foreach my $entry (sort keys %{$pack->{files}}) {
  8         87  
197 64         62 $cf++;
198 64 50       786 my $w = syswrite($pack->{handle}, $entry ."\n") or do {
199 0         0 $pack->{log}("Can't write toc into archive");
200 0         0 return 0;
201             };
202 64         144 $toc_length += $w;
203             }
204 8         24 foreach my $file (sort keys %{$pack->{files}}) {
  8         82  
205 64         201 my $entry = $pack->{files}{$file};
206 64 50       778 syswrite($pack->{handle}, pack('NNNN', $entry->{coff}, $entry->{csize}, $entry->{off}, $entry->{size})) or do {
207 0         0 $pack->{log}("Can't write toc into archive");
208 0         0 return 0;
209             };
210             }
211             syswrite($pack->{handle}, pack("a4NNNNa40a4",
212             $toc_header,
213             $cd, $cl, $cf,
214             $toc_length,
215             $pack->{uncompress_method},
216 8 50       243 $toc_footer)) or do {
217 0         0 $pack->{log}("Can't write toc into archive");
218 0         0 return 0;
219             };
220 8         32 1;
221             }
222              
223             sub read_toc {
224 8     8 0 30 my ($pack) = @_;
225 8         48 sysseek($pack->{handle}, -64, 2) ; #or return 0;
226 8         343 sysread($pack->{handle}, my $buf, 64);# == 64 or return 0;
227 8         115 my ($header, $toc_d_count, $toc_l_count, $toc_f_count, $toc_str_size, $uncompress, $trailer) =
228             unpack("a4NNNNZ40a4", $buf);
229 8 50 33     80 $header eq $toc_header && $trailer eq $toc_footer or do {
230 0         0 $pack->{log}("Error reading toc: wrong header/trailer");
231 0         0 return 0;
232             };
233              
234 8   66     43 $pack->{uncompress_method} ||= $uncompress;
235 8         37 $pack->choose_compression_method();
236              
237 8         51 sysseek($pack->{handle}, -64 - ($toc_str_size + 16 * $toc_f_count) ,2);
238 8         59 sysread($pack->{handle}, my $fileslist, $toc_str_size);
239 8         113 my @filenames = split("\n", $fileslist);
240 8         57 sysread($pack->{handle}, my $sizes_offsets, 16 * $toc_f_count);
241 8         124 my @size_offset = unpack("N" . 4*$toc_f_count, $sizes_offsets);
242              
243 8         45 foreach (1 .. $toc_d_count) {
244 2         9 $pack->{dir}{shift(@filenames)} = 1;
245             }
246 8         31 foreach (1 .. $toc_l_count) {
247 2         4 my $n = shift(@filenames);
248 2         8 $pack->{'symlink'}{$n} = shift(@filenames);
249             }
250              
251 8         22 foreach (1 .. $toc_f_count) {
252 64         95 my $f = shift(@filenames);
253 64         259 $pack->{files}{$f}{coff} = shift(@size_offset);
254 64         158 $pack->{files}{$f}{csize} = shift(@size_offset);
255 64         118 $pack->{files}{$f}{off} = shift(@size_offset);
256 64         150 $pack->{files}{$f}{size} = shift(@size_offset);
257             # looking for offset for this archive
258 64 100       306 $pack->{files}{$f}{coff} + $pack->{files}{$f}{csize} > $pack->{coff}
259             and $pack->{coff} = $pack->{files}{$f}{coff} + $pack->{files}{$f}{csize};
260             }
261 8         52 $pack->{toc_f_count} = $toc_f_count;
262 8         38 1;
263             }
264              
265             sub sort_files_by_packing {
266 8     8 0 38 my ($pack, @files) = @_;
267 124 100 33     1070 sort {
    50          
268 8         66 defined($pack->{files}{$a}) && defined($pack->{files}{$b}) ?
269             ($pack->{files}{$a}{coff} == $pack->{files}{$b}{coff} ?
270             $pack->{files}{$a}{off} <=> $pack->{files}{$b}{off} :
271             $pack->{files}{$a}{coff} <=> $pack->{files}{$b}{coff}) :
272             $a cmp $b
273             } @files;
274             }
275              
276             # Goto to the end of written compressed data
277             sub end_seek {
278 89     89 0 196 my ($pack) = @_;
279 89 100       399 my $seekvalue = $pack->direct_write ? $pack->{coff} + $pack->{current_block_csize} : $pack->{coff};
280 89         1257 sysseek($pack->{handle}, $seekvalue, 0) == $seekvalue
281             }
282              
283             #- To terminate a compressed block, flush the pending compressed data,
284             #- fill toc data still unknown
285             sub end_block {
286 17     17 0 34 my ($pack) = @_;
287 17 50       71 $pack->end_seek() or return 0;
288 17         67 my (undef, $csize) = $pack->compress_handle(undef);
289 17         44 $pack->{current_block_csize} += $csize;
290 17         33 foreach (keys %{$pack->{current_block_files}}) {
  17         143  
291 64         165 $pack->{files}{$_} = $pack->{current_block_files}{$_};
292 64         156 $pack->{files}{$_}{csize} = $pack->{current_block_csize};
293             }
294 17         47 $pack->{coff} += $pack->{current_block_csize};
295 17         47 $pack->{current_block_coff} += $pack->{current_block_csize};
296 17         29 $pack->{current_block_csize} = 0;
297 17         36 $pack->{current_block_files} = {};
298 17         59 $pack->{current_block_off} = 0;
299             }
300              
301             #######################
302             # Compression wrapper #
303             #######################
304              
305             # true if wrapper writes directly in archive and not into temp file
306 53     53 0 222 sub direct_write { 0; }
307              
308             sub compress_handle {
309 49     49 0 93 my ($pack, $sourcefh) = @_;
310 49         72 my ($insize, $outsize) = (0, 0); # aka uncompressed / compressed data length
311              
312 49 100       481 if (!defined($sourcefh)) { # bloc flush call
313 4         20 return 0, $pack->compress_data();
314             } else {
315 45         1994 while (my $length = sysread($sourcefh, my $data, $pack->{bufsize})) {
316 406         1743 $outsize += $pack->compress_data($data);
317 406         64963 $insize += $length;
318             }
319 45         179 return ($insize, $outsize)
320             }
321             }
322              
323             sub compress_data {
324 410     410 0 1012 my ($pack, $data) = ($_[0], \$_[1]);
325 410         1103 my ($outsize) = (0); # aka uncompressed / compressed data length
326 410         615 my $hout; # handle for gzip
327              
328 410 100       1715 if (defined($pack->{cstream_data})) {
329 406         1330 $hout = $pack->{cstream_data}{hout};
330             }
331 410 100       986 if (defined($$data)) {
    100          
332 406 100       33879 if (!defined($pack->{cstream_data})) {
333 3         4 my $hin;
334 3         24 ($hin, $pack->{cstream_data}{file_block}) = tempfile();
335 3         44 close($hin); # ensure the flush
336             $pack->{cstream_data}{pid} = CORE::open($hout,
337 3 50       17968 "|$pack->{compress_method} > $pack->{cstream_data}{file_block}") or do {
338 0         0 $pack->{log}("Unable to start $pack->{compress_method}");
339 0         0 return 0;
340             };
341 3         52 $pack->{cstream_data}{hout} = $hout;
342 3         154 binmode $hout;
343             }
344             # until we have data to push or data to read
345             # pushing data to compressor
346 406 50       6758005 (syswrite($hout, $$data)) == length($$data) or do {
347 0         0 $pack->{log}("Can't push all data to compressor");
348             };
349 406         2233 return 0; # We can't be sure about data really written in the pipe
350             # because of multitasking and buffer, so nothing has been
351             # written
352             } elsif (defined($pack->{cstream_data})) {
353             # If $data is not set, this mean we want a flush(), for end_block()
354 3         98670 close($hout);
355 3         55 waitpid $pack->{cstream_data}{pid}, 0;
356             # copy temp bloc to archive
357 3 50       346 sysopen(my $hin, $pack->{cstream_data}{file_block}, O_RDONLY) or do {
358 0         0 $pack->{log}("Can't open temp block file: $!");
359 0         0 return 0;
360             };
361 3         329 unlink($pack->{cstream_data}{file_block});
362 3         374 while (my $length = sysread($hin, my $tdata, $pack->{bufsize})) {
363 384 50       1331268 (my $l = syswrite($pack->{handle}, $tdata)) == $length or do {
364 0         0 $pack->{log}("Can't write all data in archive");
365             };
366 384         22504 $outsize += $l;
367             }
368 3         2680732 close($hin);
369 3         31 $pack->{cstream_data} = undef;
370             # TODO current_block_csize isn't 0 ?
371 3         127 return $outsize - $pack->{current_block_csize}
372             }
373             }
374              
375             sub uncompress_handle {
376 53     53 0 138 my ($pack, $destfh, $fileinfo) = @_;
377              
378 53 100 66     704 if (defined($pack->{ustream_data}) && (
      66        
379             !defined($fileinfo) ||
380             ($fileinfo->{coff} != $pack->{ustream_data}{coff} || $fileinfo->{off} < $pack->{ustream_data}{off})
381             )) {
382 3         144 close($pack->{ustream_data}{handle});
383 3         26016 unlink($pack->{ustream_data}{tempname}); # deleting temp file
384 3         34 $pack->{ustream_data} = undef;
385             }
386              
387 53 100       256 defined($fileinfo) or return 0;
388              
389             # We have to first extract the block to a temp file, burk !
390 45 100       138 if (!defined($pack->{ustream_data})) {
391 3         6 my $tempfh;
392 3         14 $pack->{ustream_data}{coff} = $fileinfo->{coff};
393 3         20 $pack->{ustream_data}{read} = 0;
394              
395 3         18 ($tempfh, $pack->{ustream_data}{tempname}) = tempfile();
396              
397 3         8 my $cread = 0;
398 3         12 while ($cread < $fileinfo->{csize}) {
399             my $cl = sysread($pack->{handle}, my $data,
400             $cread + $pack->{bufsize} > $fileinfo->{csize} ?
401             $fileinfo->{csize} - $cread :
402 384 100       24934 $pack->{bufsize}) or do {
    50          
403 0         0 $pack->{log}("Unexpected end of file");
404 0         0 close($tempfh);
405 0         0 unlink($pack->{ustream_data}{tempname});
406 0         0 $pack->{ustream_data} = undef;
407 0         0 return -1;
408             };
409 384         444 $cread += $cl;
410 384 50       129561 syswrite($tempfh, $data) == length($data) or do {
411 0         0 $pack->{log}("Can't write all data into temp file");
412 0         0 close($tempfh);
413 0         0 unlink($pack->{ustream_data}{tempname});
414 0         0 $pack->{ustream_data} = undef;
415 0         0 return -1;
416             };
417             }
418 3         265 close($tempfh);
419              
420 3 100 100     72 my $cmd = $pack->{uncompress_method} eq 'gzip -d' || $pack->{uncompress_method} eq 'bzip2 -d' ?
421             "$pack->{uncompress_method} -c '$pack->{ustream_data}{tempname}'" :
422             "$pack->{uncompress_method} < '$pack->{ustream_data}{tempname}'";
423 3 50       14177 CORE::open($pack->{ustream_data}{handle}, "$cmd |") or do {
424 0         0 $pack->{log}("Can't start $pack->{uncompress_method} to uncompress data");
425 0         0 unlink($pack->{ustream_data}{tempname});
426 0         0 $pack->{ustream_data} = undef;
427 0         0 return -1;
428             };
429 3         175 binmode($pack->{ustream_data}{handle});
430             }
431              
432 45         129 my $byteswritten = 0;
433 45         138 $pack->{ustream_data}{off} = $fileinfo->{off};
434              
435 45         175 while ($byteswritten < $fileinfo->{size}) {
436 1809         4137 my $data = $pack->{ustream_data}{buf};
437 1809         2888 $pack->{ustream_data}{buf} = undef;
438 1809         1790 my $length;
439 1809 100       3611 if (!defined($data)) {
440 1771 50       1711370 $length = sysread($pack->{ustream_data}{handle}, $data, $pack->{bufsize}) or do {
441 0         0 $pack->{log}("Unexpected end of stream $pack->{ustream_data}{tempname}");
442 0         0 unlink($pack->{ustream_data}{tempname});
443 0         0 close($pack->{ustream_data}{handle});
444 0         0 $pack->{ustream_data} = undef;
445 0         0 return -1;
446             };
447             } else {
448 38         66 $length = length($data);
449             }
450              
451 1809 50 33     9339 if ($pack->{ustream_data}{read} < $fileinfo->{off} && $pack->{ustream_data}{read} + $length > $fileinfo->{off}) {
452 0         0 $data = substr($data, $fileinfo->{off} - $pack->{ustream_data}{read});
453             }
454 1809         3473 $pack->{ustream_data}{read} += $length;
455 1809 50       4778 if ($pack->{ustream_data}{read} <= $fileinfo->{off}) { next }
  0         0  
456              
457 1809         2407 my $bw;
458 1809 100       5345 if ($byteswritten + length($data) > $fileinfo->{size}) {
459 38         86 $bw = $fileinfo->{size} - $byteswritten;
460 38         800 $pack->{ustream_data}{buf} = substr($data, $bw); # keeping track of unwritten uncompressed data
461 38         103 $pack->{ustream_data}{read} -= length($pack->{ustream_data}{buf});
462             } else {
463 1771         2446 $bw = length($data);
464             }
465              
466 1809 50       199282 syswrite($destfh, $data, $bw) == $bw or do {
467 0         0 $pack->{log}("Can't write data into dest");
468 0         0 return -1;
469             };
470 1809         5800 $byteswritten += $bw;
471             }
472              
473             $byteswritten
474              
475 45         362 }
476              
477             ###################
478             # Debug functions #
479             ###################
480              
481             # This function extracts in $dest the whole block containing $file, can be useful for debugging
482             sub extract_block {
483 0     0 0 0 my ($pack, $dest, $file) = @_;
484              
485 0 0       0 sysopen(my $handle, $dest, O_WRONLY | O_TRUNC | O_CREAT) or do {
486 0         0 $pack->{log}("Can't open $dest: $!");
487 0         0 return -1;
488             };
489              
490 0 0       0 sysseek($pack->{handle}, $pack->{files}{$file}->{coff}, 0) == $pack->{files}{$file}->{coff} or do {
491 0         0 $pack->{log}("Can't seek to offset $pack->{files}{$file}->{coff}");
492 0         0 close($handle);
493 0         0 return -1;
494             };
495              
496             {
497 0         0 my $l;
  0         0  
498 0 0       0 $l = sysread($pack->{handle}, my $buf, $pack->{files}{$file}->{csize}) == $pack->{files}{$file}{csize}
499             or $pack->{log}("Read only $l / $pack->{files}{$file}->{csize} bytes");
500 0         0 syswrite($handle, $buf);
501             }
502              
503 0         0 foreach ($pack->sort_files_by_packing(keys %{$pack->{files}})) {
  0         0  
504 0 0       0 $pack->{files}{$_}{coff} == $pack->{files}{$file}->{coff} or next;
505             }
506              
507 0         0 close($handle);
508              
509             }
510              
511             ##################################
512             # Really working functions #
513             # Aka function people should use #
514             ##################################
515              
516             sub add_virtual {
517 68     68 1 284 my ($pack, $type, $filename, $data) = @_;
518 68 100       229 $type eq 'l' and do {
519 2         8 $pack->{'symlink'}{$filename} = $data;
520 2         6 $pack->{need_build_toc} = 1;
521 2         9 return 1;
522             };
523 66 100       161 $type eq 'd' and do {
524 2         10 $pack->{dir}{$filename}++;
525 2         4 $pack->{need_build_toc} = 1;
526 2         11 return 1;
527             };
528 64 50       168 $type eq 'f' and do {
529             # Be sure we are at the end, allow extract + add in only one instance
530 64 50       264 $pack->end_seek() or do {
531 0         0 $pack->{log}("Can't seek to offset $pack->{coff}");
532 0         0 next;
533             };
534              
535 64 100       390 my ($size, $csize) = (ref($data) eq 'GLOB') ?
536             $pack->compress_handle($data) :
537             (length($data), $pack->compress_data($data));
538 64         1077 $pack->{current_block_files}{$filename} = {
539             size => $size,
540             off => $pack->{current_block_off},
541             coff => $pack->{current_block_coff},
542             csize => -1, # Still unknown, will be fill by end_block
543             }; # Storing in toc structure availlable info
544              
545             # Updating internal info about current block
546 64         213 $pack->{current_block_off} += $size;
547 64         167 $pack->{current_block_csize} += $csize;
548 64         150 $pack->{need_build_toc} = 1;
549 64 100 66     563 if ($pack->{block_size} > 0 && $pack->{current_block_csize} >= $pack->{block_size}) {
550 9         90 $pack->end_block();
551             }
552 64         239 return 1;
553             };
554 0         0 0
555             }
556              
557             sub add {
558 4     4 1 23 my ($pack, $prefix, @files) = @_;
559 4   50     78 $prefix ||= "";
560 4         21 foreach my $file (@files) {
561 60         256 $file =~ s://+:/:;
562 60 50       262 my $srcfile = $prefix ? "$prefix/$file" : $file;
563 60         325 $pack->{debug}("Adding '%s' as '%s' into archive", $srcfile, $file);
564              
565 60 50       1879 -l $srcfile and do {
566 0         0 $pack->add_virtual('l', $file, readlink($srcfile));
567 0         0 next;
568             };
569 60 50       763 -d $srcfile and do { # dir simple case
570 0         0 $pack->add_virtual('d', $file);
571 0         0 next;
572             };
573 60 50       675 -f $srcfile and do {
574 60 50       3479 sysopen(my $htocompress, $srcfile, O_RDONLY) or do {
575 0         0 $pack->{log}("Can't add $srcfile: $!");
576 0         0 next;
577             };
578 60         303 $pack->add_virtual('f', $file, $htocompress);
579 60         2275 close($htocompress);
580 60         621 next;
581             };
582 0         0 $pack->{log}("Can't pack $srcfile");
583             }
584 4         154 1;
585             }
586              
587             sub extract_virtual {
588 64     64 1 208 my ($pack, $destfh, $filename) = @_;
589 64 50       208 defined($pack->{files}{$filename}) or return -1;
590 64 50       714 sysseek($pack->{handle}, $pack->{files}{$filename}->{coff}, 0) == $pack->{files}{$filename}->{coff} or do {
591 0         0 $pack->{log}("Can't seek to offset $pack->{files}{$filename}->{coff}");
592 0         0 return -1;
593             };
594 64         316 $pack->uncompress_handle($destfh, $pack->{files}{$filename});
595             }
596              
597             sub extract {
598 8     8 1 961 my ($pack, $destdir, @files) = @_;
599 8         48 foreach my $f ($pack->sort_files_by_packing(@files)) {
600 64 50       470 my $dest = $destdir ? "$destdir/$f" : "$f";
601 64         932 my ($dir) = $dest =~ m!(.*)/.*!;
602 64   50     224 $dir ||= ".";
603 64 100       549 if (exists($pack->{dir}{$f})) {
    100          
    50          
604 2 50 33     565 -d $dest || mkpath($dest)
605             or $pack->{log}("Unable to create dir $dest: $!");
606 2         7 next;
607             } elsif (exists($pack->{'symlink'}{$f})) {
608 2 50 33     31 -d $dir || mkpath($dir) or
609             $pack->{log}("Unable to create dir $dest: $!");
610 2 50       35 -l $dest and unlink $dest;
611 2 50       105 symlink($pack->{'symlink'}{$f}, $dest)
612             or $pack->{log}("Unable to extract symlink $f: $!");
613 2         8 next;
614             } elsif (exists($pack->{files}{$f})) {
615 60 50 33     1769 -d $dir || mkpath($dir) or do {
616 0         0 $pack->{log}("Unable to create dir $dir");
617             };
618 60 50       1812 if (-l $dest) {
619 0 0       0 unlink($dest) or do {
620 0         0 $pack->{log}("Can't remove link $dest: $!");
621 0         0 next; # Don't overwrite a file because where the symlink point to
622             };
623             }
624 60         100 my $destfh;
625 60 50       150 if (defined $destdir) {
626 60 50       6694 sysopen($destfh, $dest, O_CREAT | O_TRUNC | O_WRONLY) or do {
627 0         0 $pack->{log}("Unable to extract $dest: $!");
628 0         0 next;
629             };
630             } else {
631 0         0 $destfh = \*STDOUT;
632             }
633 60         332 my $written = $pack->extract_virtual($destfh, $f);
634 60 50       194 $written == -1 and $pack->{log}("Unable to extract file $f");
635 60         1855 close($destfh);
636 60         645 next;
637             } else {
638 0         0 $pack->{log}("Can't find $f in archive");
639             }
640             }
641 8         173 1;
642             }
643              
644             # Return \@dir, \@files, \@symlink list
645             sub getcontent {
646 0     0 1 0 my ($pack) = @_;
647             return(
648 0         0 [ keys(%{$pack->{dir}})],
  0         0  
649 0         0 [ $pack->sort_files_by_packing(keys %{$pack->{files}}) ],
650 0         0 [ keys(%{$pack->{'symlink'}}) ]
651             );
652             }
653              
654             sub infofile {
655 8     8 1 3050 my ($pack, $file) = @_;
656 8 100       44 if (defined($pack->{files}{$file})) {
    100          
    100          
657 2         11 return ('f', $pack->{files}{$file}{size});
658             } elsif (defined($pack->{'symlink'}{$file})) {
659 2         16 return ('l', $pack->{'symlink'}{$file});
660             } elsif (defined($pack->{dir}{$file})) {
661 2         17 return ('d', undef);
662             } else {
663 2         8 return(undef, undef);
664             }
665             }
666              
667             sub list {
668 0     0 0   my ($pack, $handle) = @_;
669 0   0       $handle ||= *STDOUT;
670 0           foreach my $file (keys %{$pack->{dir}}) {
  0            
671 0           printf "d %13c %s\n", ' ', $file;
672             }
673 0           foreach my $file (keys %{$pack->{'symlink'}}) {
  0            
674 0           printf "l %13c %s -> %s\n", ' ', $file, $pack->{'symlink'}{$file};
675             }
676 0           foreach my $file ($pack->sort_files_by_packing(keys %{$pack->{files}})) {
  0            
677 0           printf "f %12d %s\n", $pack->{files}{$file}{size}, $file;
678             }
679             }
680              
681             # Print toc info
682             sub dumptoc {
683 0     0 1   my ($pack, $handle) = @_;
684 0   0       $handle ||= *STDOUT;
685 0           foreach my $file (keys %{$pack->{dir}}) {
  0            
686 0           printf $handle "d %13c %s\n", ' ', $file;
687             }
688 0           foreach my $file (keys %{$pack->{'symlink'}}) {
  0            
689 0           printf $handle "l %13c %s -> %s\n", ' ', $file, $pack->{'symlink'}{$file};
690             }
691 0           foreach my $file ($pack->sort_files_by_packing(keys %{$pack->{files}})) {
  0            
692 0           printf $handle "f %d %d %d %d %s\n", $pack->{files}{$file}{size}, $pack->{files}{$file}{off}, $pack->{files}{$file}{csize}, $pack->{files}{$file}{coff}, $file;
693             }
694             }
695              
696             1;
697              
698             __END__