| 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__ |