File Coverage

lib/Archive/Any/Lite.pm
Criterion Covered Total %
statement 78 101 77.2
branch 31 62 50.0
condition 15 21 71.4
subroutine 15 17 88.2
pod 6 6 100.0
total 145 207 70.0


line stmt bran cond sub pod time code
1             package Archive::Any::Lite;
2            
3 4     4   51511 use strict;
  4         6  
  4         96  
4 4     4   11 use warnings;
  4         5  
  4         74  
5 4     4   11 use File::Spec;
  4         9  
  4         1649  
6            
7             our $VERSION = '0.11';
8             our $IGNORE_SYMLINK;
9            
10             sub new {
11 11     11 1 9731 my ($class, $file, $opts) = @_;
12            
13 11         314 $file = File::Spec->rel2abs($file);
14 11 100       234 unless (-f $file) {
15 1         34 warn "$file not found\n";
16 1         7 return;
17             }
18            
19             # just for undocumented backward compat
20 10 50       31 my $type = !ref $opts ? $opts : '';
21            
22             # XXX: trust file extensions until I manage to make File::MMagic
23             # more reliable while fork()ing or I happen to find a decent
24             # and portable alternative to File::MMagic.
25            
26 10 50 100     145 my $handler =
    100 66        
27             ($type && lc $type eq 'tar') || $file =~ /\.(?:tar|tar\.(?:gz|bz2)|gtar|tgz)$/ ? 'Archive::Any::Lite::Tar' :
28             ($type && lc $type eq 'zip') || $file =~ /\.(?:zip)$/ ? 'Archive::Any::Lite::Zip' : undef;
29 10 50       26 unless ($handler) {
30 0         0 warn "No handler available for $file\n";
31 0         0 return;
32             }
33            
34             bless {
35 10 50       61 file => $file,
36             handler => $handler,
37             opts => ref $opts ? $opts : undef,
38             }, $class;
39             }
40            
41             sub extract {
42 6     6 1 15 my ($self, $dir, $opts) = @_;
43            
44 6   33     49 $self->{handler}->extract($self->{file}, $dir, $opts || $self->{opts});
45             }
46            
47             sub files {
48 42     42 1 4123 my $self = shift;
49 42         196 $self->{handler}->files($self->{file});
50             }
51            
52             sub is_impolite {
53 16     16 1 28 my $self = shift;
54            
55 16         31 my @files = $self->files;
56 16         52397 my $first_file = $files[0];
57 16         154 my ($first_dir) = File::Spec->splitdir($first_file);
58            
59 16 100       288 return grep( !/^\Q$first_dir\E/, @files ) ? 1 : 0;
60             }
61            
62             sub is_naughty {
63 14     14 1 49 my ($self) = shift;
64 14 100       30 return ( grep { m{^(?:/|(?:\./)*\.\./)} } $self->files ) ? 1 : 0;
  104         44807  
65             }
66            
67             sub type {
68 8     8 1 31180 my $self = shift;
69 8         53 my ($type) = lc $self->{handler} =~ /::(\w+)$/;
70 8         26 return $type;
71             }
72            
73             package Archive::Any::Lite::Tar;
74 4     4   2619 use Archive::Tar;
  4         276678  
  4         1719  
75            
76             sub files {
77 30     30   34 my ($self, $file) = @_;
78 30         111 Archive::Tar->list_archive($file);
79             }
80            
81             sub extract {
82 3     3   7 my ($self, $file, $dir, $opts) = @_;
83 3 100       10 $dir = '.' unless defined $dir;
84 3         93 $dir = File::Spec->rel2abs($dir);
85 3         26 my $tar = Archive::Tar->new;
86 3         25 my $fh;
87 3 50       20 if ($file =~ /\.(tgz|tar\.gz)$/) {
    0          
88 3         24 require IO::Zlib;
89 3 50       35 $fh = IO::Zlib->new($file, "rb") or do { warn "$file: $!"; return };
  0         0  
  0         0  
90             }
91             elsif ($file =~ /\.tar.bz2$/) {
92 0         0 require IO::Uncompress::Bunzip2;
93 0 0       0 $fh = IO::Uncompress::Bunzip2->new($file) or do { warn "$file: $!"; return };
  0         0  
  0         0  
94             }
95             else {
96 0 0       0 open $fh, '<', $file or do { warn "$file: $!"; return };
  0         0  
  0         0  
97 0         0 binmode $fh;
98             }
99            
100             # Archive::Tar is too noisy when an archive has minor glitches.
101             # Note also that $file can't hold the last error.
102 3         3827 local $Archive::Tar::WARN;
103 3         7 my %errors;
104             my $has_extracted;
105 3         11 my %read_opts = (limit => 1);
106 3 50       12 if ($opts) {
107 0         0 for (qw/limit md5 filter filter_cb extract/) {
108 0 0       0 if (exists $opts->{"tar_$_"}) {
    0          
109 0         0 $read_opts{$_} = $opts->{"tar_$_"};
110             }
111             elsif (exists $opts->{$_}) {
112 0         0 $read_opts{$_} = $opts->{$_};
113             }
114             }
115             }
116 3         30 until (eof $fh) {
117 17         423 my @files = $tar->read($fh, undef, \%read_opts);
118 17 50       10645 if (my $error = $tar->error) {
119 0 0       0 warn $error unless $errors{$error}++;
120             }
121 17 50 66     118 if (!@files && !$has_extracted) {
122 0         0 warn "No data could be read from $file";
123 0         0 return;
124             }
125 17         25 for my $file (@files) {
126 20 100 66     1256 next if $IGNORE_SYMLINK && ($file->is_symlink or $file->is_hardlink);
      66        
127 19         55 my $path = File::Spec->catfile($dir, $file->prefix, $file->name);
128 19 50       335 $tar->extract_file($file, File::Spec->canonpath($path)) or do {
129 0 0       0 if (my $error = $tar->error) {
130 0 0       0 warn $error unless $errors{$error}++;
131             }
132             };
133             }
134 17         4338 $has_extracted += @files;
135             }
136 3 50       206 return if %errors;
137 3         32 return 1;
138             }
139            
140 0     0   0 sub type { 'tar' }
141            
142             package Archive::Any::Lite::Zip;
143 4     4   2649 use Archive::Zip qw/:ERROR_CODES/;
  4         207354  
  4         896  
144            
145             sub files {
146 12     12   15 my ($self, $file) = @_;
147 12 50       42 my $zip = Archive::Zip->new($file) or return;
148 12         21849 $zip->memberNames;
149             }
150            
151             sub extract {
152 3     3   6 my ($self, $file, $dir, $opts) = @_;
153 3 50       16 my $zip = Archive::Zip->new($file) or return;
154 3 100       4308 $dir = '.' unless defined $dir;
155 3         5 my $error = 0;
156 3         11 for my $member ($zip->members) {
157 20 100 100     64 next if $IGNORE_SYMLINK && $member->isSymbolicLink;
158 19         53 my $path = File::Spec->catfile($dir, $member->fileName);
159 19         295 my $ret = $member->extractToFileNamed(File::Spec->canonpath($path));
160 19 50       12721 $error++ if $ret != AZ_OK;
161             }
162 3 50       16 return if $error;
163 3         59 return 1;
164             }
165            
166 0     0     sub type { 'zip' }
167            
168             1;
169            
170             __END__