Archive-Lha
view release on metacpan or search on metacpan
while ( defined( my $level = $stream->search_header ) ) {
my $header = Archive::Lha::Header->new(
level => $level,
stream => $stream
);
$stream->seek( $header->{next_header} );
print Dumper($header);
}
},
l => sub {
my $fname = shift or usage();
my $stream = open_archive($fname);
while ( defined( my $level = $stream->search_header ) ) {
my $header = Archive::Lha::Header->new(
level => $level,
stream => $stream
);
$stream->seek( $header->{next_header} );
my $fullname = _display_name($header);
$fullname = '' if $fullname eq '.';
my $has_path = ($fullname =~ m{/} && !_is_directory($header));
# l shows filename only (no path), + prefix if file has a path component
my $name = $has_path ? (split m{/}, $fullname)[-1] : $fullname;
$name .= '/' if _is_directory($header) && $name ne '' && $name !~ m{/$};
my $prefix = $has_path ? '+' : ' ';
printf "%s%s\n", $prefix, $name;
}
},
v => sub {
my $contents = '';
my $fname = shift or usage();
my $stream = open_archive($fname);
my $totals = { original_size => 0, encoded_size => 0, count => 0 };
print "Original Packed Ratio Date Time Name\n";
print "-------- ------- ----- --------- -------- -------------\n";
while ( defined( my $level = $stream->search_header ) ) {
my $header = Archive::Lha::Header->new(
level => $level,
stream => $stream
);
$stream->seek( $header->{next_header} );
$totals->{original_size} += $header->{original_size};
$totals->{encoded_size} += $header->{encoded_size};
$totals->{count} += 1;
printf "%8d %7d%5.1f%% %s %s %s\n",
$header->{original_size},
$header->{encoded_size},
(($header->{encoded_size} && $header->{original_size}) ? 100 * ($header->{original_size} - $header->{encoded_size}) / $header->{original_size} : 0),
_header_date($header),
_header_time($header),
_display_name($header);
printf ": %s\n", $header->{comment} if $header->{comment};
}
print "-------- ------- ----- --------- --------\n";
printf "%8d %7d%5.1f%% %s %s %s\n",
$totals->{original_size},
$totals->{encoded_size},
(($totals->{encoded_size} && $totals->{original_size}) ? 100 * ($totals->{original_size} - $totals->{encoded_size}) / $totals->{original_size} : 0),
strftime("%d-%b-%y", localtime((stat($fname))[9])),
strftime("%T", localtime((stat($fname))[9])),
(sprintf(" %d files", $totals->{count}));
},
vv => sub {
my $fname = shift or usage();
my $stream = open_archive($fname);
my $totals = { original_size => 0, encoded_size => 0, count => 0 };
print "Original Packed Ratio Date Time Atts Method CRC L OS\n";
print "-------- ------- ----- --------- -------- -------- ------ ---- -----\n";
while ( defined( my $level = $stream->search_header ) ) {
my $header = Archive::Lha::Header->new(
level => $level,
stream => $stream
);
$stream->seek( $header->{next_header} );
$totals->{original_size} += $header->{original_size};
$totals->{encoded_size} += $header->{encoded_size};
$totals->{count} += 1;
my $name = _display_name($header);
my $os_char = uc($header->{os}[0] // '?');
my $hdr_level = ref($header) =~ /Level(\d)/ ? $1 : '?';
printf "%s\n", $name;
printf "%8d %7d%5.1f%% %s %s ----rwed -%s- %04X %s %s\n",
$header->{original_size},
$header->{encoded_size},
(($header->{encoded_size} && $header->{original_size}) ? 100 * ($header->{original_size} - $header->{encoded_size}) / $header->{original_size} : 0),
_header_date($header),
_header_time($header),
$header->{method},
$header->{crc16},
$hdr_level,
$os_char;
printf ": %s\n", $header->{comment} if $header->{comment};
}
print "-------- ------- ----- --------- --------\n";
printf "%8d %7d%5.1f%% %s %s %s\n",
$totals->{original_size},
$totals->{encoded_size},
(($totals->{encoded_size} && $totals->{original_size}) ? 100 * ($totals->{original_size} - $totals->{encoded_size}) / $totals->{original_size} : 0),
strftime("%d-%b-%y", localtime((stat($fname))[9])),
strftime("%T", localtime((stat($fname))[9])),
(sprintf(" %d files", $totals->{count}));
},
t => sub {
my $fname = shift or usage();
printf "Testing integrity of archive '%s':\n", $fname;
my $stream = open_archive($fname);
my $totals = { original_size => 0, encoded_size => 0, count => 0 };
while ( defined( my $level = $stream->search_header ) ) {
my $header = Archive::Lha::Header->new(
level => $level,
stream => $stream
);
$stream->seek( $header->data_top );
_decode_entry($header, $stream);
$stream->seek( $header->{next_header} );
$totals->{original_size} += $header->{original_size};
$totals->{encoded_size} += $header->{encoded_size};
$totals->{count} += 1;
printf " Testing: (%8d/%8d) %s\n", $header->{original_size}, $header->{original_size}, _display_name($header);
}
my $error = undef;
if ($totals->{count}) {
if (!$error) {
printf "%d files tested, all files OK\n", $totals->{count};
}
} else {
$error = 1;
printf "No files tested.\n";
}
if ($error) {
printf "\nOperation not entirely successful\n\n";
} else {
printf "\nOperation succesful\n\n";
}
},
x => sub {
my $fname = shift or usage();
my %target;
if (@_) {
%target = map { $_ => 1 } @_;
}
my $stream = open_archive($fname);
while ( defined( my $level = $stream->search_header ) ) {
my $header = Archive::Lha::Header->new(
level => $level,
stream => $stream
);
if ( %target and !$target{$header->pathname} ) {
$stream->seek( $header->next_header );
next;
}
$stream->seek( $header->data_top );
if (_is_directory($header)) {
mkpath $header->pathname unless -d $header->pathname;
$stream->seek( $header->{next_header} );
next;
}
my $totals = { original_size => 0, encoded_size => 0, count => 0 };
if ($mode eq 'lv') {
printf " PERMSSN UID GID PACKED SIZE RATIO METHOD CRC STAMP NAME\n";
printf "---------- ----------- ------- ------- ------ ---------- ------------ -------------\n";
} else {
printf " PERMSSN UID GID SIZE RATIO STAMP NAME\n";
printf "---------- ----------- ------- ------ ------------ --------------------\n";
}
while ( defined( my $level = $stream->search_header ) ) {
my $header = Archive::Lha::Header->new(
level => $level,
stream => $stream
);
# Skip to next header - no need to decode for listing
$stream->seek( $header->{next_header} );
$totals->{original_size} += $header->{original_size};
$totals->{encoded_size} += $header->{encoded_size};
$totals->{count} += 1;
my $stamp = _ls_stamp_header($header);
my $name = _display_name($header);
$name = '' if $name eq '.'; # empty root directory
$name .= '/' if _is_directory($header) && $name ne '' && $name !~ m{/$};
$name = _fix_msdos_allcaps($name) if ($header->{os}[0] // '') eq 'M';
my $prefix = _lhasa_prefix($header);
my $ratio_str = _is_directory($header) ? '******'
: sprintf("%5.1f%%", $header->{original_size}
? unpack('f', pack('f', 100 * $header->{encoded_size} / $header->{original_size})) : 100);
if ($mode eq 'lv') {
printf "%s%7d %7d %s -%s- %04x %s %s\n",
$prefix,
$header->{encoded_size},
$header->{original_size},
$ratio_str,
$header->{method},
$header->{crc16},
$stamp,
$name;
} else {
printf "%s%7d %s %s %s\n",
$prefix,
$header->{original_size},
$ratio_str,
$stamp,
$name;
}
}
if ($mode eq 'lv') {
printf "---------- ----------- ------- ------- ------ ---------- ------------ -------------\n";
} else {
printf "---------- ----------- ------- ------ ------------ --------------------\n";
}
my $ratio = $totals->{original_size}
? unpack('f', pack('f', 100 * $totals->{encoded_size} / $totals->{original_size})) : 100;
my $stamp = _ls_stamp((stat($fname))[9]);
# PERMSSN (10) + sep (1) + UID/GID (11) + sep (1) = 23 chars for prefix
# " Total " (PERMSSN 10) + " " (sep) + "%5d files" (UID/GID 11) + " " (sep) = 23
my $file_str = $totals->{count} == 1 ? 'file ' : 'files';
my $prefix = " Total " . sprintf(" %5d %s ", $totals->{count}, $file_str);
if ($mode eq 'lv') {
printf "%s%7d %7d %5.1f%% %s\n",
$prefix,
$totals->{encoded_size},
$totals->{original_size},
$ratio,
$stamp;
} else {
printf "%s%7d %5.1f%% %s\n",
$prefix,
$totals->{original_size},
$ratio,
$stamp;
}
}
sub _is_directory { $_[0]->{method} eq 'lhd' }
# MS-DOS archives store filenames in all-caps. Lhasa detects per-file
# all-caps paths and converts to lowercase. Match that behavior.
sub _fix_msdos_allcaps {
my ($name) = @_;
return $name if $name =~ /[a-z]/; # has lowercase = not all-caps
return lc $name;
}
# Format permission/owner prefix like lhasa does
sub _lhasa_prefix {
my ($header) = @_;
if (defined $header->{unix_perm}) {
my $perm = $header->{unix_perm};
my $type = _is_directory($header) ? 'd' : '-';
my $str = $type;
for my $shift (6, 3, 0) {
my $bits = ($perm >> $shift) & 7;
$str .= ($bits & 4) ? 'r' : '-';
$str .= ($bits & 2) ? 'w' : '-';
$str .= ($bits & 1) ? 'x' : '-';
}
my $uid = $header->{unix_uid} // 0;
my $gid = $header->{unix_gid} // 0;
# PERMSSN(10) + sep(1) + UID/GID(%5d/%-5d = 11) + sep(1) = 23
return sprintf "%s %5d/%-5d ", $str, $uid, $gid;
}
return sprintf "%-23s", '[' . ($header->{os}[1] // 'generic') . ']';
}
sub _decode_entry {
my ($header, $stream) = @_;
return ('', 0) if _is_directory($header);
my $decoded = '';
my $decoder = Archive::Lha::Decode->new(
header => $header,
read => sub { $stream->read(@_) },
( run in 0.812 second using v1.01-cache-2.11-cpan-cb04b646d8c )