line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
########################################################### |
2
|
|
|
|
|
|
|
# Archive::Ar - Pure perl module to handle ar achives |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# Copyright 2003 - Jay Bonci |
5
|
|
|
|
|
|
|
# Copyright 2014 - John Bazik |
6
|
|
|
|
|
|
|
# Licensed under the same terms as perl itself |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
########################################################### |
9
|
|
|
|
|
|
|
package Archive::Ar; |
10
|
|
|
|
|
|
|
|
11
|
15
|
|
|
15
|
|
780182
|
use base qw(Exporter); |
|
15
|
|
|
|
|
41
|
|
|
15
|
|
|
|
|
2420
|
|
12
|
|
|
|
|
|
|
our @EXPORT_OK = qw(COMMON BSD GNU); |
13
|
|
|
|
|
|
|
|
14
|
15
|
|
|
15
|
|
92
|
use strict; |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
564
|
|
15
|
15
|
|
|
15
|
|
96
|
use File::Spec; |
|
15
|
|
|
|
|
24
|
|
|
15
|
|
|
|
|
337
|
|
16
|
15
|
|
|
15
|
|
29097
|
use Time::Local; |
|
15
|
|
|
|
|
37545
|
|
|
15
|
|
|
|
|
1084
|
|
17
|
15
|
|
|
15
|
|
116
|
use Carp qw(carp longmess); |
|
15
|
|
|
|
|
32
|
|
|
15
|
|
|
|
|
955
|
|
18
|
|
|
|
|
|
|
|
19
|
15
|
|
|
15
|
|
78
|
use vars qw($VERSION); |
|
15
|
|
|
|
|
33
|
|
|
15
|
|
|
|
|
1026
|
|
20
|
|
|
|
|
|
|
$VERSION = '2.02'; |
21
|
|
|
|
|
|
|
|
22
|
15
|
|
33
|
15
|
|
76
|
use constant CAN_CHOWN => ($> == 0 and $^O ne 'MacOS' and $^O ne 'MSWin32'); |
|
15
|
|
|
|
|
30
|
|
|
15
|
|
|
|
|
1128
|
|
23
|
|
|
|
|
|
|
|
24
|
15
|
|
|
15
|
|
74
|
use constant ARMAG => "!\n"; |
|
15
|
|
|
|
|
26
|
|
|
15
|
|
|
|
|
822
|
|
25
|
15
|
|
|
15
|
|
79
|
use constant SARMAG => length(ARMAG); |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
850
|
|
26
|
15
|
|
|
15
|
|
244
|
use constant ARFMAG => "`\n"; |
|
15
|
|
|
|
|
28
|
|
|
15
|
|
|
|
|
1931
|
|
27
|
15
|
|
|
15
|
|
76
|
use constant AR_EFMT1 => "#1/"; |
|
15
|
|
|
|
|
26
|
|
|
15
|
|
|
|
|
719
|
|
28
|
|
|
|
|
|
|
|
29
|
15
|
|
|
15
|
|
72
|
use constant COMMON => 1; |
|
15
|
|
|
|
|
29
|
|
|
15
|
|
|
|
|
616
|
|
30
|
15
|
|
|
15
|
|
73
|
use constant BSD => 2; |
|
15
|
|
|
|
|
33
|
|
|
15
|
|
|
|
|
862
|
|
31
|
15
|
|
|
15
|
|
73
|
use constant GNU => 3; |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
1194
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my $has_io_string; |
34
|
|
|
|
|
|
|
BEGIN { |
35
|
15
|
|
50
|
15
|
|
56
|
$has_io_string = eval { |
36
|
|
|
|
|
|
|
require IO::String; |
37
|
|
|
|
|
|
|
IO::String->import(); |
38
|
|
|
|
|
|
|
1; |
39
|
|
|
|
|
|
|
} || 0; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub new { |
43
|
26
|
|
|
26
|
1
|
50024
|
my $class = shift; |
44
|
26
|
|
|
|
|
60
|
my $file = shift; |
45
|
26
|
|
100
|
|
|
196
|
my $opts = shift || 0; |
46
|
26
|
|
|
|
|
87
|
my $self = bless {}, $class; |
47
|
26
|
50
|
|
|
|
295
|
my $defopts = { |
48
|
|
|
|
|
|
|
chmod => 1, |
49
|
|
|
|
|
|
|
chown => 1, |
50
|
|
|
|
|
|
|
same_perms => ($> == 0) ? 1:0, |
51
|
|
|
|
|
|
|
symbols => undef, |
52
|
|
|
|
|
|
|
}; |
53
|
26
|
50
|
|
|
|
138
|
$opts = {warn => $opts} unless ref $opts; |
54
|
|
|
|
|
|
|
|
55
|
26
|
|
|
|
|
107
|
$self->clear(); |
56
|
26
|
|
|
|
|
91
|
$self->{opts} = {(%$defopts, %{$opts})}; |
|
26
|
|
|
|
|
129
|
|
57
|
26
|
100
|
|
|
|
101
|
if ($file) { |
58
|
10
|
100
|
|
|
|
37
|
return unless $self->read($file); |
59
|
|
|
|
|
|
|
} |
60
|
23
|
|
|
|
|
111
|
return $self; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub set_opt { |
64
|
1
|
|
|
1
|
1
|
379
|
my $self = shift; |
65
|
1
|
|
|
|
|
2
|
my $name = shift; |
66
|
1
|
|
|
|
|
2
|
my $val = shift; |
67
|
|
|
|
|
|
|
|
68
|
1
|
|
|
|
|
3
|
$self->{opts}->{$name} = $val; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub get_opt { |
72
|
3
|
|
|
3
|
1
|
348
|
my $self = shift; |
73
|
3
|
|
|
|
|
3
|
my $name = shift; |
74
|
|
|
|
|
|
|
|
75
|
3
|
|
|
|
|
18
|
return $self->{opts}->{$name}; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub type { |
79
|
3
|
|
|
3
|
1
|
25
|
return shift->{type}; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub clear { |
83
|
41
|
|
|
41
|
1
|
68
|
my $self = shift; |
84
|
|
|
|
|
|
|
|
85
|
41
|
|
|
|
|
215
|
$self->{names} = []; |
86
|
41
|
|
|
|
|
104
|
$self->{files} = {}; |
87
|
41
|
|
|
|
|
523
|
$self->{type} = undef; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub read { |
91
|
7
|
|
|
7
|
1
|
25
|
my $self = shift; |
92
|
7
|
|
|
|
|
11
|
my $file = shift; |
93
|
|
|
|
|
|
|
|
94
|
7
|
|
|
|
|
41
|
my $fh = $self->_get_handle($file); |
95
|
7
|
|
|
|
|
28
|
local $/ = undef; |
96
|
7
|
|
|
|
|
183
|
my $data = <$fh>; |
97
|
7
|
|
|
|
|
75
|
close $fh; |
98
|
|
|
|
|
|
|
|
99
|
7
|
|
|
|
|
31
|
return $self->read_memory($data); |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub read_memory { |
103
|
15
|
|
|
15
|
1
|
967
|
my $self = shift; |
104
|
15
|
|
|
|
|
26
|
my $data = shift; |
105
|
|
|
|
|
|
|
|
106
|
15
|
|
|
|
|
48
|
$self->clear(); |
107
|
15
|
50
|
|
|
|
66
|
return unless $self->_parse($data); |
108
|
15
|
|
|
|
|
140
|
return length($data); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub contains_file { |
112
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
113
|
0
|
|
|
|
|
0
|
my $filename = shift; |
114
|
|
|
|
|
|
|
|
115
|
0
|
0
|
|
|
|
0
|
return unless defined $filename; |
116
|
0
|
|
|
|
|
0
|
return exists $self->{files}->{$filename}; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub extract { |
120
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
121
|
|
|
|
|
|
|
|
122
|
1
|
50
|
|
|
|
4
|
for my $filename (@_ ? @_ : @{$self->{names}}) { |
|
1
|
|
|
|
|
6
|
|
123
|
2
|
50
|
|
|
|
6
|
$self->extract_file($filename) or return; |
124
|
|
|
|
|
|
|
} |
125
|
1
|
|
|
|
|
6
|
return 1; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub extract_file { |
129
|
2
|
|
|
2
|
0
|
3
|
my $self = shift; |
130
|
2
|
|
|
|
|
3
|
my $filename = shift; |
131
|
2
|
|
33
|
|
|
11
|
my $target = shift || $filename; |
132
|
|
|
|
|
|
|
|
133
|
2
|
|
|
|
|
6
|
my $meta = $self->{files}->{$filename}; |
134
|
2
|
50
|
|
|
|
5
|
return $self->_error("$filename: not in archive") unless $meta; |
135
|
2
|
50
|
|
|
|
183
|
open my $fh, '>', $target or return $self->_error("$target: $!"); |
136
|
2
|
|
|
|
|
6
|
binmode $fh; |
137
|
2
|
50
|
|
|
|
91
|
syswrite $fh, $meta->{data} or return $self->_error("$filename: $!"); |
138
|
2
|
50
|
|
|
|
26
|
close $fh or return $self->_error("$filename: $!"); |
139
|
2
|
50
|
|
|
|
8
|
if (CAN_CHOWN && $self->{opts}->{chown}) { |
140
|
2
|
50
|
|
|
|
53
|
chown $meta->{uid}, $meta->{gid}, $filename or |
141
|
|
|
|
|
|
|
return $self->_error("$filename: $!"); |
142
|
|
|
|
|
|
|
} |
143
|
2
|
50
|
|
|
|
7
|
if ($self->{opts}->{chmod}) { |
144
|
2
|
|
|
|
|
5
|
my $mode = $meta->{mode}; |
145
|
2
|
50
|
|
|
|
12
|
unless ($self->{opts}->{same_perms}) { |
146
|
0
|
|
|
|
|
0
|
$mode &= ~(oct(7000) | (umask | 0)); |
147
|
|
|
|
|
|
|
} |
148
|
2
|
50
|
|
|
|
38
|
chmod $mode, $filename or return $self->_error("$filename: $!"); |
149
|
|
|
|
|
|
|
} |
150
|
2
|
50
|
|
|
|
46
|
utime $meta->{date}, $meta->{date}, $filename or |
151
|
|
|
|
|
|
|
return $self->_error("$filename: $!"); |
152
|
2
|
|
|
|
|
16
|
return 1; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub rename { |
156
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
157
|
2
|
|
|
|
|
5
|
my $filename = shift; |
158
|
2
|
|
|
|
|
3
|
my $target = shift; |
159
|
|
|
|
|
|
|
|
160
|
2
|
50
|
|
|
|
10
|
if ($self->{files}->{$filename}) { |
161
|
2
|
|
|
|
|
6
|
$self->{files}->{$target} = $self->{files}->{$filename}; |
162
|
2
|
|
|
|
|
6
|
delete $self->{files}->{$filename}; |
163
|
2
|
|
|
|
|
3
|
for (@{$self->{names}}) { |
|
2
|
|
|
|
|
5
|
|
164
|
5
|
100
|
|
|
|
12
|
if ($_ eq $filename) { |
165
|
2
|
|
|
|
|
4
|
$_ = $target; |
166
|
2
|
|
|
|
|
5
|
last; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub chmod { |
173
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
174
|
0
|
|
|
|
|
0
|
my $filename = shift; |
175
|
0
|
|
|
|
|
0
|
my $mode = shift; # octal string or numeric |
176
|
|
|
|
|
|
|
|
177
|
0
|
0
|
|
|
|
0
|
return unless $self->{files}->{$filename}; |
178
|
0
|
0
|
|
|
|
0
|
$self->{files}->{$filename}->{mode} = |
179
|
|
|
|
|
|
|
$mode + 0 eq $mode ? $mode : oct($mode); |
180
|
0
|
|
|
|
|
0
|
return 1; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub chown { |
184
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
185
|
0
|
|
|
|
|
0
|
my $filename = shift; |
186
|
0
|
|
|
|
|
0
|
my $uid = shift; |
187
|
0
|
|
|
|
|
0
|
my $gid = shift; |
188
|
|
|
|
|
|
|
|
189
|
0
|
0
|
|
|
|
0
|
return unless $self->{files}->{$filename}; |
190
|
0
|
0
|
|
|
|
0
|
$self->{files}->{$filename}->{uid} = $uid if $uid >= 0; |
191
|
0
|
0
|
0
|
|
|
0
|
$self->{files}->{$filename}->{gid} = $gid if defined $gid && $gid >= 0; |
192
|
0
|
|
|
|
|
0
|
return 1; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub remove { |
196
|
2
|
|
|
2
|
1
|
983
|
my $self = shift; |
197
|
2
|
100
|
|
|
|
8
|
my $files = ref $_[0] ? shift : \@_; |
198
|
|
|
|
|
|
|
|
199
|
2
|
|
|
|
|
3
|
my $nfiles_orig = scalar @{$self->{names}}; |
|
2
|
|
|
|
|
4
|
|
200
|
|
|
|
|
|
|
|
201
|
2
|
|
|
|
|
5
|
for my $file (@$files) { |
202
|
4
|
50
|
|
|
|
10
|
next unless $file; |
203
|
4
|
50
|
|
|
|
11
|
if (exists($self->{files}->{$file})) { |
204
|
4
|
|
|
|
|
19
|
delete $self->{files}->{$file}; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
else { |
207
|
0
|
|
|
|
|
0
|
$self->_error("$file: no such member") |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
} |
210
|
2
|
|
|
|
|
3
|
@{$self->{names}} = grep($self->{files}->{$_}, @{$self->{names}}); |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
10
|
|
211
|
|
|
|
|
|
|
|
212
|
2
|
|
|
|
|
3
|
return $nfiles_orig - scalar @{$self->{names}}; |
|
2
|
|
|
|
|
9
|
|
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub list_files { |
216
|
17
|
|
|
17
|
1
|
3067
|
my $self = shift; |
217
|
|
|
|
|
|
|
|
218
|
17
|
100
|
|
|
|
80
|
return wantarray ? @{$self->{names}} : $self->{names}; |
|
8
|
|
|
|
|
52
|
|
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub add_files { |
222
|
2
|
|
|
2
|
1
|
72
|
my $self = shift; |
223
|
2
|
50
|
|
|
|
7
|
my $files = ref $_[0] ? shift : \@_; |
224
|
|
|
|
|
|
|
|
225
|
2
|
|
|
|
|
6
|
for my $path (@$files) { |
226
|
6
|
50
|
|
|
|
280
|
if (open my $fd, $path) { |
227
|
6
|
50
|
|
|
|
66
|
my @st = stat $fd or return $self->_error("$path: $!"); |
228
|
6
|
|
|
|
|
29
|
local $/ = undef; |
229
|
6
|
|
|
|
|
12
|
binmode $fd; |
230
|
6
|
|
|
|
|
137
|
my $content = <$fd>; |
231
|
6
|
|
|
|
|
60
|
close $fd; |
232
|
|
|
|
|
|
|
|
233
|
6
|
|
|
|
|
89
|
my $filename = (File::Spec->splitpath($path))[2]; |
234
|
|
|
|
|
|
|
|
235
|
6
|
|
|
|
|
25
|
$self->_add_data($filename, $content, @st[9,4,5,2,7]); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
else { |
238
|
0
|
|
|
|
|
0
|
$self->_error("$path: $!"); |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
} |
241
|
2
|
|
|
|
|
3
|
return scalar @{$self->{names}}; |
|
2
|
|
|
|
|
10
|
|
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub add_data { |
245
|
8
|
|
|
8
|
1
|
35
|
my $self = shift; |
246
|
8
|
|
|
|
|
12
|
my $path = shift; |
247
|
8
|
|
|
|
|
11
|
my $content = shift; |
248
|
8
|
|
100
|
|
|
34
|
my $params = shift || {}; |
249
|
|
|
|
|
|
|
|
250
|
8
|
50
|
|
|
|
19
|
return $self->_error("No filename given") unless $path; |
251
|
|
|
|
|
|
|
|
252
|
8
|
|
|
|
|
111
|
my $filename = (File::Spec->splitpath($path))[2]; |
253
|
|
|
|
|
|
|
|
254
|
8
|
50
|
33
|
|
|
248
|
$self->_add_data($filename, $content, |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
255
|
|
|
|
|
|
|
$params->{date} || timelocal(localtime()), |
256
|
|
|
|
|
|
|
$params->{uid} || 0, |
257
|
|
|
|
|
|
|
$params->{gid} || 0, |
258
|
|
|
|
|
|
|
$params->{mode} || 0100644) or return; |
259
|
|
|
|
|
|
|
|
260
|
8
|
|
|
|
|
43
|
return $self->{files}->{$filename}->{size}; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub write { |
264
|
7
|
|
|
7
|
1
|
4146
|
my $self = shift; |
265
|
7
|
|
|
|
|
15
|
my $filename = shift; |
266
|
7
|
50
|
|
|
|
12
|
my $opts = {(%{$self->{opts}}, %{shift || {}})}; |
|
7
|
|
|
|
|
33
|
|
|
7
|
|
|
|
|
89
|
|
267
|
7
|
|
100
|
|
|
71
|
my $type = $opts->{type} || $self->{type} || COMMON; |
268
|
|
|
|
|
|
|
|
269
|
7
|
|
|
|
|
21
|
my @body = ( ARMAG ); |
270
|
|
|
|
|
|
|
|
271
|
7
|
|
|
|
|
13
|
my %gnuindex; |
272
|
7
|
|
|
|
|
13
|
my @filenames = @{$self->{names}}; |
|
7
|
|
|
|
|
22
|
|
273
|
7
|
100
|
|
|
|
47
|
if ($type eq GNU) { |
274
|
|
|
|
|
|
|
# |
275
|
|
|
|
|
|
|
# construct extended filename index, if needed |
276
|
|
|
|
|
|
|
# |
277
|
3
|
50
|
|
|
|
19
|
if (my @longs = grep(length($_) > 15, @filenames)) { |
278
|
3
|
|
|
|
|
6
|
my $ptr = 0; |
279
|
3
|
|
|
|
|
7
|
for my $long (@longs) { |
280
|
3
|
|
|
|
|
7
|
$gnuindex{$long} = $ptr; |
281
|
3
|
|
|
|
|
10
|
$ptr += length($long) + 2; |
282
|
|
|
|
|
|
|
} |
283
|
3
|
|
|
|
|
24
|
push @body, pack('A16A32A10A2', '//', '', $ptr, ARFMAG), |
284
|
|
|
|
|
|
|
join("/\n", @longs, ''); |
285
|
3
|
100
|
|
|
|
14
|
push @body, "\n" if $ptr % 2; # padding |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
} |
288
|
7
|
|
|
|
|
28
|
for my $fn (@filenames) { |
289
|
13
|
|
|
|
|
35
|
my $meta = $self->{files}->{$fn}; |
290
|
13
|
|
|
|
|
52
|
my $mode = sprintf('%o', $meta->{mode}); |
291
|
13
|
|
|
|
|
27
|
my $size = $meta->{size}; |
292
|
13
|
|
|
|
|
15
|
my $name; |
293
|
|
|
|
|
|
|
|
294
|
13
|
100
|
|
|
|
44
|
if ($type eq GNU) { |
295
|
7
|
100
|
100
|
|
|
29
|
$fn = '' if defined $opts->{symbols} && $fn eq $opts->{symbols}; |
296
|
7
|
|
|
|
|
12
|
$name = $fn . '/'; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
else { |
299
|
6
|
|
|
|
|
13
|
$name = $fn; |
300
|
|
|
|
|
|
|
} |
301
|
13
|
100
|
66
|
|
|
76
|
if (length($name) <= 16 || $type eq COMMON) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
302
|
9
|
|
|
|
|
77
|
push @body, pack('A16A12A6A6A8A10A2', $name, |
303
|
|
|
|
|
|
|
@$meta{qw/date uid gid/}, $mode, $size, ARFMAG); |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
elsif ($type eq GNU) { |
306
|
3
|
|
|
|
|
19
|
push @body, pack('A1A15A12A6A6A8A10A2', '/', $gnuindex{$fn}, |
307
|
|
|
|
|
|
|
@$meta{qw/date uid gid/}, $mode, $size, ARFMAG); |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
elsif ($type eq BSD) { |
310
|
1
|
|
|
|
|
2
|
$size += length($name); |
311
|
1
|
|
|
|
|
15
|
push @body, pack('A3A13A12A6A6A8A10A2', AR_EFMT1, length($name), |
312
|
|
|
|
|
|
|
@$meta{qw/date uid gid/}, $mode, $size, ARFMAG), |
313
|
|
|
|
|
|
|
$name; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
else { |
316
|
0
|
|
|
|
|
0
|
return $self->_error("$type: unexpected ar type"); |
317
|
|
|
|
|
|
|
} |
318
|
13
|
|
|
|
|
25
|
push @body, $meta->{data}; |
319
|
13
|
100
|
|
|
|
54
|
push @body, "\n" if $size % 2; # padding |
320
|
|
|
|
|
|
|
} |
321
|
7
|
100
|
|
|
|
25
|
if ($filename) { |
322
|
1
|
|
|
|
|
5
|
my $fh = $self->_get_handle($filename, '>'); |
323
|
1
|
|
|
|
|
23
|
print $fh @body; |
324
|
1
|
|
|
|
|
56
|
close $fh; |
325
|
1
|
|
|
|
|
2
|
my $len = 0; |
326
|
1
|
|
|
|
|
7
|
$len += length($_) for @body; |
327
|
1
|
|
|
|
|
7
|
return $len; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
else { |
330
|
6
|
|
|
|
|
45
|
return join '', @body; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub get_content { |
335
|
20
|
|
|
20
|
1
|
18591
|
my $self = shift; |
336
|
20
|
|
|
|
|
30
|
my ($filename) = @_; |
337
|
|
|
|
|
|
|
|
338
|
20
|
50
|
|
|
|
56
|
unless ($filename) { |
339
|
0
|
|
|
|
|
0
|
$self->_error("get_content can't continue without a filename"); |
340
|
0
|
|
|
|
|
0
|
return; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
20
|
100
|
|
|
|
77
|
unless (exists($self->{files}->{$filename})) { |
344
|
2
|
|
|
|
|
14
|
$self->_error( |
345
|
|
|
|
|
|
|
"get_content failed because there is not a file named $filename"); |
346
|
2
|
|
|
|
|
8
|
return; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
18
|
|
|
|
|
87
|
return $self->{files}->{$filename}; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub get_data { |
353
|
3
|
|
|
3
|
1
|
5
|
my $self = shift; |
354
|
3
|
|
|
|
|
5
|
my $filename = shift; |
355
|
|
|
|
|
|
|
|
356
|
3
|
50
|
|
|
|
11
|
return $self->_error("$filename: no such member") |
357
|
|
|
|
|
|
|
unless exists $self->{files}->{$filename}; |
358
|
3
|
|
|
|
|
15
|
return $self->{files}->{$filename}->{data}; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub get_handle { |
362
|
3
|
|
|
3
|
1
|
1889
|
my $self = shift; |
363
|
3
|
|
|
|
|
9
|
my $filename = shift; |
364
|
3
|
|
|
|
|
3
|
my $fh; |
365
|
|
|
|
|
|
|
|
366
|
3
|
50
|
|
|
|
20
|
return $self->_error("$filename: no such member") |
367
|
|
|
|
|
|
|
unless exists $self->{files}->{$filename}; |
368
|
3
|
50
|
|
|
|
8
|
if ($has_io_string) { |
369
|
0
|
|
|
|
|
0
|
$fh = IO::String->new($self->{files}->{$filename}->{data}); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
else { |
372
|
3
|
|
|
|
|
8
|
my $data = $self->{files}->{$filename}->{data}; |
373
|
3
|
50
|
|
1
|
|
61
|
open $fh, '<', \$data or return $self->_error("in-memory file: $!"); |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
8
|
|
374
|
|
|
|
|
|
|
} |
375
|
3
|
|
|
|
|
1645
|
return $fh; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub error { |
379
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
380
|
|
|
|
|
|
|
|
381
|
0
|
0
|
|
|
|
0
|
return shift() ? $self->{longmess} : $self->{error}; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# |
385
|
|
|
|
|
|
|
# deprecated |
386
|
|
|
|
|
|
|
# |
387
|
|
|
|
|
|
|
sub DEBUG { |
388
|
1
|
|
|
1
|
0
|
2
|
my $self = shift; |
389
|
1
|
|
|
|
|
2
|
my $debug = shift; |
390
|
|
|
|
|
|
|
|
391
|
1
|
50
|
33
|
|
|
9
|
$self->{opts}->{warn} = 1 unless (defined($debug) and int($debug) == 0); |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
sub _parse { |
395
|
15
|
|
|
15
|
|
26
|
my $self = shift; |
396
|
15
|
|
|
|
|
28
|
my $data = shift; |
397
|
|
|
|
|
|
|
|
398
|
15
|
50
|
|
|
|
114
|
unless (substr($data, 0, SARMAG, '') eq ARMAG) { |
399
|
0
|
|
|
|
|
0
|
return $self->_error("Bad magic number - not an ar archive"); |
400
|
|
|
|
|
|
|
} |
401
|
15
|
|
|
|
|
26
|
my $type; |
402
|
|
|
|
|
|
|
my $names; |
403
|
15
|
|
|
|
|
99
|
while ($data =~ /\S/) { |
404
|
41
|
|
|
|
|
289
|
my ($name, $date, $uid, $gid, $mode, $size, $magic) = |
405
|
|
|
|
|
|
|
unpack('A16A12A6A6A8A10a2', substr($data, 0, 60, '')); |
406
|
41
|
50
|
|
|
|
124
|
unless ($magic eq "`\n") { |
407
|
0
|
|
|
|
|
0
|
return $self->_error("Bad file header"); |
408
|
|
|
|
|
|
|
} |
409
|
41
|
100
|
|
|
|
167
|
if ($name =~ m|^/|) { |
|
|
100
|
|
|
|
|
|
410
|
8
|
|
|
|
|
11
|
$type = GNU; |
411
|
8
|
100
|
|
|
|
21
|
if ($name eq '//') { |
|
|
100
|
|
|
|
|
|
412
|
3
|
|
|
|
|
8
|
$names = substr($data, 0, $size, ''); |
413
|
3
|
|
|
|
|
9
|
substr($data, 0, $size % 2, ''); |
414
|
3
|
|
|
|
|
39
|
next; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
elsif ($name eq '/') { |
417
|
2
|
|
|
|
|
5
|
$name = $self->{opts}->{symbols}; |
418
|
2
|
100
|
66
|
|
|
9
|
unless (defined $name && $name) { |
419
|
1
|
|
|
|
|
2
|
substr($data, 0, $size + $size % 2, ''); |
420
|
1
|
|
|
|
|
4
|
next; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
else { |
424
|
3
|
|
|
|
|
8
|
$name = substr($names, int(substr($name, 1))); |
425
|
3
|
|
|
|
|
18
|
$name =~ s/\n.*//; |
426
|
3
|
|
|
|
|
7
|
chop $name; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
elsif ($name =~ m|^#1/|) { |
430
|
1
|
|
|
|
|
3
|
$type = BSD; |
431
|
1
|
|
|
|
|
4
|
$name = substr($data, 0, int(substr($name, 3)), ''); |
432
|
1
|
|
|
|
|
3
|
$size -= length($name); |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
else { |
435
|
32
|
100
|
|
|
|
120
|
if ($name =~ m|/$|) { |
436
|
3
|
|
50
|
|
|
9
|
$type ||= GNU; # only gnu has trailing slashes |
437
|
3
|
|
|
|
|
9
|
chop $name; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
} |
440
|
37
|
|
|
|
|
66
|
$uid = int($uid); |
441
|
37
|
|
|
|
|
46
|
$gid = int($gid); |
442
|
37
|
|
|
|
|
59
|
$mode = oct($mode); |
443
|
37
|
|
|
|
|
85
|
my $content = substr($data, 0, $size, ''); |
444
|
37
|
|
|
|
|
128
|
substr($data, 0, $size % 2, ''); |
445
|
|
|
|
|
|
|
|
446
|
37
|
|
|
|
|
111
|
$self->_add_data($name, $content, $date, $uid, $gid, $mode, $size); |
447
|
|
|
|
|
|
|
} |
448
|
15
|
|
100
|
|
|
111
|
$self->{type} = $type || COMMON; |
449
|
15
|
|
|
|
|
28
|
return scalar @{$self->{names}}; |
|
15
|
|
|
|
|
66
|
|
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub _add_data { |
453
|
51
|
|
|
51
|
|
534
|
my $self = shift; |
454
|
51
|
|
|
|
|
65
|
my $filename = shift; |
455
|
51
|
|
100
|
|
|
135
|
my $content = shift || ''; |
456
|
51
|
|
|
|
|
70
|
my $date = shift; |
457
|
51
|
|
|
|
|
60
|
my $uid = shift; |
458
|
51
|
|
|
|
|
71
|
my $gid = shift; |
459
|
51
|
|
|
|
|
57
|
my $mode = shift; |
460
|
51
|
|
|
|
|
68
|
my $size = shift; |
461
|
|
|
|
|
|
|
|
462
|
51
|
50
|
|
|
|
143
|
if (exists($self->{files}->{$filename})) { |
463
|
0
|
|
|
|
|
0
|
return $self->_error("$filename: entry already exists"); |
464
|
|
|
|
|
|
|
} |
465
|
51
|
50
|
|
|
|
583
|
$self->{files}->{$filename} = { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
466
|
|
|
|
|
|
|
name => $filename, |
467
|
|
|
|
|
|
|
date => defined $date ? $date : timelocal(localtime()), |
468
|
|
|
|
|
|
|
uid => defined $uid ? $uid : 0, |
469
|
|
|
|
|
|
|
gid => defined $gid ? $gid : 0, |
470
|
|
|
|
|
|
|
mode => defined $mode ? $mode : 0100644, |
471
|
|
|
|
|
|
|
size => defined $size ? $size : length($content), |
472
|
|
|
|
|
|
|
data => $content, |
473
|
|
|
|
|
|
|
}; |
474
|
51
|
|
|
|
|
81
|
push @{$self->{names}}, $filename; |
|
51
|
|
|
|
|
116
|
|
475
|
51
|
|
|
|
|
220
|
return 1; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub _get_handle { |
479
|
8
|
|
|
8
|
|
13
|
my $self = shift; |
480
|
8
|
|
|
|
|
16
|
my $file = shift; |
481
|
8
|
|
100
|
|
|
44
|
my $mode = shift || '<'; |
482
|
|
|
|
|
|
|
|
483
|
8
|
100
|
|
|
|
25
|
if (ref $file) { |
484
|
2
|
50
|
33
|
|
|
5
|
return $file if eval{*$file{IO}} or $file->isa('IO::Handle'); |
|
2
|
|
|
|
|
40
|
|
485
|
0
|
|
|
|
|
0
|
return $self->_error("Not a filehandle"); |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
else { |
488
|
6
|
50
|
|
|
|
280
|
open my $fh, $mode, $file or return $self->_error("$file: $!"); |
489
|
6
|
|
|
|
|
18
|
binmode $fh; |
490
|
6
|
|
|
|
|
20
|
return $fh; |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
sub _error { |
495
|
2
|
|
|
2
|
|
3
|
my $self = shift; |
496
|
2
|
|
|
|
|
3
|
my $msg = shift; |
497
|
|
|
|
|
|
|
|
498
|
2
|
|
|
|
|
4
|
$self->{error} = $msg; |
499
|
2
|
|
|
|
|
296
|
$self->{longerror} = longmess($msg); |
500
|
2
|
50
|
|
|
|
772
|
if ($self->{opts}->{warn} > 1) { |
|
|
50
|
|
|
|
|
|
501
|
0
|
|
|
|
|
0
|
carp $self->{longerror}; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
elsif ($self->{opts}->{warn}) { |
504
|
0
|
|
|
|
|
0
|
carp $self->{error}; |
505
|
|
|
|
|
|
|
} |
506
|
2
|
|
|
|
|
5
|
return; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
1; |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
__END__ |