Archive-SevenZip
view release on metacpan or search on metacpan
lib/Archive/SevenZip.pm view on Meta::CPAN
package Archive::SevenZip;
use strict;
use warnings;
use Carp qw(croak);
use Encode qw( decode encode );
use File::Basename qw(dirname basename);
use Archive::SevenZip::Entry;
use File::Temp qw(tempfile tempdir);
use File::Copy;
use IPC::Open3 'open3';
use Path::Class;
use Exporter 'import'; # for the error codes, in Archive::Zip API compatibility
=head1 NAME
Archive::SevenZip - Read/write 7z , zip , ISO9960 and other archives
=head1 SYNOPSIS
my $ar = Archive::SevenZip->new(
find => 1,
archivename => $archivename,
verbose => $verbose,
);
for my $entry ( $ar->list ) {
my $target = join "/", "$target_dir", $entry->basename;
$ar->extractMember( $entry->fileName, $target );
};
=head1 METHODS
=cut
our $VERSION= '0.20';
# Archive::Zip API
# Error codes
use constant AZ_OK => 0;
use constant COMPRESSION_STORED => 'Store'; # file is stored (no compression)
use constant COMPRESSION_DEFLATED => 'Deflate'; # file is Deflated
our @EXPORT_OK = (qw(AZ_OK COMPRESSION_STORED COMPRESSION_DEFLATED));
our %EXPORT_TAGS = (
ERROR_CODES => [
qw(
AZ_OK
)
#AZ_STREAM_END
#AZ_ERROR
#AZ_FORMAT_ERROR
#AZ_IO_ERROR
],
CONSTANTS => [
qw(COMPRESSION_STORED COMPRESSION_DEFLATED)
],
);
our %sevenzip_charsetname = (
'UTF-8' => 'UTF-8',
'Latin-1' => 'WIN',
'ISO-8859-1' => 'WIN',
'' => 'DOS', # dunno what the appropriate name would be
);
our %sevenzip_stdin_support = (
#'7z' => 1,
'xz' => 1,
'lzma' => 1,
'tar' => 1,
'gzip' => 1,
'bzip2' => 1,
);
if( $^O !~ /MSWin/i ) {
# Wipe all filesystem encodings because my Debian 7z 9.20 doesn't understand them
$sevenzip_charsetname{ $_ } = ''
for keys %sevenzip_charsetname;
};
our %class_defaults = (
'7zip' => '7z',
fs_encoding => 'UTF-8',
default_options => [ "-y", "-bd" ],
type => 'zip',
system_needs_quotes => scalar ($^O =~ /MSWin/i),
);
=head2 C<< Archive::SevenZip->find_7z_executable >>
my $version = Archive::SevenZip->find_7z_executable()
or die "No 7z found.";
print "Found 7z version '$version'";
Finds the 7z executable in the path or in C<< $ENV{ProgramFiles} >>
or C<< $ENV{ProgramFiles(x86)} >>. This is called
when a C<< Archive::SevenZip >> instance is created with the C<find>
parameter set to 1.
If C<< $ENV{PERL_ARCHIVE_SEVENZIP_BIN} >> is set, this value will be used as
the 7z executable and the path will not be searched.
=cut
sub find_7z_executable {
my($class) = @_;
my $old_default = $class_defaults{ '7zip' };
my $envsep = $^O =~ /MSWin/ ? ';' : ':';
my $found;
if( $ENV{PERL_ARCHIVE_SEVENZIP_BIN}) {
$class_defaults{'7zip'} = $ENV{PERL_ARCHIVE_SEVENZIP_BIN};
$found = $class->version || "7zip not found via environment '($ENV{PERL_ARCHIVE_SEVENZIP_BIN})'";
} else {
my @search;
push @search, split /$envsep/, $ENV{PATH};
if( $^O =~ /MSWin/i ) {
push @search, map { "$_\\7-Zip" } grep {defined} ($ENV{'ProgramFiles'}, $ENV{'ProgramFiles(x86)'});
};
$found = $class->version;
while( ! defined $found and @search) {
my $dir = shift @search;
if ($^O eq 'MSWin32') {
next unless -e file("$dir", "7z.exe" );
}
$class_defaults{'7zip'} = "" . file("$dir", "7z" );
$found = $class->version;
};
};
if( ! $found) {
$class_defaults{ '7zip' } = $old_default;
};
return defined $found ? $found : ()
}
=head2 C<< Archive::SevenZip->new >>
lib/Archive/SevenZip.pm view on Meta::CPAN
$src = "$target_dir/" . encode('UTF-8', $org);
}
rename $src => $extractedName
or croak "Couldn't move '$src' ('$memberOrName') to '$extractedName': $!";
};
return AZ_OK;
};
=head2 C<< $ar->removeMember >>
$ar->removeMember('test.txt');
Removes the member from the archive.
=cut
# strikingly similar to Archive::Zip API
sub removeMember {
my( $self, $name, %_options ) = @_;
my %options = (%$self, %_options);
if( $^O =~ /MSWin/ ) {
$name =~ s!/!\\!g;
}
my $cmd = $self->get_command(
command => "d",
archivename => $options{ archivename },
members => [ $name ],
);
my $fh = $self->run($cmd, encoding => $options{ encoding } );
$self->wait($fh, %options);
return AZ_OK;
};
sub add_quotes {
my $quote = shift;
$quote ?
map {
defined $_ && /\s/ ? qq{"$_"} : $_
} @_
: @_
};
sub get_command {
my( $self, %options )= @_;
$options{ members } ||= [];
$options{ archivename } = $self->{ archivename }
unless defined $options{ archivename };
if( ! exists $options{ fs_encoding }) {
$options{ fs_encoding } = defined $self->{ fs_encoding } ? $self->{ fs_encoding } : $class_defaults{ fs_encoding };
};
if( ! defined $options{ default_options }) {
$options{ default_options } = defined $self->{ default_options } ? $self->{ default_options } : $class_defaults{ default_options };
};
my @charset;
if( defined $options{ fs_encoding }) {
exists $sevenzip_charsetname{ $options{ fs_encoding }}
or croak "Unknown filesystem encoding '$options{ fs_encoding }'";
if( my $charset = $sevenzip_charsetname{ $options{ fs_encoding }}) {
push @charset, "-scs" . $sevenzip_charsetname{ $options{ fs_encoding }};
};
};
for(@{ $options{ members }}) {
$_ = encode $options{ fs_encoding }, $_;
};
my $add_quote = $self->{system_needs_quotes};
return [grep {defined $_}
add_quotes($add_quote, $self->{ '7zip' }),
@{ $options{ default_options }},
$options{ command },
@charset,
add_quotes($add_quote, @{ $options{ options }} ),
# "--",
add_quotes($add_quote, $options{ archivename } ),
add_quotes($add_quote, @{ $options{ members }} ),
];
}
sub run {
my( $self, $cmd, %options )= @_;
my $mode = '-|';
if( defined $options{ stdin } || defined $options{ stdin_fh }) {
$mode = '|-';
};
my $fh;
warn "Opening [@$cmd]"
if $options{ verbose } || $self->{verbose};
if( $self->{verbose} ) {
CORE::open( $fh, $mode, @$cmd)
or croak "Couldn't launch [$mode @$cmd]: $!/$?";
} else {
CORE::open( my $fh_err, '>', File::Spec->devnull )
or warn "Couldn't redirect child STDERR";
my $errh = fileno $fh_err;
my $fh_in = $options{ stdin_fh };
# We accumulate zombie PIDs here, ah well.
$SIG{'CHLD'} = 'IGNORE';
my $pid = open3( $fh_in, my $fh_out, '>&' . $errh, @$cmd)
or croak "Couldn't launch [$mode @$cmd]: $!/$?";
if( $mode eq '|-' ) {
$fh = $fh_in;
} else {
$fh = $fh_out
};
}
if( $options{ encoding }) {
binmode $fh, ":encoding($options{ encoding })";
} elsif( $options{ binmode } ) {
binmode $fh, $options{ binmode };
};
if( $options{ stdin }) {
print {$fh} $options{ stdin };
close $fh;
} elsif( $options{ stdin_fh } ) {
close $fh;
} elsif( $options{ skip }) {
for( 1..$options{ skip }) {
# Read that many lines
local $/ = "\n";
scalar <$fh>;
};
};
$fh;
}
( run in 1.981 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )