| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
|
2
|
14
|
|
|
14
|
|
72508
|
use strict; |
|
|
14
|
|
|
|
|
26
|
|
|
|
14
|
|
|
|
|
600
|
|
|
3
|
14
|
|
|
14
|
|
61
|
use warnings; |
|
|
14
|
|
|
|
|
24
|
|
|
|
14
|
|
|
|
|
801
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
14
|
|
|
14
|
|
8643
|
use Data::Dumper; |
|
|
14
|
|
|
|
|
149634
|
|
|
|
14
|
|
|
|
|
1461
|
|
|
6
|
14
|
|
|
14
|
|
8037
|
use Encode; |
|
|
14
|
|
|
|
|
257625
|
|
|
|
14
|
|
|
|
|
1412
|
|
|
7
|
14
|
|
|
14
|
|
7163
|
use FindBin; |
|
|
14
|
|
|
|
|
19580
|
|
|
|
14
|
|
|
|
|
856
|
|
|
8
|
14
|
|
|
14
|
|
100
|
use File::Spec; |
|
|
14
|
|
|
|
|
21
|
|
|
|
14
|
|
|
|
|
280
|
|
|
9
|
14
|
|
|
14
|
|
82
|
use File::Basename; |
|
|
14
|
|
|
|
|
23
|
|
|
|
14
|
|
|
|
|
902
|
|
|
10
|
14
|
|
|
14
|
|
110
|
use File::Path; |
|
|
14
|
|
|
|
|
31
|
|
|
|
14
|
|
|
|
|
1111
|
|
|
11
|
14
|
|
|
14
|
|
10562
|
use Getopt::Long qw( GetOptionsFromArray ); |
|
|
14
|
|
|
|
|
294338
|
|
|
|
14
|
|
|
|
|
100
|
|
|
12
|
14
|
|
|
14
|
|
9632
|
use lib File::Spec->catfile($FindBin::Bin, '..', 'lib'); |
|
|
14
|
|
|
|
|
11314
|
|
|
|
14
|
|
|
|
|
387
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
14
|
|
|
14
|
|
9336
|
use Archive::Lha::Decode; |
|
|
14
|
|
|
|
|
45
|
|
|
|
14
|
|
|
|
|
573
|
|
|
15
|
14
|
|
|
14
|
|
6801
|
use Archive::Lha::Header; |
|
|
14
|
|
|
|
|
50
|
|
|
|
14
|
|
|
|
|
145
|
|
|
16
|
14
|
|
|
14
|
|
98
|
use Archive::Lha::Header::Utils (); |
|
|
14
|
|
|
|
|
29
|
|
|
|
14
|
|
|
|
|
294
|
|
|
17
|
14
|
|
|
14
|
|
28472
|
use Archive::Lha::Stream::File; |
|
|
14
|
|
|
|
|
39
|
|
|
|
14
|
|
|
|
|
501
|
|
|
18
|
14
|
|
|
14
|
|
115
|
use Carp; |
|
|
14
|
|
|
|
|
29
|
|
|
|
14
|
|
|
|
|
1186
|
|
|
19
|
14
|
|
|
14
|
|
95
|
use POSIX qw( strftime setlocale LC_TIME ); |
|
|
14
|
|
|
|
|
29
|
|
|
|
14
|
|
|
|
|
114
|
|
|
20
|
14
|
|
|
14
|
|
35232
|
use Time::Moment; |
|
|
14
|
|
|
|
|
24078
|
|
|
|
14
|
|
|
|
|
140291
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Charset options: -fc (from charset) and -tc (to charset) |
|
23
|
14
|
|
|
|
|
2802900
|
my $opt_from_charset; |
|
24
|
|
|
|
|
|
|
my $opt_to_charset; |
|
25
|
14
|
|
|
|
|
0
|
my $opt_use_locale; |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Parse --use-locale before anything else so setlocale runs at startup |
|
28
|
14
|
|
|
|
|
166
|
Getopt::Long::GetOptionsFromArray(\@ARGV, |
|
29
|
|
|
|
|
|
|
'use-locale' => \$opt_use_locale, |
|
30
|
|
|
|
|
|
|
'from-charset|fc=s' => \$opt_from_charset, |
|
31
|
|
|
|
|
|
|
'to-charset|tc=s' => \$opt_to_charset, |
|
32
|
|
|
|
|
|
|
); |
|
33
|
14
|
50
|
|
|
|
10910
|
setlocale(LC_TIME, 'C') unless $opt_use_locale; |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Return display name for a header, respecting -fc/-tc options. |
|
36
|
|
|
|
|
|
|
# Without options, pathname() auto-detects from the OS field. |
|
37
|
|
|
|
|
|
|
sub _display_name { |
|
38
|
224
|
|
|
224
|
|
330
|
my ($header) = @_; |
|
39
|
224
|
|
100
|
|
|
936
|
return $header->pathname( $opt_from_charset, $opt_to_charset // 'UTF-8' ); |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
my $controller = +{ |
|
43
|
|
|
|
|
|
|
d => sub { |
|
44
|
0
|
0
|
|
0
|
|
0
|
my $fname = shift or usage(); |
|
45
|
0
|
|
|
|
|
0
|
my $stream = open_archive($fname); |
|
46
|
0
|
|
|
|
|
0
|
while ( defined( my $level = $stream->search_header ) ) { |
|
47
|
0
|
|
|
|
|
0
|
my $header = Archive::Lha::Header->new( |
|
48
|
|
|
|
|
|
|
level => $level, |
|
49
|
|
|
|
|
|
|
stream => $stream |
|
50
|
|
|
|
|
|
|
); |
|
51
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
52
|
0
|
|
|
|
|
0
|
print Dumper($header); |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
}, |
|
56
|
|
|
|
|
|
|
l => sub { |
|
57
|
1
|
50
|
|
1
|
|
3
|
my $fname = shift or usage(); |
|
58
|
1
|
|
|
|
|
3
|
my $stream = open_archive($fname); |
|
59
|
1
|
|
|
|
|
6
|
while ( defined( my $level = $stream->search_header ) ) { |
|
60
|
3
|
|
|
|
|
13
|
my $header = Archive::Lha::Header->new( |
|
61
|
|
|
|
|
|
|
level => $level, |
|
62
|
|
|
|
|
|
|
stream => $stream |
|
63
|
|
|
|
|
|
|
); |
|
64
|
3
|
|
|
|
|
12
|
$stream->seek( $header->{next_header} ); |
|
65
|
3
|
|
|
|
|
6
|
my $fullname = _display_name($header); |
|
66
|
3
|
50
|
|
|
|
5
|
$fullname = '' if $fullname eq '.'; |
|
67
|
3
|
|
33
|
|
|
17
|
my $has_path = ($fullname =~ m{/} && !_is_directory($header)); |
|
68
|
|
|
|
|
|
|
# l shows filename only (no path), + prefix if file has a path component |
|
69
|
3
|
50
|
|
|
|
9
|
my $name = $has_path ? (split m{/}, $fullname)[-1] : $fullname; |
|
70
|
3
|
0
|
33
|
|
|
5
|
$name .= '/' if _is_directory($header) && $name ne '' && $name !~ m{/$}; |
|
|
|
|
33
|
|
|
|
|
|
71
|
3
|
50
|
|
|
|
9
|
my $prefix = $has_path ? '+' : ' '; |
|
72
|
3
|
|
|
|
|
28
|
printf "%s%s\n", $prefix, $name; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
}, |
|
75
|
|
|
|
|
|
|
v => sub { |
|
76
|
5
|
|
|
5
|
|
10
|
my $contents = ''; |
|
77
|
5
|
50
|
|
|
|
31
|
my $fname = shift or usage(); |
|
78
|
5
|
|
|
|
|
19
|
my $stream = open_archive($fname); |
|
79
|
5
|
|
|
|
|
25
|
my $totals = { original_size => 0, encoded_size => 0, count => 0 }; |
|
80
|
5
|
|
|
|
|
20
|
print "Original Packed Ratio Date Time Name\n"; |
|
81
|
5
|
|
|
|
|
12
|
print "-------- ------- ----- --------- -------- -------------\n"; |
|
82
|
5
|
|
|
|
|
42
|
while ( defined( my $level = $stream->search_header ) ) { |
|
83
|
204
|
|
|
|
|
647
|
my $header = Archive::Lha::Header->new( |
|
84
|
|
|
|
|
|
|
level => $level, |
|
85
|
|
|
|
|
|
|
stream => $stream |
|
86
|
|
|
|
|
|
|
); |
|
87
|
204
|
|
|
|
|
532
|
$stream->seek( $header->{next_header} ); |
|
88
|
|
|
|
|
|
|
|
|
89
|
204
|
|
|
|
|
328
|
$totals->{original_size} += $header->{original_size}; |
|
90
|
204
|
|
|
|
|
254
|
$totals->{encoded_size} += $header->{encoded_size}; |
|
91
|
204
|
|
|
|
|
223
|
$totals->{count} += 1; |
|
92
|
|
|
|
|
|
|
printf "%8d %7d%5.1f%% %s %s %s\n", |
|
93
|
|
|
|
|
|
|
$header->{original_size}, |
|
94
|
|
|
|
|
|
|
$header->{encoded_size}, |
|
95
|
204
|
100
|
66
|
|
|
987
|
(($header->{encoded_size} && $header->{original_size}) ? 100 * ($header->{original_size} - $header->{encoded_size}) / $header->{original_size} : 0), |
|
96
|
|
|
|
|
|
|
_header_date($header), |
|
97
|
|
|
|
|
|
|
_header_time($header), |
|
98
|
|
|
|
|
|
|
_display_name($header); |
|
99
|
204
|
50
|
|
|
|
1357
|
printf ": %s\n", $header->{comment} if $header->{comment}; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
5
|
|
|
|
|
13
|
print "-------- ------- ----- --------- --------\n"; |
|
102
|
|
|
|
|
|
|
printf "%8d %7d%5.1f%% %s %s %s\n", |
|
103
|
|
|
|
|
|
|
$totals->{original_size}, |
|
104
|
|
|
|
|
|
|
$totals->{encoded_size}, |
|
105
|
|
|
|
|
|
|
(($totals->{encoded_size} && $totals->{original_size}) ? 100 * ($totals->{original_size} - $totals->{encoded_size}) / $totals->{original_size} : 0), |
|
106
|
|
|
|
|
|
|
strftime("%d-%b-%y", localtime((stat($fname))[9])), |
|
107
|
|
|
|
|
|
|
strftime("%T", localtime((stat($fname))[9])), |
|
108
|
5
|
100
|
66
|
|
|
708
|
(sprintf(" %d files", $totals->{count})); |
|
109
|
|
|
|
|
|
|
}, |
|
110
|
|
|
|
|
|
|
vv => sub { |
|
111
|
1
|
50
|
|
1
|
|
5
|
my $fname = shift or usage(); |
|
112
|
1
|
|
|
|
|
5
|
my $stream = open_archive($fname); |
|
113
|
1
|
|
|
|
|
6
|
my $totals = { original_size => 0, encoded_size => 0, count => 0 }; |
|
114
|
1
|
|
|
|
|
5
|
print "Original Packed Ratio Date Time Atts Method CRC L OS\n"; |
|
115
|
1
|
|
|
|
|
3
|
print "-------- ------- ----- --------- -------- -------- ------ ---- -----\n"; |
|
116
|
1
|
|
|
|
|
17
|
while ( defined( my $level = $stream->search_header ) ) { |
|
117
|
3
|
|
|
|
|
42
|
my $header = Archive::Lha::Header->new( |
|
118
|
|
|
|
|
|
|
level => $level, |
|
119
|
|
|
|
|
|
|
stream => $stream |
|
120
|
|
|
|
|
|
|
); |
|
121
|
3
|
|
|
|
|
24
|
$stream->seek( $header->{next_header} ); |
|
122
|
|
|
|
|
|
|
|
|
123
|
3
|
|
|
|
|
9
|
$totals->{original_size} += $header->{original_size}; |
|
124
|
3
|
|
|
|
|
7
|
$totals->{encoded_size} += $header->{encoded_size}; |
|
125
|
3
|
|
|
|
|
7
|
$totals->{count} += 1; |
|
126
|
|
|
|
|
|
|
|
|
127
|
3
|
|
|
|
|
8
|
my $name = _display_name($header); |
|
128
|
3
|
|
50
|
|
|
10
|
my $os_char = uc($header->{os}[0] // '?'); |
|
129
|
3
|
50
|
|
|
|
24
|
my $hdr_level = ref($header) =~ /Level(\d)/ ? $1 : '?'; |
|
130
|
|
|
|
|
|
|
|
|
131
|
3
|
|
|
|
|
14
|
printf "%s\n", $name; |
|
132
|
|
|
|
|
|
|
printf "%8d %7d%5.1f%% %s %s ----rwed -%s- %04X %s %s\n", |
|
133
|
|
|
|
|
|
|
$header->{original_size}, |
|
134
|
|
|
|
|
|
|
$header->{encoded_size}, |
|
135
|
|
|
|
|
|
|
(($header->{encoded_size} && $header->{original_size}) ? 100 * ($header->{original_size} - $header->{encoded_size}) / $header->{original_size} : 0), |
|
136
|
|
|
|
|
|
|
_header_date($header), |
|
137
|
|
|
|
|
|
|
_header_time($header), |
|
138
|
|
|
|
|
|
|
$header->{method}, |
|
139
|
|
|
|
|
|
|
$header->{crc16}, |
|
140
|
3
|
50
|
33
|
|
|
76
|
$hdr_level, |
|
141
|
|
|
|
|
|
|
$os_char; |
|
142
|
3
|
50
|
|
|
|
46
|
printf ": %s\n", $header->{comment} if $header->{comment}; |
|
143
|
|
|
|
|
|
|
} |
|
144
|
1
|
|
|
|
|
3
|
print "-------- ------- ----- --------- --------\n"; |
|
145
|
|
|
|
|
|
|
printf "%8d %7d%5.1f%% %s %s %s\n", |
|
146
|
|
|
|
|
|
|
$totals->{original_size}, |
|
147
|
|
|
|
|
|
|
$totals->{encoded_size}, |
|
148
|
|
|
|
|
|
|
(($totals->{encoded_size} && $totals->{original_size}) ? 100 * ($totals->{original_size} - $totals->{encoded_size}) / $totals->{original_size} : 0), |
|
149
|
|
|
|
|
|
|
strftime("%d-%b-%y", localtime((stat($fname))[9])), |
|
150
|
|
|
|
|
|
|
strftime("%T", localtime((stat($fname))[9])), |
|
151
|
1
|
50
|
33
|
|
|
135
|
(sprintf(" %d files", $totals->{count})); |
|
152
|
|
|
|
|
|
|
}, |
|
153
|
|
|
|
|
|
|
t => sub { |
|
154
|
0
|
0
|
|
0
|
|
0
|
my $fname = shift or usage(); |
|
155
|
0
|
|
|
|
|
0
|
printf "Testing integrity of archive '%s':\n", $fname; |
|
156
|
0
|
|
|
|
|
0
|
my $stream = open_archive($fname); |
|
157
|
0
|
|
|
|
|
0
|
my $totals = { original_size => 0, encoded_size => 0, count => 0 }; |
|
158
|
0
|
|
|
|
|
0
|
while ( defined( my $level = $stream->search_header ) ) { |
|
159
|
0
|
|
|
|
|
0
|
my $header = Archive::Lha::Header->new( |
|
160
|
|
|
|
|
|
|
level => $level, |
|
161
|
|
|
|
|
|
|
stream => $stream |
|
162
|
|
|
|
|
|
|
); |
|
163
|
0
|
|
|
|
|
0
|
$stream->seek( $header->data_top ); |
|
164
|
0
|
|
|
|
|
0
|
_decode_entry($header, $stream); |
|
165
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
166
|
|
|
|
|
|
|
|
|
167
|
0
|
|
|
|
|
0
|
$totals->{original_size} += $header->{original_size}; |
|
168
|
0
|
|
|
|
|
0
|
$totals->{encoded_size} += $header->{encoded_size}; |
|
169
|
0
|
|
|
|
|
0
|
$totals->{count} += 1; |
|
170
|
0
|
|
|
|
|
0
|
printf " Testing: (%8d/%8d) %s\n", $header->{original_size}, $header->{original_size}, _display_name($header); |
|
171
|
|
|
|
|
|
|
} |
|
172
|
0
|
|
|
|
|
0
|
my $error = undef; |
|
173
|
0
|
0
|
|
|
|
0
|
if ($totals->{count}) { |
|
174
|
0
|
0
|
|
|
|
0
|
if (!$error) { |
|
175
|
0
|
|
|
|
|
0
|
printf "%d files tested, all files OK\n", $totals->{count}; |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
} else { |
|
178
|
0
|
|
|
|
|
0
|
$error = 1; |
|
179
|
0
|
|
|
|
|
0
|
printf "No files tested.\n"; |
|
180
|
|
|
|
|
|
|
} |
|
181
|
0
|
0
|
|
|
|
0
|
if ($error) { |
|
182
|
0
|
|
|
|
|
0
|
printf "\nOperation not entirely successful\n\n"; |
|
183
|
|
|
|
|
|
|
} else { |
|
184
|
0
|
|
|
|
|
0
|
printf "\nOperation succesful\n\n"; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
}, |
|
187
|
|
|
|
|
|
|
x => sub { |
|
188
|
0
|
0
|
|
0
|
|
0
|
my $fname = shift or usage(); |
|
189
|
0
|
|
|
|
|
0
|
my %target; |
|
190
|
0
|
0
|
|
|
|
0
|
if (@_) { |
|
191
|
0
|
|
|
|
|
0
|
%target = map { $_ => 1 } @_; |
|
|
0
|
|
|
|
|
0
|
|
|
192
|
|
|
|
|
|
|
} |
|
193
|
0
|
|
|
|
|
0
|
my $stream = open_archive($fname); |
|
194
|
0
|
|
|
|
|
0
|
while ( defined( my $level = $stream->search_header ) ) { |
|
195
|
0
|
|
|
|
|
0
|
my $header = Archive::Lha::Header->new( |
|
196
|
|
|
|
|
|
|
level => $level, |
|
197
|
|
|
|
|
|
|
stream => $stream |
|
198
|
|
|
|
|
|
|
); |
|
199
|
0
|
0
|
0
|
|
|
0
|
if ( %target and !$target{$header->pathname} ) { |
|
200
|
0
|
|
|
|
|
0
|
$stream->seek( $header->next_header ); |
|
201
|
0
|
|
|
|
|
0
|
next; |
|
202
|
|
|
|
|
|
|
} |
|
203
|
0
|
|
|
|
|
0
|
$stream->seek( $header->data_top ); |
|
204
|
|
|
|
|
|
|
|
|
205
|
0
|
0
|
|
|
|
0
|
if (_is_directory($header)) { |
|
206
|
0
|
0
|
|
|
|
0
|
mkpath $header->pathname unless -d $header->pathname; |
|
207
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
208
|
0
|
|
|
|
|
0
|
next; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
0
|
my ($decoded, $crc) = _decode_entry($header, $stream); |
|
212
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
213
|
0
|
0
|
|
|
|
0
|
die "crc mismatch" if $crc != $header->crc16; |
|
214
|
|
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
0
|
write_all($header->pathname, $decoded); |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
}, |
|
218
|
14
|
|
|
|
|
375
|
}; |
|
219
|
|
|
|
|
|
|
|
|
220
|
14
|
100
|
|
|
|
605
|
my $PROGNAME = $ENV{PLHASA} ? 'plhasa' : basename($0); |
|
221
|
|
|
|
|
|
|
|
|
222
|
14
|
|
|
|
|
57
|
&main;exit; |
|
|
13
|
|
|
|
|
0
|
|
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub main { |
|
225
|
14
|
100
|
|
14
|
|
50
|
if ($PROGNAME eq 'plhasa') { |
|
226
|
6
|
|
|
|
|
21
|
_main_lhasa(); |
|
227
|
|
|
|
|
|
|
} else { |
|
228
|
8
|
|
|
|
|
29
|
_main_plha(); |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub _main_plha { |
|
233
|
8
|
|
|
8
|
|
45
|
GetOptionsFromArray(\@ARGV, |
|
234
|
|
|
|
|
|
|
'from-charset|fc=s' => \$opt_from_charset, |
|
235
|
|
|
|
|
|
|
'to-charset|tc=s' => \$opt_to_charset, |
|
236
|
|
|
|
|
|
|
'use-locale' => \$opt_use_locale, |
|
237
|
|
|
|
|
|
|
); |
|
238
|
8
|
50
|
|
|
|
2205
|
my $cmd = shift @ARGV or usage(); |
|
239
|
8
|
50
|
|
|
|
32
|
my $file = shift @ARGV or usage(); |
|
240
|
8
|
|
|
|
|
44
|
check_magic($file); |
|
241
|
8
|
100
|
|
|
|
33
|
if ( !exists $controller->{$cmd} ) { |
|
242
|
1
|
|
|
|
|
5
|
usage("Unknown command: $cmd"); |
|
243
|
|
|
|
|
|
|
} |
|
244
|
7
|
|
|
|
|
29
|
$controller->{$cmd}->($file, @ARGV); |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# lhasa-compatible argument parsing: |
|
248
|
|
|
|
|
|
|
# [-]{lvtxep[q{num}][finv]}[w=] archive_file [file...] |
|
249
|
|
|
|
|
|
|
sub _main_lhasa { |
|
250
|
6
|
50
|
|
6
|
|
24
|
my $arg = shift @ARGV or usage_lhasa(); |
|
251
|
6
|
|
|
|
|
19
|
$arg =~ s/^-//; # strip optional leading dash |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# extract command letter (first char) |
|
254
|
6
|
50
|
|
|
|
38
|
my ($cmd_char) = $arg =~ /^([lvtxep])/i or usage_lhasa(); |
|
255
|
6
|
|
|
|
|
18
|
my $flags = substr($arg, 1); # everything after command char |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# parse options from flags string |
|
258
|
6
|
|
|
|
|
46
|
my %opts = (quiet => 0, verbose => 0, force => 0, ignore_path => 0, dry_run => 0, extract_dir => undef); |
|
259
|
6
|
|
|
|
|
23
|
while (length $flags) { |
|
260
|
0
|
0
|
|
|
|
0
|
if ($flags =~ s/^q(\d*)//) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
261
|
0
|
0
|
|
|
|
0
|
$opts{quiet} = length($1) ? int($1) : 1; |
|
262
|
|
|
|
|
|
|
} elsif ($flags =~ s/^w=([^\s]+)//) { |
|
263
|
0
|
|
|
|
|
0
|
$opts{extract_dir} = $1; |
|
264
|
|
|
|
|
|
|
} elsif ($flags =~ s/^f//) { |
|
265
|
0
|
|
|
|
|
0
|
$opts{force} = 1; |
|
266
|
|
|
|
|
|
|
} elsif ($flags =~ s/^i//) { |
|
267
|
0
|
|
|
|
|
0
|
$opts{ignore_path} = 1; |
|
268
|
|
|
|
|
|
|
} elsif ($flags =~ s/^n//) { |
|
269
|
0
|
|
|
|
|
0
|
$opts{dry_run} = 1; |
|
270
|
|
|
|
|
|
|
} elsif ($flags =~ s/^v//) { |
|
271
|
0
|
|
|
|
|
0
|
$opts{verbose} = 1; |
|
272
|
|
|
|
|
|
|
} else { |
|
273
|
0
|
|
|
|
|
0
|
$flags = substr($flags, 1); # skip unknown flag |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# also allow w= as a separate argument |
|
278
|
6
|
50
|
33
|
|
|
50
|
if (@ARGV && $ARGV[0] =~ /^w=(.+)/) { |
|
279
|
0
|
|
|
|
|
0
|
$opts{extract_dir} = $1; |
|
280
|
0
|
|
|
|
|
0
|
shift @ARGV; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
|
|
283
|
6
|
50
|
|
|
|
22
|
my $file = shift @ARGV or usage_lhasa(); |
|
284
|
6
|
|
|
|
|
30
|
check_magic($file); |
|
285
|
|
|
|
|
|
|
|
|
286
|
6
|
|
|
|
|
19
|
my $cmd = lc $cmd_char; |
|
287
|
6
|
50
|
|
|
|
30
|
$cmd = 'x' if $cmd eq 'e'; |
|
288
|
|
|
|
|
|
|
|
|
289
|
6
|
50
|
|
|
|
40
|
if ($cmd eq 'p') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
290
|
0
|
|
|
|
|
0
|
_print_to_stdout($file, \%opts, @ARGV); |
|
291
|
|
|
|
|
|
|
} elsif ($cmd eq 'x') { |
|
292
|
0
|
|
|
|
|
0
|
_extract_lhasa($file, \%opts, @ARGV); |
|
293
|
|
|
|
|
|
|
} elsif ($cmd eq 'l') { |
|
294
|
2
|
|
|
|
|
9
|
_list_lhasa($file, 'l'); |
|
295
|
|
|
|
|
|
|
} elsif ($cmd eq 'v') { |
|
296
|
4
|
|
|
|
|
19
|
_list_lhasa($file, 'lv'); |
|
297
|
|
|
|
|
|
|
} elsif ($cmd eq 't') { |
|
298
|
0
|
|
|
|
|
0
|
$controller->{t}->($file); |
|
299
|
|
|
|
|
|
|
} else { |
|
300
|
0
|
|
|
|
|
0
|
usage_lhasa(); |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
sub _print_to_stdout { |
|
305
|
0
|
|
|
0
|
|
0
|
my ($fname, $opts, @targets) = @_; |
|
306
|
0
|
|
|
|
|
0
|
my %target = map { $_ => 1 } @targets; |
|
|
0
|
|
|
|
|
0
|
|
|
307
|
0
|
|
|
|
|
0
|
my $stream = open_archive($fname); |
|
308
|
0
|
|
|
|
|
0
|
while ( defined( my $level = $stream->search_header ) ) { |
|
309
|
0
|
|
|
|
|
0
|
my $header = Archive::Lha::Header->new( level => $level, stream => $stream ); |
|
310
|
0
|
0
|
0
|
|
|
0
|
if (%target && !$target{$header->pathname}) { |
|
311
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
312
|
0
|
|
|
|
|
0
|
next; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
0
|
0
|
|
|
|
0
|
next if _is_directory($header); |
|
315
|
0
|
|
|
|
|
0
|
$stream->seek( $header->data_top ); |
|
316
|
0
|
|
|
|
|
0
|
my ($decoded) = _decode_entry($header, $stream); |
|
317
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
318
|
0
|
|
|
|
|
0
|
print $decoded; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub _extract_lhasa { |
|
323
|
0
|
|
|
0
|
|
0
|
my ($fname, $opts, @targets) = @_; |
|
324
|
0
|
|
|
|
|
0
|
my %target = map { $_ => 1 } @targets; |
|
|
0
|
|
|
|
|
0
|
|
|
325
|
0
|
|
|
|
|
0
|
my $stream = open_archive($fname); |
|
326
|
0
|
|
|
|
|
0
|
while ( defined( my $level = $stream->search_header ) ) { |
|
327
|
0
|
|
|
|
|
0
|
my $header = Archive::Lha::Header->new( level => $level, stream => $stream ); |
|
328
|
0
|
|
|
|
|
0
|
my $pathname = $header->pathname; |
|
329
|
0
|
0
|
|
|
|
0
|
$pathname =~ s{.*/}{} if $opts->{ignore_path}; |
|
330
|
0
|
0
|
0
|
|
|
0
|
if (%target && !$target{$pathname}) { |
|
331
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
332
|
0
|
|
|
|
|
0
|
next; |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
$pathname = File::Spec->catfile($opts->{extract_dir}, $pathname) |
|
335
|
0
|
0
|
|
|
|
0
|
if $opts->{extract_dir}; |
|
336
|
|
|
|
|
|
|
|
|
337
|
0
|
0
|
|
|
|
0
|
if (_is_directory($header)) { |
|
338
|
0
|
0
|
0
|
|
|
0
|
mkpath $pathname unless -d $pathname || $opts->{dry_run}; |
|
339
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
340
|
0
|
|
|
|
|
0
|
next; |
|
341
|
|
|
|
|
|
|
} |
|
342
|
|
|
|
|
|
|
|
|
343
|
0
|
|
|
|
|
0
|
$stream->seek( $header->data_top ); |
|
344
|
0
|
|
|
|
|
0
|
my ($decoded, $crc) = _decode_entry($header, $stream); |
|
345
|
0
|
|
|
|
|
0
|
$stream->seek( $header->{next_header} ); |
|
346
|
0
|
0
|
|
|
|
0
|
die "crc mismatch for " . $header->pathname if $crc != $header->crc16; |
|
347
|
|
|
|
|
|
|
|
|
348
|
0
|
0
|
|
|
|
0
|
unless ($opts->{dry_run}) { |
|
349
|
0
|
0
|
0
|
|
|
0
|
if (-e $pathname && !$opts->{force}) { |
|
350
|
0
|
|
|
|
|
0
|
print STDERR "$pathname already exists, skipping (use -f to force)\n"; |
|
351
|
0
|
|
|
|
|
0
|
next; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
0
|
|
|
|
|
0
|
write_all($pathname, $decoded); |
|
354
|
|
|
|
|
|
|
} |
|
355
|
0
|
0
|
|
|
|
0
|
printf " %s\n", $pathname if $opts->{verbose}; |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
sub usage_lhasa { |
|
360
|
0
|
|
|
0
|
|
0
|
die "plhasa -- Perl LHA tool (lhasa-compatible)\n" . |
|
361
|
|
|
|
|
|
|
"usage: plhasa [-]{lvtxep[q{num}][finv]}[w=] archive_file [file...]\n" . |
|
362
|
|
|
|
|
|
|
"commands: options:\n" . |
|
363
|
|
|
|
|
|
|
" l List (terse) f Force overwrite (no prompt)\n" . |
|
364
|
|
|
|
|
|
|
" v Verbose list i Ignore directory path\n" . |
|
365
|
|
|
|
|
|
|
" t Test file CRC in archive n Perform dry run\n" . |
|
366
|
|
|
|
|
|
|
" x,e Extract from archive q{num} Quiet mode\n" . |
|
367
|
|
|
|
|
|
|
" p Print to stdout from archive v Verbose\n" . |
|
368
|
|
|
|
|
|
|
" w= Specify extract directory\n"; |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub usage { |
|
372
|
1
|
|
|
1
|
|
1
|
my ($msg) = @_; |
|
373
|
1
|
|
|
|
|
6
|
my $text = "Usage: $0 [options] (l|v|vv|x|t|d) archive (files)\n" . |
|
374
|
|
|
|
|
|
|
" l - list contents (LhA terse format, filename only)\n" . |
|
375
|
|
|
|
|
|
|
" v - list archive verbose (LhA v format)\n" . |
|
376
|
|
|
|
|
|
|
" vv - list archive full (LhA vv format)\n" . |
|
377
|
|
|
|
|
|
|
" x - extract archive\n" . |
|
378
|
|
|
|
|
|
|
" t - test file\n" . |
|
379
|
|
|
|
|
|
|
" d - dump each header\n" . |
|
380
|
|
|
|
|
|
|
" -fc, --from-charset source encoding for filenames (default: auto-detect)\n" . |
|
381
|
|
|
|
|
|
|
" -tc, --to-charset output encoding for filenames (default: UTF-8)\n" . |
|
382
|
|
|
|
|
|
|
" --use-locale use system locale for month names (default: English)\n"; |
|
383
|
1
|
50
|
|
|
|
2
|
if ($msg) { |
|
384
|
1
|
|
|
|
|
0
|
die "$msg\n$text"; |
|
385
|
|
|
|
|
|
|
} |
|
386
|
0
|
|
|
|
|
0
|
die $text; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
sub _header_date { |
|
390
|
207
|
|
|
207
|
|
276
|
my ($h) = @_; |
|
391
|
|
|
|
|
|
|
return $h->{timestamp_is_unix} |
|
392
|
|
|
|
|
|
|
? strftime("%d-%b-%y", localtime($h->{timestamp})) |
|
393
|
207
|
100
|
|
|
|
1089
|
: strftime("%d-%b-%y", Archive::Lha::Header::Utils::dostime_fields($h->{timestamp})); |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
sub _header_time { |
|
397
|
207
|
|
|
207
|
|
328
|
my ($h) = @_; |
|
398
|
|
|
|
|
|
|
return $h->{timestamp_is_unix} |
|
399
|
|
|
|
|
|
|
? strftime("%T", localtime($h->{timestamp})) |
|
400
|
207
|
100
|
|
|
|
689
|
: strftime("%T", Archive::Lha::Header::Utils::dostime_fields($h->{timestamp})); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# ls-style date from Unix epoch (stat mtime etc) |
|
404
|
|
|
|
|
|
|
# Note: avoid %e (space-padded day) — not supported on Windows MSVC runtime. |
|
405
|
|
|
|
|
|
|
# Use %d and strip the leading zero manually instead. |
|
406
|
|
|
|
|
|
|
sub _ls_stamp { |
|
407
|
20
|
|
|
20
|
|
39
|
my ($epoch) = @_; |
|
408
|
20
|
|
|
|
|
36
|
my $six_months = 6 * 30 * 86400; |
|
409
|
20
|
|
|
|
|
467
|
my @t = localtime($epoch); |
|
410
|
20
|
|
|
|
|
94
|
my $day = sprintf "%2d", $t[3]; # space-pad day |
|
411
|
20
|
100
|
|
|
|
92
|
if (abs(time - $epoch) < $six_months) { |
|
412
|
2
|
|
|
|
|
55
|
return strftime("%b", @t) . " $day " . strftime("%H:%M", @t); |
|
413
|
|
|
|
|
|
|
} |
|
414
|
18
|
|
|
|
|
563
|
return strftime("%b", @t) . " $day " . strftime("%Y", @t); |
|
415
|
|
|
|
|
|
|
} |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
# ls-style date from a header (handles both DOS and Unix timestamps) |
|
418
|
|
|
|
|
|
|
sub _ls_stamp_header { |
|
419
|
14
|
|
|
14
|
|
44
|
my ($header) = @_; |
|
420
|
|
|
|
|
|
|
my $epoch = $header->{timestamp_is_unix} |
|
421
|
|
|
|
|
|
|
? $header->{timestamp} |
|
422
|
14
|
100
|
|
|
|
60
|
: Archive::Lha::Header::Utils::_dostime2utime($header->{timestamp}); |
|
423
|
14
|
|
|
|
|
41
|
return _ls_stamp($epoch); |
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# Lhasa-compatible listing (l = terse, lv = verbose with method+crc) |
|
427
|
|
|
|
|
|
|
sub _list_lhasa { |
|
428
|
6
|
|
|
6
|
|
18
|
my ($fname, $mode) = @_; |
|
429
|
6
|
|
|
|
|
22
|
my $stream = open_archive($fname); |
|
430
|
6
|
|
|
|
|
61
|
my $totals = { original_size => 0, encoded_size => 0, count => 0 }; |
|
431
|
|
|
|
|
|
|
|
|
432
|
6
|
100
|
|
|
|
26
|
if ($mode eq 'lv') { |
|
433
|
4
|
|
|
|
|
23
|
printf " PERMSSN UID GID PACKED SIZE RATIO METHOD CRC STAMP NAME\n"; |
|
434
|
4
|
|
|
|
|
20
|
printf "---------- ----------- ------- ------- ------ ---------- ------------ -------------\n"; |
|
435
|
|
|
|
|
|
|
} else { |
|
436
|
2
|
|
|
|
|
10
|
printf " PERMSSN UID GID SIZE RATIO STAMP NAME\n"; |
|
437
|
2
|
|
|
|
|
6
|
printf "---------- ----------- ------- ------ ------------ --------------------\n"; |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
6
|
|
|
|
|
99
|
while ( defined( my $level = $stream->search_header ) ) { |
|
441
|
14
|
|
|
|
|
114
|
my $header = Archive::Lha::Header->new( |
|
442
|
|
|
|
|
|
|
level => $level, |
|
443
|
|
|
|
|
|
|
stream => $stream |
|
444
|
|
|
|
|
|
|
); |
|
445
|
|
|
|
|
|
|
# Skip to next header - no need to decode for listing |
|
446
|
14
|
|
|
|
|
117
|
$stream->seek( $header->{next_header} ); |
|
447
|
|
|
|
|
|
|
|
|
448
|
14
|
|
|
|
|
38
|
$totals->{original_size} += $header->{original_size}; |
|
449
|
14
|
|
|
|
|
38
|
$totals->{encoded_size} += $header->{encoded_size}; |
|
450
|
14
|
|
|
|
|
29
|
$totals->{count} += 1; |
|
451
|
|
|
|
|
|
|
|
|
452
|
14
|
|
|
|
|
45
|
my $stamp = _ls_stamp_header($header); |
|
453
|
14
|
|
|
|
|
58
|
my $name = _display_name($header); |
|
454
|
14
|
50
|
|
|
|
40
|
$name = '' if $name eq '.'; # empty root directory |
|
455
|
14
|
0
|
33
|
|
|
51
|
$name .= '/' if _is_directory($header) && $name ne '' && $name !~ m{/$}; |
|
|
|
|
33
|
|
|
|
|
|
456
|
14
|
100
|
50
|
|
|
80
|
$name = _fix_msdos_allcaps($name) if ($header->{os}[0] // '') eq 'M'; |
|
457
|
14
|
|
|
|
|
41
|
my $prefix = _lhasa_prefix($header); |
|
458
|
|
|
|
|
|
|
my $ratio_str = _is_directory($header) ? '******' |
|
459
|
|
|
|
|
|
|
: sprintf("%5.1f%%", $header->{original_size} |
|
460
|
14
|
50
|
|
|
|
56
|
? unpack('f', pack('f', 100 * $header->{encoded_size} / $header->{original_size})) : 100); |
|
|
|
50
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
|
|
462
|
14
|
100
|
|
|
|
47
|
if ($mode eq 'lv') { |
|
463
|
|
|
|
|
|
|
printf "%s%7d %7d %s -%s- %04x %s %s\n", |
|
464
|
|
|
|
|
|
|
$prefix, |
|
465
|
|
|
|
|
|
|
$header->{encoded_size}, |
|
466
|
|
|
|
|
|
|
$header->{original_size}, |
|
467
|
|
|
|
|
|
|
$ratio_str, |
|
468
|
|
|
|
|
|
|
$header->{method}, |
|
469
|
|
|
|
|
|
|
$header->{crc16}, |
|
470
|
10
|
|
|
|
|
188
|
$stamp, |
|
471
|
|
|
|
|
|
|
$name; |
|
472
|
|
|
|
|
|
|
} else { |
|
473
|
|
|
|
|
|
|
printf "%s%7d %s %s %s\n", |
|
474
|
|
|
|
|
|
|
$prefix, |
|
475
|
|
|
|
|
|
|
$header->{original_size}, |
|
476
|
4
|
|
|
|
|
97
|
$ratio_str, |
|
477
|
|
|
|
|
|
|
$stamp, |
|
478
|
|
|
|
|
|
|
$name; |
|
479
|
|
|
|
|
|
|
} |
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
|
|
482
|
6
|
100
|
|
|
|
24
|
if ($mode eq 'lv') { |
|
483
|
4
|
|
|
|
|
17
|
printf "---------- ----------- ------- ------- ------ ---------- ------------ -------------\n"; |
|
484
|
|
|
|
|
|
|
} else { |
|
485
|
2
|
|
|
|
|
7
|
printf "---------- ----------- ------- ------ ------------ --------------------\n"; |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
my $ratio = $totals->{original_size} |
|
489
|
6
|
50
|
|
|
|
61
|
? unpack('f', pack('f', 100 * $totals->{encoded_size} / $totals->{original_size})) : 100; |
|
490
|
6
|
|
|
|
|
168
|
my $stamp = _ls_stamp((stat($fname))[9]); |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
# PERMSSN (10) + sep (1) + UID/GID (11) + sep (1) = 23 chars for prefix |
|
493
|
|
|
|
|
|
|
# " Total " (PERMSSN 10) + " " (sep) + "%5d files" (UID/GID 11) + " " (sep) = 23 |
|
494
|
6
|
100
|
|
|
|
39
|
my $file_str = $totals->{count} == 1 ? 'file ' : 'files'; |
|
495
|
6
|
|
|
|
|
39
|
my $prefix = " Total " . sprintf(" %5d %s ", $totals->{count}, $file_str); |
|
496
|
|
|
|
|
|
|
|
|
497
|
6
|
100
|
|
|
|
37
|
if ($mode eq 'lv') { |
|
498
|
|
|
|
|
|
|
printf "%s%7d %7d %5.1f%% %s\n", |
|
499
|
|
|
|
|
|
|
$prefix, |
|
500
|
|
|
|
|
|
|
$totals->{encoded_size}, |
|
501
|
|
|
|
|
|
|
$totals->{original_size}, |
|
502
|
4
|
|
|
|
|
180
|
$ratio, |
|
503
|
|
|
|
|
|
|
$stamp; |
|
504
|
|
|
|
|
|
|
} else { |
|
505
|
|
|
|
|
|
|
printf "%s%7d %5.1f%% %s\n", |
|
506
|
|
|
|
|
|
|
$prefix, |
|
507
|
|
|
|
|
|
|
$totals->{original_size}, |
|
508
|
2
|
|
|
|
|
78
|
$ratio, |
|
509
|
|
|
|
|
|
|
$stamp; |
|
510
|
|
|
|
|
|
|
} |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
|
|
513
|
34
|
|
|
34
|
|
376
|
sub _is_directory { $_[0]->{method} eq 'lhd' } |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# MS-DOS archives store filenames in all-caps. Lhasa detects per-file |
|
516
|
|
|
|
|
|
|
# all-caps paths and converts to lowercase. Match that behavior. |
|
517
|
|
|
|
|
|
|
sub _fix_msdos_allcaps { |
|
518
|
12
|
|
|
12
|
|
29
|
my ($name) = @_; |
|
519
|
12
|
50
|
|
|
|
64
|
return $name if $name =~ /[a-z]/; # has lowercase = not all-caps |
|
520
|
0
|
|
|
|
|
0
|
return lc $name; |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# Format permission/owner prefix like lhasa does |
|
524
|
|
|
|
|
|
|
sub _lhasa_prefix { |
|
525
|
14
|
|
|
14
|
|
30
|
my ($header) = @_; |
|
526
|
14
|
50
|
|
|
|
54
|
if (defined $header->{unix_perm}) { |
|
527
|
0
|
|
|
|
|
0
|
my $perm = $header->{unix_perm}; |
|
528
|
0
|
0
|
|
|
|
0
|
my $type = _is_directory($header) ? 'd' : '-'; |
|
529
|
0
|
|
|
|
|
0
|
my $str = $type; |
|
530
|
0
|
|
|
|
|
0
|
for my $shift (6, 3, 0) { |
|
531
|
0
|
|
|
|
|
0
|
my $bits = ($perm >> $shift) & 7; |
|
532
|
0
|
0
|
|
|
|
0
|
$str .= ($bits & 4) ? 'r' : '-'; |
|
533
|
0
|
0
|
|
|
|
0
|
$str .= ($bits & 2) ? 'w' : '-'; |
|
534
|
0
|
0
|
|
|
|
0
|
$str .= ($bits & 1) ? 'x' : '-'; |
|
535
|
|
|
|
|
|
|
} |
|
536
|
0
|
|
0
|
|
|
0
|
my $uid = $header->{unix_uid} // 0; |
|
537
|
0
|
|
0
|
|
|
0
|
my $gid = $header->{unix_gid} // 0; |
|
538
|
|
|
|
|
|
|
# PERMSSN(10) + sep(1) + UID/GID(%5d/%-5d = 11) + sep(1) = 23 |
|
539
|
0
|
|
|
|
|
0
|
return sprintf "%s %5d/%-5d ", $str, $uid, $gid; |
|
540
|
|
|
|
|
|
|
} |
|
541
|
14
|
|
50
|
|
|
93
|
return sprintf "%-23s", '[' . ($header->{os}[1] // 'generic') . ']'; |
|
542
|
|
|
|
|
|
|
} |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
sub _decode_entry { |
|
545
|
0
|
|
|
0
|
|
0
|
my ($header, $stream) = @_; |
|
546
|
0
|
0
|
|
|
|
0
|
return ('', 0) if _is_directory($header); |
|
547
|
0
|
|
|
|
|
0
|
my $decoded = ''; |
|
548
|
|
|
|
|
|
|
my $decoder = Archive::Lha::Decode->new( |
|
549
|
|
|
|
|
|
|
header => $header, |
|
550
|
0
|
|
|
0
|
|
0
|
read => sub { $stream->read(@_) }, |
|
551
|
0
|
|
|
0
|
|
0
|
write => sub { $decoded .= join '', @_ }, |
|
552
|
0
|
|
|
|
|
0
|
); |
|
553
|
0
|
|
|
|
|
0
|
my $crc = $decoder->decode; |
|
554
|
0
|
|
|
|
|
0
|
return ($decoded, $crc); |
|
555
|
|
|
|
|
|
|
} |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub open_archive { |
|
558
|
13
|
|
|
13
|
|
53
|
my $fname = shift; |
|
559
|
13
|
50
|
|
|
|
48
|
die "fname missing" unless $fname; |
|
560
|
13
|
|
|
|
|
214
|
Archive::Lha::Stream::File->new(file => $fname); |
|
561
|
|
|
|
|
|
|
} |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
sub write_all { |
|
564
|
0
|
|
|
0
|
|
0
|
my ($fname, $data) = @_; |
|
565
|
0
|
|
|
|
|
0
|
my $dir = dirname($fname); |
|
566
|
0
|
0
|
|
|
|
0
|
mkpath $dir unless -d $dir; |
|
567
|
0
|
0
|
|
|
|
0
|
open my $fh, '>:raw', $fname or die $!; |
|
568
|
0
|
|
|
|
|
0
|
binmode $fh; |
|
569
|
0
|
|
|
|
|
0
|
print $fh $data; |
|
570
|
0
|
|
|
|
|
0
|
close $fh; |
|
571
|
|
|
|
|
|
|
} |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
sub check_magic { |
|
574
|
14
|
|
|
14
|
|
138
|
my $fname = shift; |
|
575
|
14
|
50
|
|
|
|
1294
|
open my $fh, '<:raw', $fname or die "Cannot open $fname: $!"; |
|
576
|
14
|
|
|
|
|
52
|
binmode $fh; |
|
577
|
14
|
|
|
|
|
67
|
my $magic; |
|
578
|
14
|
|
|
|
|
258
|
my $chars = read($fh, $magic, 5); |
|
579
|
14
|
|
|
|
|
242
|
my ($signature) = unpack("x2a3", $magic); |
|
580
|
14
|
50
|
|
|
|
154
|
die 'Does not look like an LHa file' unless $signature eq "-lh"; |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# Check for truncation: last byte of a well-formed LHA archive is 0x00 |
|
583
|
14
|
|
|
|
|
152
|
seek $fh, -1, 2; |
|
584
|
14
|
|
|
|
|
65
|
my $last_byte; |
|
585
|
14
|
|
|
|
|
88
|
read $fh, $last_byte, 1; |
|
586
|
14
|
100
|
|
|
|
59
|
if ( ord($last_byte) != 0x00 ) { |
|
587
|
1
|
|
|
|
|
23
|
warn "WARNING: Archive may be truncated or corrupt (last byte is not 0x00)\n"; |
|
588
|
|
|
|
|
|
|
} |
|
589
|
14
|
|
|
|
|
210
|
close $fh; |
|
590
|
|
|
|
|
|
|
} |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
__END__ |