Blame lib/Archive/Extract.pm

Packit c0c648
package Archive::Extract;
Packit c0c648
use if $] > 5.017, 'deprecate';
Packit c0c648
Packit c0c648
use strict;
Packit c0c648
Packit c0c648
use Cwd                         qw[cwd chdir];
Packit c0c648
use Carp                        qw[carp];
Packit c0c648
use IPC::Cmd                    qw[run can_run];
Packit c0c648
use FileHandle;
Packit c0c648
use File::Path                  qw[mkpath];
Packit c0c648
use File::Spec;
Packit c0c648
use File::Basename              qw[dirname basename];
Packit c0c648
use Params::Check               qw[check];
Packit c0c648
use Module::Load::Conditional   qw[can_load check_install];
Packit c0c648
use Locale::Maketext::Simple    Style => 'gettext';
Packit c0c648
Packit c0c648
### solaris has silly /bin/tar output ###
Packit c0c648
use constant ON_SOLARIS     => $^O eq 'solaris' ? 1 : 0;
Packit c0c648
use constant ON_NETBSD      => $^O eq 'netbsd' ? 1 : 0;
Packit c0c648
use constant ON_OPENBSD     => $^O =~ m!^(openbsd|bitrig)$! ? 1 : 0;
Packit c0c648
use constant ON_FREEBSD     => $^O =~ m!^(free|midnight|dragonfly)(bsd)?$! ? 1 : 0;
Packit c0c648
use constant ON_LINUX       => $^O eq 'linux' ? 1 : 0;
Packit c0c648
use constant FILE_EXISTS    => sub { -e $_[0] ? 1 : 0 };
Packit c0c648
Packit c0c648
### VMS may require quoting upper case command options
Packit c0c648
use constant ON_VMS         => $^O eq 'VMS' ? 1 : 0;
Packit c0c648
Packit c0c648
### Windows needs special treatment of Tar options
Packit c0c648
use constant ON_WIN32       => $^O eq 'MSWin32' ? 1 : 0;
Packit c0c648
Packit c0c648
### we can't use this extraction method, because of missing
Packit c0c648
### modules/binaries:
Packit c0c648
use constant METHOD_NA      => [];
Packit c0c648
Packit c0c648
### If these are changed, update @TYPES and the new() POD
Packit c0c648
use constant TGZ            => 'tgz';
Packit c0c648
use constant TAR            => 'tar';
Packit c0c648
use constant GZ             => 'gz';
Packit c0c648
use constant ZIP            => 'zip';
Packit c0c648
use constant BZ2            => 'bz2';
Packit c0c648
use constant TBZ            => 'tbz';
Packit c0c648
use constant Z              => 'Z';
Packit c0c648
use constant LZMA           => 'lzma';
Packit c0c648
use constant XZ             => 'xz';
Packit c0c648
use constant TXZ            => 'txz';
Packit c0c648
Packit c0c648
use vars qw[$VERSION $PREFER_BIN $PROGRAMS $WARN $DEBUG
Packit c0c648
            $_ALLOW_BIN $_ALLOW_PURE_PERL $_ALLOW_TAR_ITER
Packit c0c648
         ];
Packit c0c648
Packit c0c648
$VERSION            = '0.80';
Packit c0c648
$PREFER_BIN         = 0;
Packit c0c648
$WARN               = 1;
Packit c0c648
$DEBUG              = 0;
Packit c0c648
$_ALLOW_PURE_PERL   = 1;    # allow pure perl extractors
Packit c0c648
$_ALLOW_BIN         = 1;    # allow binary extractors
Packit c0c648
$_ALLOW_TAR_ITER    = 1;    # try to use Archive::Tar->iter if available
Packit c0c648
Packit c0c648
# same as all constants
Packit c0c648
my @Types           = ( TGZ, TAR, GZ, ZIP, BZ2, TBZ, Z, LZMA, XZ, TXZ );
Packit c0c648
Packit c0c648
local $Params::Check::VERBOSE = $Params::Check::VERBOSE = 1;
Packit c0c648
local $Module::Load::Conditional::FORCE_SAFE_INC = 1;
Packit c0c648
Packit c0c648
=pod
Packit c0c648
Packit c0c648
=head1 NAME
Packit c0c648
Packit c0c648
Archive::Extract - A generic archive extracting mechanism
Packit c0c648
Packit c0c648
=head1 SYNOPSIS
Packit c0c648
Packit c0c648
    use Archive::Extract;
Packit c0c648
Packit c0c648
    ### build an Archive::Extract object ###
Packit c0c648
    my $ae = Archive::Extract->new( archive => 'foo.tgz' );
Packit c0c648
Packit c0c648
    ### extract to cwd() ###
Packit c0c648
    my $ok = $ae->extract;
Packit c0c648
Packit c0c648
    ### extract to /tmp ###
Packit c0c648
    my $ok = $ae->extract( to => '/tmp' );
Packit c0c648
Packit c0c648
    ### what if something went wrong?
Packit c0c648
    my $ok = $ae->extract or die $ae->error;
Packit c0c648
Packit c0c648
    ### files from the archive ###
Packit c0c648
    my $files   = $ae->files;
Packit c0c648
Packit c0c648
    ### dir that was extracted to ###
Packit c0c648
    my $outdir  = $ae->extract_path;
Packit c0c648
Packit c0c648
Packit c0c648
    ### quick check methods ###
Packit c0c648
    $ae->is_tar     # is it a .tar file?
Packit c0c648
    $ae->is_tgz     # is it a .tar.gz or .tgz file?
Packit c0c648
    $ae->is_gz;     # is it a .gz file?
Packit c0c648
    $ae->is_zip;    # is it a .zip file?
Packit c0c648
    $ae->is_bz2;    # is it a .bz2 file?
Packit c0c648
    $ae->is_tbz;    # is it a .tar.bz2 or .tbz file?
Packit c0c648
    $ae->is_lzma;   # is it a .lzma file?
Packit c0c648
    $ae->is_xz;     # is it a .xz file?
Packit c0c648
    $ae->is_txz;    # is it a .tar.xz or .txz file?
Packit c0c648
Packit c0c648
    ### absolute path to the archive you provided ###
Packit c0c648
    $ae->archive;
Packit c0c648
Packit c0c648
    ### commandline tools, if found ###
Packit c0c648
    $ae->bin_tar     # path to /bin/tar, if found
Packit c0c648
    $ae->bin_gzip    # path to /bin/gzip, if found
Packit c0c648
    $ae->bin_unzip   # path to /bin/unzip, if found
Packit c0c648
    $ae->bin_bunzip2 # path to /bin/bunzip2 if found
Packit c0c648
    $ae->bin_unlzma  # path to /bin/unlzma if found
Packit c0c648
    $ae->bin_unxz    # path to /bin/unxz if found
Packit c0c648
Packit c0c648
=head1 DESCRIPTION
Packit c0c648
Packit c0c648
Archive::Extract is a generic archive extraction mechanism.
Packit c0c648
Packit c0c648
It allows you to extract any archive file of the type .tar, .tar.gz,
Packit c0c648
.gz, .Z, tar.bz2, .tbz, .bz2, .zip, .xz,, .txz, .tar.xz or .lzma
Packit c0c648
without having to worry how it
Packit c0c648
does so, or use different interfaces for each type by using either
Packit c0c648
perl modules, or commandline tools on your system.
Packit c0c648
Packit c0c648
See the C<HOW IT WORKS> section further down for details.
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
Packit c0c648
### see what /bin/programs are available ###
Packit c0c648
$PROGRAMS = {};
Packit c0c648
CMD: for my $pgm (qw[tar unzip gzip bunzip2 uncompress unlzma unxz]) {
Packit c0c648
    if ( $pgm eq 'unzip' and ON_FREEBSD and my $unzip = can_run('info-unzip') ) {
Packit c0c648
      $PROGRAMS->{$pgm} = $unzip;
Packit c0c648
      next CMD;
Packit c0c648
    }
Packit c0c648
    if ( $pgm eq 'unzip' and ( ON_FREEBSD || ON_LINUX ) ) {
Packit c0c648
      local $IPC::Cmd::INSTANCES = 1;
Packit c0c648
      ($PROGRAMS->{$pgm}) = grep { _is_infozip_esque($_) } can_run($pgm);
Packit c0c648
      next CMD;
Packit c0c648
    }
Packit c0c648
    if ( $pgm eq 'unzip' and ON_NETBSD ) {
Packit c0c648
      local $IPC::Cmd::INSTANCES = 1;
Packit c0c648
      ($PROGRAMS->{$pgm}) = grep { m!/usr/pkg/! } can_run($pgm);
Packit c0c648
      next CMD;
Packit c0c648
    }
Packit c0c648
    if ( $pgm eq 'tar' and ( ON_OPENBSD || ON_SOLARIS || ON_NETBSD ) ) {
Packit c0c648
      # try gtar first
Packit c0c648
      next CMD if $PROGRAMS->{$pgm} = can_run('gtar');
Packit c0c648
    }
Packit c0c648
    $PROGRAMS->{$pgm} = can_run($pgm);
Packit c0c648
}
Packit c0c648
Packit c0c648
### mapping from types to extractor methods ###
Packit c0c648
my $Mapping = {  # binary program           # pure perl module
Packit c0c648
    is_tgz  => { bin => '_untar_bin',       pp => '_untar_at'   },
Packit c0c648
    is_tar  => { bin => '_untar_bin',       pp => '_untar_at'   },
Packit c0c648
    is_gz   => { bin => '_gunzip_bin',      pp => '_gunzip_cz'  },
Packit c0c648
    is_zip  => { bin => '_unzip_bin',       pp => '_unzip_az'   },
Packit c0c648
    is_tbz  => { bin => '_untar_bin',       pp => '_untar_at'   },
Packit c0c648
    is_bz2  => { bin => '_bunzip2_bin',     pp => '_bunzip2_bz2'},
Packit c0c648
    is_Z    => { bin => '_uncompress_bin',  pp => '_gunzip_cz'  },
Packit c0c648
    is_lzma => { bin => '_unlzma_bin',      pp => '_unlzma_cz'  },
Packit c0c648
    is_xz   => { bin => '_unxz_bin',        pp => '_unxz_cz'    },
Packit c0c648
    is_txz  => { bin => '_untar_bin',       pp => '_untar_at'   },
Packit c0c648
};
Packit c0c648
Packit c0c648
{   ### use subs so we re-generate array refs etc for the no-override flags
Packit c0c648
    ### if we don't, then we reuse the same arrayref, meaning objects store
Packit c0c648
    ### previous errors
Packit c0c648
    my $tmpl = {
Packit c0c648
        archive         => sub { { required => 1, allow => FILE_EXISTS }    },
Packit c0c648
        type            => sub { { default => '', allow => [ @Types ] }     },
Packit c0c648
        _error_msg      => sub { { no_override => 1, default => [] }        },
Packit c0c648
        _error_msg_long => sub { { no_override => 1, default => [] }        },
Packit c0c648
    };
Packit c0c648
Packit c0c648
    ### build accessors ###
Packit c0c648
    for my $method( keys %$tmpl,
Packit c0c648
                    qw[_extractor _gunzip_to files extract_path],
Packit c0c648
    ) {
Packit c0c648
        no strict 'refs';
Packit c0c648
        *$method = sub {
Packit c0c648
                        my $self = shift;
Packit c0c648
                        $self->{$method} = $_[0] if @_;
Packit c0c648
                        return $self->{$method};
Packit c0c648
                    }
Packit c0c648
    }
Packit c0c648
Packit c0c648
=head1 METHODS
Packit c0c648
Packit c0c648
=head2 $ae = Archive::Extract->new(archive => '/path/to/archive',[type => TYPE])
Packit c0c648
Packit c0c648
Creates a new C<Archive::Extract> object based on the archive file you
Packit c0c648
passed it. Automatically determines the type of archive based on the
Packit c0c648
extension, but you can override that by explicitly providing the
Packit c0c648
C<type> argument.
Packit c0c648
Packit c0c648
Valid values for C<type> are:
Packit c0c648
Packit c0c648
=over 4
Packit c0c648
Packit c0c648
=item tar
Packit c0c648
Packit c0c648
Standard tar files, as produced by, for example, C</bin/tar>.
Packit c0c648
Corresponds to a C<.tar> suffix.
Packit c0c648
Packit c0c648
=item tgz
Packit c0c648
Packit c0c648
Gzip compressed tar files, as produced by, for example C</bin/tar -z>.
Packit c0c648
Corresponds to a C<.tgz> or C<.tar.gz> suffix.
Packit c0c648
Packit c0c648
=item gz
Packit c0c648
Packit c0c648
Gzip compressed file, as produced by, for example C</bin/gzip>.
Packit c0c648
Corresponds to a C<.gz> suffix.
Packit c0c648
Packit c0c648
=item Z
Packit c0c648
Packit c0c648
Lempel-Ziv compressed file, as produced by, for example C</bin/compress>.
Packit c0c648
Corresponds to a C<.Z> suffix.
Packit c0c648
Packit c0c648
=item zip
Packit c0c648
Packit c0c648
Zip compressed file, as produced by, for example C</bin/zip>.
Packit c0c648
Corresponds to a C<.zip>, C<.jar> or C<.par> suffix.
Packit c0c648
Packit c0c648
=item bz2
Packit c0c648
Packit c0c648
Bzip2 compressed file, as produced by, for example, C</bin/bzip2>.
Packit c0c648
Corresponds to a C<.bz2> suffix.
Packit c0c648
Packit c0c648
=item tbz
Packit c0c648
Packit c0c648
Bzip2 compressed tar file, as produced by, for example C</bin/tar -j>.
Packit c0c648
Corresponds to a C<.tbz> or C<.tar.bz2> suffix.
Packit c0c648
Packit c0c648
=item lzma
Packit c0c648
Packit c0c648
Lzma compressed file, as produced by C</bin/lzma>.
Packit c0c648
Corresponds to a C<.lzma> suffix.
Packit c0c648
Packit c0c648
=item xz
Packit c0c648
Packit c0c648
Xz compressed file, as produced by C</bin/xz>.
Packit c0c648
Corresponds to a C<.xz> suffix.
Packit c0c648
Packit c0c648
=item txz
Packit c0c648
Packit c0c648
Xz compressed tar file, as produced by, for example C</bin/tar -J>.
Packit c0c648
Corresponds to a C<.txz> or C<.tar.xz> suffix.
Packit c0c648
Packit c0c648
=back
Packit c0c648
Packit c0c648
Returns a C<Archive::Extract> object on success, or false on failure.
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
    ### constructor ###
Packit c0c648
    sub new {
Packit c0c648
        my $class   = shift;
Packit c0c648
        my %hash    = @_;
Packit c0c648
Packit c0c648
        ### see above why we use subs here and generate the template;
Packit c0c648
        ### it's basically to not re-use arrayrefs
Packit c0c648
        my %utmpl   = map { $_ => $tmpl->{$_}->() } keys %$tmpl;
Packit c0c648
Packit c0c648
        my $parsed = check( \%utmpl, \%hash ) or return;
Packit c0c648
Packit c0c648
        ### make sure we have an absolute path ###
Packit c0c648
        my $ar = $parsed->{archive} = File::Spec->rel2abs( $parsed->{archive} );
Packit c0c648
Packit c0c648
        ### figure out the type, if it wasn't already specified ###
Packit c0c648
        unless ( $parsed->{type} ) {
Packit c0c648
            $parsed->{type} =
Packit c0c648
                $ar =~ /.+?\.(?:tar\.gz|tgz)$/i         ? TGZ   :
Packit c0c648
                $ar =~ /.+?\.gz$/i                      ? GZ    :
Packit c0c648
                $ar =~ /.+?\.tar$/i                     ? TAR   :
Packit c0c648
                $ar =~ /.+?\.(zip|jar|ear|war|par)$/i   ? ZIP   :
Packit c0c648
                $ar =~ /.+?\.(?:tbz2?|tar\.bz2?)$/i     ? TBZ   :
Packit c0c648
                $ar =~ /.+?\.bz2$/i                     ? BZ2   :
Packit c0c648
                $ar =~ /.+?\.Z$/                        ? Z     :
Packit c0c648
                $ar =~ /.+?\.lzma$/                     ? LZMA  :
Packit c0c648
                $ar =~ /.+?\.(?:txz|tar\.xz)$/i         ? TXZ   :
Packit c0c648
                $ar =~ /.+?\.xz$/                       ? XZ    :
Packit c0c648
                '';
Packit c0c648
Packit c0c648
        }
Packit c0c648
Packit c0c648
        bless $parsed, $class;
Packit c0c648
Packit c0c648
        ### don't know what type of file it is
Packit c0c648
        ### XXX this *has* to be an object call, not a package call
Packit c0c648
        return $parsed->_error(loc("Cannot determine file type for '%1'",
Packit c0c648
                                $parsed->{archive} )) unless $parsed->{type};
Packit c0c648
        return $parsed;
Packit c0c648
    }
Packit c0c648
}
Packit c0c648
Packit c0c648
=head2 $ae->extract( [to => '/output/path'] )
Packit c0c648
Packit c0c648
Extracts the archive represented by the C<Archive::Extract> object to
Packit c0c648
the path of your choice as specified by the C<to> argument. Defaults to
Packit c0c648
C<cwd()>.
Packit c0c648
Packit c0c648
Since C<.gz> files never hold a directory, but only a single file; if
Packit c0c648
the C<to> argument is an existing directory, the file is extracted
Packit c0c648
there, with its C<.gz> suffix stripped.
Packit c0c648
If the C<to> argument is not an existing directory, the C<to> argument
Packit c0c648
is understood to be a filename, if the archive type is C<gz>.
Packit c0c648
In the case that you did not specify a C<to> argument, the output
Packit c0c648
file will be the name of the archive file, stripped from its C<.gz>
Packit c0c648
suffix, in the current working directory.
Packit c0c648
Packit c0c648
C<extract> will try a pure perl solution first, and then fall back to
Packit c0c648
commandline tools if they are available. See the C<GLOBAL VARIABLES>
Packit c0c648
section below on how to alter this behaviour.
Packit c0c648
Packit c0c648
It will return true on success, and false on failure.
Packit c0c648
Packit c0c648
On success, it will also set the follow attributes in the object:
Packit c0c648
Packit c0c648
=over 4
Packit c0c648
Packit c0c648
=item $ae->extract_path
Packit c0c648
Packit c0c648
This is the directory that the files where extracted to.
Packit c0c648
Packit c0c648
=item $ae->files
Packit c0c648
Packit c0c648
This is an array ref with the paths of all the files in the archive,
Packit c0c648
relative to the C<to> argument you specified.
Packit c0c648
To get the full path to an extracted file, you would use:
Packit c0c648
Packit c0c648
    File::Spec->catfile( $to, $ae->files->[0] );
Packit c0c648
Packit c0c648
Note that all files from a tar archive will be in unix format, as per
Packit c0c648
the tar specification.
Packit c0c648
Packit c0c648
=back
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
sub extract {
Packit c0c648
    my $self = shift;
Packit c0c648
    my %hash = @_;
Packit c0c648
Packit c0c648
    ### reset error messages
Packit c0c648
    $self->_error_msg( [] );
Packit c0c648
    $self->_error_msg_long( [] );
Packit c0c648
Packit c0c648
    my $to;
Packit c0c648
    my $tmpl = {
Packit c0c648
        to  => { default => '.', store => \$to }
Packit c0c648
    };
Packit c0c648
Packit c0c648
    check( $tmpl, \%hash ) or return;
Packit c0c648
Packit c0c648
    ### so 'to' could be a file or a dir, depending on whether it's a .gz
Packit c0c648
    ### file, or basically anything else.
Packit c0c648
    ### so, check that, then act accordingly.
Packit c0c648
    ### set an accessor specifically so _gunzip can know what file to extract
Packit c0c648
    ### to.
Packit c0c648
    my $dir;
Packit c0c648
    {   ### a foo.gz file
Packit c0c648
        if( $self->is_gz or $self->is_bz2 or $self->is_Z or $self->is_lzma or $self->is_xz ) {
Packit c0c648
Packit c0c648
            my $cp = $self->archive; $cp =~ s/\.(?:gz|bz2?|Z|lzma|xz)$//i;
Packit c0c648
Packit c0c648
            ### to is a dir?
Packit c0c648
            if ( -d $to ) {
Packit c0c648
                $dir = $to;
Packit c0c648
                $self->_gunzip_to( basename($cp) );
Packit c0c648
Packit c0c648
            ### then it's a filename
Packit c0c648
            } else {
Packit c0c648
                $dir = dirname($to);
Packit c0c648
                $self->_gunzip_to( basename($to) );
Packit c0c648
            }
Packit c0c648
Packit c0c648
        ### not a foo.gz file
Packit c0c648
        } else {
Packit c0c648
            $dir = $to;
Packit c0c648
        }
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### make the dir if it doesn't exist ###
Packit c0c648
    unless( -d $dir ) {
Packit c0c648
        eval { mkpath( $dir ) };
Packit c0c648
Packit c0c648
        return $self->_error(loc("Could not create path '%1': %2", $dir, $@))
Packit c0c648
            if $@;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### get the current dir, to restore later ###
Packit c0c648
    my $cwd = cwd();
Packit c0c648
Packit c0c648
    my $ok = 1;
Packit c0c648
    EXTRACT: {
Packit c0c648
Packit c0c648
        ### chdir to the target dir ###
Packit c0c648
        unless( chdir $dir ) {
Packit c0c648
            $self->_error(loc("Could not chdir to '%1': %2", $dir, $!));
Packit c0c648
            $ok = 0; last EXTRACT;
Packit c0c648
        }
Packit c0c648
Packit c0c648
        ### set files to an empty array ref, so there's always an array
Packit c0c648
        ### ref IN the accessor, to avoid errors like:
Packit c0c648
        ### Can't use an undefined value as an ARRAY reference at
Packit c0c648
        ### ../lib/Archive/Extract.pm line 742. (rt #19815)
Packit c0c648
        $self->files( [] );
Packit c0c648
Packit c0c648
        ### find out the dispatch methods needed for this type of
Packit c0c648
        ### archive. Do a $self->is_XXX to figure out the type, then
Packit c0c648
        ### get the hashref with bin + pure perl dispatchers.
Packit c0c648
        my ($map) = map { $Mapping->{$_} } grep { $self->$_ } keys %$Mapping;
Packit c0c648
Packit c0c648
        ### add pure perl extractor if allowed & add bin extractor if allowed
Packit c0c648
        my @methods;
Packit c0c648
        push @methods, $map->{'pp'}  if $_ALLOW_PURE_PERL;
Packit c0c648
        push @methods, $map->{'bin'} if $_ALLOW_BIN;
Packit c0c648
Packit c0c648
        ### reverse it if we prefer bin extractors
Packit c0c648
        @methods = reverse @methods if $PREFER_BIN;
Packit c0c648
Packit c0c648
        my($na, $fail);
Packit c0c648
        for my $method (@methods) {
Packit c0c648
            $self->debug( "# Extracting with ->$method\n" );
Packit c0c648
Packit c0c648
            my $rv = $self->$method;
Packit c0c648
Packit c0c648
            ### a positive extraction
Packit c0c648
            if( $rv and $rv ne METHOD_NA ) {
Packit c0c648
                $self->debug( "# Extraction succeeded\n" );
Packit c0c648
                $self->_extractor($method);
Packit c0c648
                last;
Packit c0c648
Packit c0c648
            ### method is not available
Packit c0c648
            } elsif ( $rv and $rv eq METHOD_NA ) {
Packit c0c648
                $self->debug( "# Extraction method not available\n" );
Packit c0c648
                $na++;
Packit c0c648
            } else {
Packit c0c648
                $self->debug( "# Extraction method failed\n" );
Packit c0c648
                $fail++;
Packit c0c648
            }
Packit c0c648
        }
Packit c0c648
Packit c0c648
        ### warn something went wrong if we didn't get an extractor
Packit c0c648
        unless( $self->_extractor ) {
Packit c0c648
            my $diag = $fail ? loc("Extract failed due to errors") :
Packit c0c648
                       $na   ? loc("Extract failed; no extractors available") :
Packit c0c648
                       '';
Packit c0c648
Packit c0c648
            $self->_error($diag);
Packit c0c648
            $ok = 0;
Packit c0c648
        }
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### and chdir back ###
Packit c0c648
    unless( chdir $cwd ) {
Packit c0c648
        $self->_error(loc("Could not chdir back to start dir '%1': %2'",
Packit c0c648
                            $cwd, $!));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    return $ok;
Packit c0c648
}
Packit c0c648
Packit c0c648
=pod
Packit c0c648
Packit c0c648
=head1 ACCESSORS
Packit c0c648
Packit c0c648
=head2 $ae->error([BOOL])
Packit c0c648
Packit c0c648
Returns the last encountered error as string.
Packit c0c648
Pass it a true value to get the C<Carp::longmess()> output instead.
Packit c0c648
Packit c0c648
=head2 $ae->extract_path
Packit c0c648
Packit c0c648
This is the directory the archive got extracted to.
Packit c0c648
See C<extract()> for details.
Packit c0c648
Packit c0c648
=head2 $ae->files
Packit c0c648
Packit c0c648
This is an array ref holding all the paths from the archive.
Packit c0c648
See C<extract()> for details.
Packit c0c648
Packit c0c648
=head2 $ae->archive
Packit c0c648
Packit c0c648
This is the full path to the archive file represented by this
Packit c0c648
C<Archive::Extract> object.
Packit c0c648
Packit c0c648
=head2 $ae->type
Packit c0c648
Packit c0c648
This is the type of archive represented by this C<Archive::Extract>
Packit c0c648
object. See accessors below for an easier way to use this.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=head2 $ae->types
Packit c0c648
Packit c0c648
Returns a list of all known C<types> for C<Archive::Extract>'s
Packit c0c648
C<new> method.
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
sub types { return @Types }
Packit c0c648
Packit c0c648
=head2 $ae->is_tgz
Packit c0c648
Packit c0c648
Returns true if the file is of type C<.tar.gz>.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=head2 $ae->is_tar
Packit c0c648
Packit c0c648
Returns true if the file is of type C<.tar>.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=head2 $ae->is_gz
Packit c0c648
Packit c0c648
Returns true if the file is of type C<.gz>.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=head2 $ae->is_Z
Packit c0c648
Packit c0c648
Returns true if the file is of type C<.Z>.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=head2 $ae->is_zip
Packit c0c648
Packit c0c648
Returns true if the file is of type C<.zip>.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=head2 $ae->is_lzma
Packit c0c648
Packit c0c648
Returns true if the file is of type C<.lzma>.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=head2 $ae->is_xz
Packit c0c648
Packit c0c648
Returns true if the file is of type C<.xz>.
Packit c0c648
See the C<new()> method for details.
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
### quick check methods ###
Packit c0c648
sub is_tgz  { return $_[0]->type eq TGZ }
Packit c0c648
sub is_tar  { return $_[0]->type eq TAR }
Packit c0c648
sub is_gz   { return $_[0]->type eq GZ  }
Packit c0c648
sub is_zip  { return $_[0]->type eq ZIP }
Packit c0c648
sub is_tbz  { return $_[0]->type eq TBZ }
Packit c0c648
sub is_bz2  { return $_[0]->type eq BZ2 }
Packit c0c648
sub is_Z    { return $_[0]->type eq Z   }
Packit c0c648
sub is_lzma { return $_[0]->type eq LZMA }
Packit c0c648
sub is_xz   { return $_[0]->type eq XZ   }
Packit c0c648
sub is_txz  { return $_[0]->type eq TXZ }
Packit c0c648
Packit c0c648
=pod
Packit c0c648
Packit c0c648
=head2 $ae->bin_tar
Packit c0c648
Packit c0c648
Returns the full path to your tar binary, if found.
Packit c0c648
Packit c0c648
=head2 $ae->bin_gzip
Packit c0c648
Packit c0c648
Returns the full path to your gzip binary, if found
Packit c0c648
Packit c0c648
=head2 $ae->bin_unzip
Packit c0c648
Packit c0c648
Returns the full path to your unzip binary, if found
Packit c0c648
Packit c0c648
=head2 $ae->bin_unlzma
Packit c0c648
Packit c0c648
Returns the full path to your unlzma binary, if found
Packit c0c648
Packit c0c648
=head2 $ae->bin_unxz
Packit c0c648
Packit c0c648
Returns the full path to your unxz binary, if found
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
### paths to commandline tools ###
Packit c0c648
sub bin_gzip        { return $PROGRAMS->{'gzip'}    if $PROGRAMS->{'gzip'}  }
Packit c0c648
sub bin_unzip       { return $PROGRAMS->{'unzip'}   if $PROGRAMS->{'unzip'} }
Packit c0c648
sub bin_tar         { return $PROGRAMS->{'tar'}     if $PROGRAMS->{'tar'}   }
Packit c0c648
sub bin_bunzip2     { return $PROGRAMS->{'bunzip2'} if $PROGRAMS->{'bunzip2'} }
Packit c0c648
sub bin_uncompress  { return $PROGRAMS->{'uncompress'}
Packit c0c648
                                                 if $PROGRAMS->{'uncompress'} }
Packit c0c648
sub bin_unlzma      { return $PROGRAMS->{'unlzma'}  if $PROGRAMS->{'unlzma'} }
Packit c0c648
sub bin_unxz        { return $PROGRAMS->{'unxz'}    if $PROGRAMS->{'unxz'} }
Packit c0c648
Packit c0c648
=head2 $bool = $ae->have_old_bunzip2
Packit c0c648
Packit c0c648
Older versions of C</bin/bunzip2>, from before the C<bunzip2 1.0> release,
Packit c0c648
require all archive names to end in C<.bz2> or it will not extract
Packit c0c648
them. This method checks if you have a recent version of C<bunzip2>
Packit c0c648
that allows any extension, or an older one that doesn't.
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
sub have_old_bunzip2 {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### no bunzip2? no old bunzip2 either :)
Packit c0c648
    return unless $self->bin_bunzip2;
Packit c0c648
Packit c0c648
    ### if we can't run this, we can't be sure if it's too old or not
Packit c0c648
    ### XXX stupid stupid stupid bunzip2 doesn't understand --version
Packit c0c648
    ### is not a request to extract data:
Packit c0c648
    ### $ bunzip2 --version
Packit c0c648
    ### bzip2, a block-sorting file compressor.  Version 1.0.2, 30-Dec-2001.
Packit c0c648
    ### [...]
Packit c0c648
    ### bunzip2: I won't read compressed data from a terminal.
Packit c0c648
    ### bunzip2: For help, type: `bunzip2 --help'.
Packit c0c648
    ### $ echo $?
Packit c0c648
    ### 1
Packit c0c648
    ### HATEFUL!
Packit c0c648
Packit c0c648
    ### double hateful: bunzip2 --version also hangs if input is a pipe
Packit c0c648
    ### See #32370: Archive::Extract will hang if stdin is a pipe [+PATCH]
Packit c0c648
    ### So, we have to provide *another* argument which is a fake filename,
Packit c0c648
    ### just so it wont try to read from stdin to print its version..
Packit c0c648
    ### *sigh*
Packit c0c648
    ### Even if the file exists, it won't clobber or change it.
Packit c0c648
    my $buffer;
Packit c0c648
    scalar run(
Packit c0c648
         command => [$self->bin_bunzip2, '--version', 'NoSuchFile'],
Packit c0c648
         verbose => 0,
Packit c0c648
         buffer  => \$buffer
Packit c0c648
    );
Packit c0c648
Packit c0c648
    ### no output
Packit c0c648
    return unless $buffer;
Packit c0c648
Packit c0c648
    my ($version) = $buffer =~ /version \s+ (\d+)/ix;
Packit c0c648
Packit c0c648
    return 1 if $version < 1;
Packit c0c648
    return;
Packit c0c648
}
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# Untar code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
### annoying issue with (gnu) tar on win32, as illustrated by this
Packit c0c648
### bug: https://rt.cpan.org/Ticket/Display.html?id=40138
Packit c0c648
### which shows that (gnu) tar will interpret a file name with a :
Packit c0c648
### in it as a remote file name, so C:\tmp\foo.txt is interpreted
Packit c0c648
### as a remote shell, and the extract fails.
Packit c0c648
{   my @ExtraTarFlags;
Packit c0c648
    if( ON_WIN32 and my $cmd = __PACKAGE__->bin_tar ) {
Packit c0c648
Packit c0c648
        ### if this is gnu tar we are running, we need to use --force-local
Packit c0c648
        push @ExtraTarFlags, '--force-local' if `$cmd --version` =~ /gnu tar/i;
Packit c0c648
    }
Packit c0c648
Packit c0c648
Packit c0c648
    ### use /bin/tar to extract ###
Packit c0c648
    sub _untar_bin {
Packit c0c648
        my $self = shift;
Packit c0c648
Packit c0c648
        ### check for /bin/tar ###
Packit c0c648
        ### check for /bin/gzip if we need it ###
Packit c0c648
        ### if any of the binaries are not available, return NA
Packit c0c648
        {   my $diag =  !$self->bin_tar ?
Packit c0c648
                            loc("No '%1' program found", '/bin/tar') :
Packit c0c648
                        $self->is_tgz && !$self->bin_gzip ?
Packit c0c648
                            loc("No '%1' program found", '/bin/gzip') :
Packit c0c648
                        $self->is_tbz && !$self->bin_bunzip2 ?
Packit c0c648
                            loc("No '%1' program found", '/bin/bunzip2') :
Packit c0c648
                        $self->is_txz && !$self->bin_unxz ?
Packit c0c648
                            loc("No '%1' program found", '/bin/unxz') :
Packit c0c648
                        '';
Packit c0c648
Packit c0c648
            if( $diag ) {
Packit c0c648
                $self->_error( $diag );
Packit c0c648
                return METHOD_NA;
Packit c0c648
            }
Packit c0c648
        }
Packit c0c648
Packit c0c648
        ### XXX figure out how to make IPC::Run do this in one call --
Packit c0c648
        ### currently i don't know how to get output of a command after a pipe
Packit c0c648
        ### trapped in a scalar. Mailed barries about this 5th of june 2004.
Packit c0c648
Packit c0c648
        ### see what command we should run, based on whether
Packit c0c648
        ### it's a .tgz or .tar
Packit c0c648
Packit c0c648
        ### GNU tar can't handled VMS filespecs, but VMSTAR can handle Unix filespecs.
Packit c0c648
        my $archive = $self->archive;
Packit c0c648
        $archive = VMS::Filespec::unixify($archive) if ON_VMS;
Packit c0c648
Packit c0c648
        ### XXX solaris tar and bsdtar are having different outputs
Packit c0c648
        ### depending whether you run with -x or -t
Packit c0c648
        ### compensate for this insanity by running -t first, then -x
Packit c0c648
        {    my $cmd =
Packit c0c648
                $self->is_tgz ? [$self->bin_gzip, '-c', '-d', '-f', $archive, '|',
Packit c0c648
                                 $self->bin_tar, '-tf', '-'] :
Packit c0c648
                $self->is_tbz ? [$self->bin_bunzip2, '-cd', $archive, '|',
Packit c0c648
                                 $self->bin_tar, '-tf', '-'] :
Packit c0c648
                $self->is_txz ? [$self->bin_unxz, '-cd', $archive, '|',
Packit c0c648
                                 $self->bin_tar, '-tf', '-'] :
Packit c0c648
                [$self->bin_tar, @ExtraTarFlags, '-tf', $archive];
Packit c0c648
Packit c0c648
            ### run the command
Packit c0c648
            ### newer versions of 'tar' (1.21 and up) now print record size
Packit c0c648
            ### to STDERR as well if v OR t is given (used to be both). This
Packit c0c648
            ### is a 'feature' according to the changelog, so we must now only
Packit c0c648
            ### inspect STDOUT, otherwise, failures like these occur:
Packit c0c648
            ### http://www.cpantesters.org/cpan/report/3230366
Packit c0c648
            my $buffer  = '';
Packit c0c648
            my @out     = run(  command => $cmd,
Packit c0c648
                                buffer  => \$buffer,
Packit c0c648
                                verbose => $DEBUG );
Packit c0c648
Packit c0c648
            ### command was unsuccessful
Packit c0c648
            unless( $out[0] ) {
Packit c0c648
                return $self->_error(loc(
Packit c0c648
                                "Error listing contents of archive '%1': %2",
Packit c0c648
                                $archive, $buffer ));
Packit c0c648
            }
Packit c0c648
Packit c0c648
            ### no buffers available?
Packit c0c648
            if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
Packit c0c648
                $self->_error( $self->_no_buffer_files( $archive ) );
Packit c0c648
Packit c0c648
            } else {
Packit c0c648
                ### if we're on solaris we /might/ be using /bin/tar, which has
Packit c0c648
                ### a weird output format... we might also be using
Packit c0c648
                ### /usr/local/bin/tar, which is gnu tar, which is perfectly
Packit c0c648
                ### fine... so we have to do some guessing here =/
Packit c0c648
                my @files = map { chomp;
Packit c0c648
                              !ON_SOLARIS ? $_
Packit c0c648
                                          : (m|^ x \s+  # 'xtract' -- sigh
Packit c0c648
                                                (.+?),  # the actual file name
Packit c0c648
                                                \s+ [\d,.]+ \s bytes,
Packit c0c648
                                                \s+ [\d,.]+ \s tape \s blocks
Packit c0c648
                                            |x ? $1 : $_);
Packit c0c648
Packit c0c648
                        ### only STDOUT, see above. Sometimes, extra whitespace
Packit c0c648
                        ### is present, so make sure we only pick lines with
Packit c0c648
                        ### a length
Packit c0c648
                        } grep { length } map { split $/, $_ } join '', @{$out[3]};
Packit c0c648
Packit c0c648
                ### store the files that are in the archive ###
Packit c0c648
                $self->files(\@files);
Packit c0c648
            }
Packit c0c648
        }
Packit c0c648
Packit c0c648
        ### now actually extract it ###
Packit c0c648
        {   my $cmd =
Packit c0c648
                $self->is_tgz ? [$self->bin_gzip, '-c', '-d', '-f', $archive, '|',
Packit c0c648
                                 $self->bin_tar, '-xf', '-'] :
Packit c0c648
                $self->is_tbz ? [$self->bin_bunzip2, '-cd', $archive, '|',
Packit c0c648
                                 $self->bin_tar, '-xf', '-'] :
Packit c0c648
                $self->is_txz ? [$self->bin_unxz, '-cd', $archive, '|',
Packit c0c648
                                 $self->bin_tar, '-xf', '-'] :
Packit c0c648
                [$self->bin_tar, @ExtraTarFlags, '-xf', $archive];
Packit c0c648
Packit c0c648
            my $buffer = '';
Packit c0c648
            unless( scalar run( command => $cmd,
Packit c0c648
                                buffer  => \$buffer,
Packit c0c648
                                verbose => $DEBUG )
Packit c0c648
            ) {
Packit c0c648
                return $self->_error(loc("Error extracting archive '%1': %2",
Packit c0c648
                                $archive, $buffer ));
Packit c0c648
            }
Packit c0c648
Packit c0c648
            ### we might not have them, due to lack of buffers
Packit c0c648
            if( $self->files ) {
Packit c0c648
                ### now that we've extracted, figure out where we extracted to
Packit c0c648
                my $dir = $self->__get_extract_dir( $self->files );
Packit c0c648
Packit c0c648
                ### store the extraction dir ###
Packit c0c648
                $self->extract_path( $dir );
Packit c0c648
            }
Packit c0c648
        }
Packit c0c648
Packit c0c648
        ### we got here, no error happened
Packit c0c648
        return 1;
Packit c0c648
    }
Packit c0c648
}
Packit c0c648
Packit c0c648
Packit c0c648
### use archive::tar to extract ###
Packit c0c648
sub _untar_at {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### Loading Archive::Tar is going to set it to 1, so make it local
Packit c0c648
    ### within this block, starting with its initial value. Whatever
Packit c0c648
    ### Achive::Tar does will be undone when we return.
Packit c0c648
    ###
Packit c0c648
    ### Also, later, set $Archive::Tar::WARN to $Archive::Extract::WARN
Packit c0c648
    ### so users don't have to even think about this variable. If they
Packit c0c648
    ### do, they still get their set value outside of this call.
Packit c0c648
    local $Archive::Tar::WARN = $Archive::Tar::WARN;
Packit c0c648
Packit c0c648
    ### we definitely need Archive::Tar, so load that first
Packit c0c648
    {   my $use_list = { 'Archive::Tar' => '0.0' };
Packit c0c648
Packit c0c648
        unless( can_load( modules => $use_list ) ) {
Packit c0c648
Packit c0c648
            $self->_error(loc("You do not have '%1' installed - " .
Packit c0c648
                              "Please install it as soon as possible.",
Packit c0c648
                              'Archive::Tar'));
Packit c0c648
Packit c0c648
            return METHOD_NA;
Packit c0c648
        }
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### we might pass it a filehandle if it's a .tbz file..
Packit c0c648
    my $fh_to_read = $self->archive;
Packit c0c648
Packit c0c648
    ### we will need Compress::Zlib too, if it's a tgz... and IO::Zlib
Packit c0c648
    ### if A::T's version is 0.99 or higher
Packit c0c648
    if( $self->is_tgz ) {
Packit c0c648
        my $use_list = { 'Compress::Zlib' => '0.0' };
Packit c0c648
           $use_list->{ 'IO::Zlib' } = '0.0'
Packit c0c648
                if $Archive::Tar::VERSION >= '0.99';
Packit c0c648
Packit c0c648
        unless( can_load( modules => $use_list ) ) {
Packit c0c648
            my $which = join '/', sort keys %$use_list;
Packit c0c648
Packit c0c648
            $self->_error(loc(
Packit c0c648
                "You do not have '%1' installed - Please ".
Packit c0c648
                "install it as soon as possible.", $which)
Packit c0c648
            );
Packit c0c648
Packit c0c648
            return METHOD_NA;
Packit c0c648
        }
Packit c0c648
Packit c0c648
    } elsif ( $self->is_tbz ) {
Packit c0c648
        my $use_list = { 'IO::Uncompress::Bunzip2' => '0.0' };
Packit c0c648
        unless( can_load( modules => $use_list ) ) {
Packit c0c648
            $self->_error(loc(
Packit c0c648
                "You do not have '%1' installed - Please " .
Packit c0c648
                "install it as soon as possible.",
Packit c0c648
                'IO::Uncompress::Bunzip2')
Packit c0c648
            );
Packit c0c648
Packit c0c648
            return METHOD_NA;
Packit c0c648
        }
Packit c0c648
Packit c0c648
        my $bz = IO::Uncompress::Bunzip2->new( $self->archive ) or
Packit c0c648
            return $self->_error(loc("Unable to open '%1': %2",
Packit c0c648
                            $self->archive,
Packit c0c648
                            $IO::Uncompress::Bunzip2::Bunzip2Error));
Packit c0c648
Packit c0c648
        $fh_to_read = $bz;
Packit c0c648
    } elsif ( $self->is_txz ) {
Packit c0c648
        my $use_list = { 'IO::Uncompress::UnXz' => '0.0' };
Packit c0c648
        unless( can_load( modules => $use_list ) ) {
Packit c0c648
            $self->_error(loc(
Packit c0c648
                "You do not have '%1' installed - Please " .
Packit c0c648
                "install it as soon as possible.",
Packit c0c648
                'IO::Uncompress::UnXz')
Packit c0c648
            );
Packit c0c648
Packit c0c648
            return METHOD_NA;
Packit c0c648
        }
Packit c0c648
Packit c0c648
        my $xz = IO::Uncompress::UnXz->new( $self->archive ) or
Packit c0c648
            return $self->_error(loc("Unable to open '%1': %2",
Packit c0c648
                            $self->archive,
Packit c0c648
                            $IO::Uncompress::UnXz::UnXzError));
Packit c0c648
Packit c0c648
        $fh_to_read = $xz;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my @files;
Packit c0c648
    {
Packit c0c648
        ### $Archive::Tar::WARN is 1 by default in Archive::Tar, but we've
Packit c0c648
        ### localized $Archive::Tar::WARN already.
Packit c0c648
        $Archive::Tar::WARN = $Archive::Extract::WARN;
Packit c0c648
Packit c0c648
        ### only tell it it's compressed if it's a .tgz, as we give it a file
Packit c0c648
        ### handle if it's a .tbz
Packit c0c648
        my @read = ( $fh_to_read, ( $self->is_tgz ? 1 : 0 ) );
Packit c0c648
Packit c0c648
        ### for version of Archive::Tar > 1.04
Packit c0c648
        local $Archive::Tar::CHOWN = 0;
Packit c0c648
Packit c0c648
        ### use the iterator if we can. it's a feature of A::T 1.40 and up
Packit c0c648
        if ( $_ALLOW_TAR_ITER && Archive::Tar->can( 'iter' ) ) {
Packit c0c648
Packit c0c648
            my $next;
Packit c0c648
            unless ( $next = Archive::Tar->iter( @read ) ) {
Packit c0c648
                return $self->_error(loc(
Packit c0c648
                            "Unable to read '%1': %2", $self->archive,
Packit c0c648
                            $Archive::Tar::error));
Packit c0c648
            }
Packit c0c648
Packit c0c648
            while ( my $file = $next->() ) {
Packit c0c648
                push @files, $file->full_path;
Packit c0c648
Packit c0c648
                $file->extract or return $self->_error(loc(
Packit c0c648
                        "Unable to read '%1': %2",
Packit c0c648
                        $self->archive,
Packit c0c648
                        $Archive::Tar::error));
Packit c0c648
            }
Packit c0c648
Packit c0c648
        ### older version, read the archive into memory
Packit c0c648
        } else {
Packit c0c648
Packit c0c648
            my $tar = Archive::Tar->new();
Packit c0c648
Packit c0c648
            unless( $tar->read( @read ) ) {
Packit c0c648
                return $self->_error(loc("Unable to read '%1': %2",
Packit c0c648
                            $self->archive, $Archive::Tar::error));
Packit c0c648
            }
Packit c0c648
Packit c0c648
            ### workaround to prevent Archive::Tar from setting uid, which
Packit c0c648
            ### is a potential security hole. -autrijus
Packit c0c648
            ### have to do it here, since A::T needs to be /loaded/ first ###
Packit c0c648
            {   no strict 'refs'; local $^W;
Packit c0c648
Packit c0c648
                ### older versions of archive::tar <= 0.23
Packit c0c648
                *Archive::Tar::chown = sub {};
Packit c0c648
            }
Packit c0c648
Packit c0c648
            {   local $^W;  # quell 'splice() offset past end of array' warnings
Packit c0c648
                            # on older versions of A::T
Packit c0c648
Packit c0c648
                ### older archive::tar always returns $self, return value
Packit c0c648
                ### slightly fux0r3d because of it.
Packit c0c648
                $tar->extract or return $self->_error(loc(
Packit c0c648
                        "Unable to extract '%1': %2",
Packit c0c648
                        $self->archive, $Archive::Tar::error ));
Packit c0c648
            }
Packit c0c648
Packit c0c648
            @files = $tar->list_files;
Packit c0c648
        }
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $dir = $self->__get_extract_dir( \@files );
Packit c0c648
Packit c0c648
    ### store the files that are in the archive ###
Packit c0c648
    $self->files(\@files);
Packit c0c648
Packit c0c648
    ### store the extraction dir ###
Packit c0c648
    $self->extract_path( $dir );
Packit c0c648
Packit c0c648
    ### check if the dir actually appeared ###
Packit c0c648
    return 1 if -d $self->extract_path;
Packit c0c648
Packit c0c648
    ### no dir, we failed ###
Packit c0c648
    return $self->_error(loc("Unable to extract '%1': %2",
Packit c0c648
                                $self->archive, $Archive::Tar::error ));
Packit c0c648
}
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# Gunzip code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
sub _gunzip_bin {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### check for /bin/gzip -- we need it ###
Packit c0c648
    unless( $self->bin_gzip ) {
Packit c0c648
        $self->_error(loc("No '%1' program found", '/bin/gzip'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
        return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
                            $self->_gunzip_to, $! ));
Packit c0c648
Packit c0c648
    my $cmd = [ $self->bin_gzip, '-c', '-d', '-f', $self->archive ];
Packit c0c648
Packit c0c648
    my $buffer;
Packit c0c648
    unless( scalar run( command => $cmd,
Packit c0c648
                        verbose => $DEBUG,
Packit c0c648
                        buffer  => \$buffer )
Packit c0c648
    ) {
Packit c0c648
        return $self->_error(loc("Unable to gunzip '%1': %2",
Packit c0c648
                                    $self->archive, $buffer));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### no buffers available?
Packit c0c648
    if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
Packit c0c648
        $self->_error( $self->_no_buffer_content( $self->archive ) );
Packit c0c648
    }
Packit c0c648
Packit c0c648
    $self->_print($fh, $buffer) if defined $buffer;
Packit c0c648
Packit c0c648
    close $fh;
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
sub _gunzip_cz {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    my $use_list = { 'Compress::Zlib' => '0.0' };
Packit c0c648
    unless( can_load( modules => $use_list ) ) {
Packit c0c648
        $self->_error(loc("You do not have '%1' installed - Please " .
Packit c0c648
                    "install it as soon as possible.", 'Compress::Zlib'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $gz = Compress::Zlib::gzopen( $self->archive, "rb" ) or
Packit c0c648
                return $self->_error(loc("Unable to open '%1': %2",
Packit c0c648
                            $self->archive, $Compress::Zlib::gzerrno));
Packit c0c648
Packit c0c648
    my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
        return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
                            $self->_gunzip_to, $! ));
Packit c0c648
Packit c0c648
    my $buffer;
Packit c0c648
    $self->_print($fh, $buffer) while $gz->gzread($buffer) > 0;
Packit c0c648
    $fh->close;
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# Uncompress code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
sub _uncompress_bin {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### check for /bin/gzip -- we need it ###
Packit c0c648
    unless( $self->bin_uncompress ) {
Packit c0c648
        $self->_error(loc("No '%1' program found", '/bin/uncompress'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
        return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
                            $self->_gunzip_to, $! ));
Packit c0c648
Packit c0c648
    my $cmd = [ $self->bin_uncompress, '-c', $self->archive ];
Packit c0c648
Packit c0c648
    my $buffer;
Packit c0c648
    unless( scalar run( command => $cmd,
Packit c0c648
                        verbose => $DEBUG,
Packit c0c648
                        buffer  => \$buffer )
Packit c0c648
    ) {
Packit c0c648
        return $self->_error(loc("Unable to uncompress '%1': %2",
Packit c0c648
                                    $self->archive, $buffer));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### no buffers available?
Packit c0c648
    if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
Packit c0c648
        $self->_error( $self->_no_buffer_content( $self->archive ) );
Packit c0c648
    }
Packit c0c648
Packit c0c648
    $self->_print($fh, $buffer) if defined $buffer;
Packit c0c648
Packit c0c648
    close $fh;
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# Unzip code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
Packit c0c648
sub _unzip_bin {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### check for /bin/gzip if we need it ###
Packit c0c648
    unless( $self->bin_unzip ) {
Packit c0c648
        $self->_error(loc("No '%1' program found", '/bin/unzip'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### first, get the files.. it must be 2 different commands with 'unzip' :(
Packit c0c648
    {   ### on VMS, capital letter options have to be quoted. This is
Packit c0c648
        ### reported by John Malmberg on P5P Tue 21 Aug 2007 05:05:11
Packit c0c648
        ### Subject: [patch@31735]Archive Extract fix on VMS.
Packit c0c648
        my $opt = ON_VMS ? '"-Z"' : '-Z';
Packit c0c648
        my $cmd = [ $self->bin_unzip, $opt, '-1', $self->archive ];
Packit c0c648
Packit c0c648
        my $buffer = '';
Packit c0c648
        unless( scalar run( command => $cmd,
Packit c0c648
                            verbose => $DEBUG,
Packit c0c648
                            buffer  => \$buffer )
Packit c0c648
        ) {
Packit c0c648
            return $self->_error(loc("Unable to unzip '%1': %2",
Packit c0c648
                                        $self->archive, $buffer));
Packit c0c648
        }
Packit c0c648
Packit c0c648
        ### no buffers available?
Packit c0c648
        if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
Packit c0c648
            $self->_error( $self->_no_buffer_files( $self->archive ) );
Packit c0c648
Packit c0c648
        } else {
Packit c0c648
            ### Annoyingly, pesky MSWin32 can either have 'native' tools
Packit c0c648
            ### which have \r\n line endings or Cygwin-based tools which
Packit c0c648
            ### have \n line endings. Jan Dubois suggested using this fix
Packit c0c648
            my $split = ON_WIN32 ? qr/\r?\n/ : "\n";
Packit c0c648
            $self->files( [split $split, $buffer] );
Packit c0c648
        }
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### now, extract the archive ###
Packit c0c648
    {   my $cmd = [ $self->bin_unzip, '-qq', '-o', $self->archive ];
Packit c0c648
Packit c0c648
        my $buffer;
Packit c0c648
        unless( scalar run( command => $cmd,
Packit c0c648
                            verbose => $DEBUG,
Packit c0c648
                            buffer  => \$buffer )
Packit c0c648
        ) {
Packit c0c648
            return $self->_error(loc("Unable to unzip '%1': %2",
Packit c0c648
                                        $self->archive, $buffer));
Packit c0c648
        }
Packit c0c648
Packit c0c648
        if( scalar @{$self->files} ) {
Packit c0c648
            my $files   = $self->files;
Packit c0c648
            my $dir     = $self->__get_extract_dir( $files );
Packit c0c648
Packit c0c648
            $self->extract_path( $dir );
Packit c0c648
        }
Packit c0c648
    }
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
sub _unzip_az {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    my $use_list = { 'Archive::Zip' => '0.0' };
Packit c0c648
    unless( can_load( modules => $use_list ) ) {
Packit c0c648
        $self->_error(loc("You do not have '%1' installed - Please " .
Packit c0c648
                      "install it as soon as possible.", 'Archive::Zip'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $zip = Archive::Zip->new();
Packit c0c648
Packit c0c648
    unless( $zip->read( $self->archive ) == &Archive::Zip::AZ_OK ) {
Packit c0c648
        return $self->_error(loc("Unable to read '%1'", $self->archive));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my @files;
Packit c0c648
Packit c0c648
Packit c0c648
    ### Address: #43278: Explicitly tell Archive::Zip where to put the files:
Packit c0c648
    ### "In my BackPAN indexing, Archive::Zip was extracting things
Packit c0c648
    ### in my script's directory instead of the current working directory.
Packit c0c648
    ### I traced this back through Archive::Zip::_asLocalName which
Packit c0c648
    ### eventually calls File::Spec::Win32::rel2abs which on Windows might
Packit c0c648
    ### call Cwd::getdcwd. getdcwd returns the wrong directory in my
Packit c0c648
    ### case, even though I think I'm on the same drive.
Packit c0c648
    ###
Packit c0c648
    ### To fix this, I pass the optional second argument to
Packit c0c648
    ### extractMember using the cwd from Archive::Extract." --bdfoy
Packit c0c648
Packit c0c648
    ## store cwd() before looping; calls to cwd() can be expensive, and
Packit c0c648
    ### it won't change during the loop
Packit c0c648
    my $extract_dir = cwd();
Packit c0c648
Packit c0c648
    ### have to extract every member individually ###
Packit c0c648
    for my $member ($zip->members) {
Packit c0c648
        push @files, $member->{fileName};
Packit c0c648
Packit c0c648
        ### file to extract to, to avoid the above problem
Packit c0c648
        my $to = File::Spec->catfile( $extract_dir, $member->{fileName} );
Packit c0c648
Packit c0c648
        unless( $zip->extractMember($member, $to) == &Archive::Zip::AZ_OK ) {
Packit c0c648
            return $self->_error(loc("Extraction of '%1' from '%2' failed",
Packit c0c648
                        $member->{fileName}, $self->archive ));
Packit c0c648
        }
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $dir = $self->__get_extract_dir( \@files );
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( \@files );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs($dir) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
sub __get_extract_dir {
Packit c0c648
    my $self    = shift;
Packit c0c648
    my $files   = shift || [];
Packit c0c648
Packit c0c648
    return unless scalar @$files;
Packit c0c648
Packit c0c648
    my($dir1, $dir2);
Packit c0c648
    for my $aref ( [ \$dir1, 0 ], [ \$dir2, -1 ] ) {
Packit c0c648
        my($dir,$pos) = @$aref;
Packit c0c648
Packit c0c648
        ### add a catdir(), so that any trailing slashes get
Packit c0c648
        ### take care of (removed)
Packit c0c648
        ### also, a catdir() normalises './dir/foo' to 'dir/foo';
Packit c0c648
        ### which was the problem in bug #23999
Packit c0c648
        my $res = -d $files->[$pos]
Packit c0c648
                    ? File::Spec->catdir( $files->[$pos], '' )
Packit c0c648
                    : File::Spec->catdir( dirname( $files->[$pos] ) );
Packit c0c648
Packit c0c648
        $$dir = $res;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### if the first and last dir don't match, make sure the
Packit c0c648
    ### dirname is not set wrongly
Packit c0c648
    my $dir;
Packit c0c648
Packit c0c648
    ### dirs are the same, so we know for sure what the extract dir is
Packit c0c648
    if( $dir1 eq $dir2 ) {
Packit c0c648
        $dir = $dir1;
Packit c0c648
Packit c0c648
    ### dirs are different.. do they share the base dir?
Packit c0c648
    ### if so, use that, if not, fall back to '.'
Packit c0c648
    } else {
Packit c0c648
        my $base1 = [ File::Spec->splitdir( $dir1 ) ]->[0];
Packit c0c648
        my $base2 = [ File::Spec->splitdir( $dir2 ) ]->[0];
Packit c0c648
Packit c0c648
        $dir = File::Spec->rel2abs( $base1 eq $base2 ? $base1 : '.' );
Packit c0c648
    }
Packit c0c648
Packit c0c648
    return File::Spec->rel2abs( $dir );
Packit c0c648
}
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# Bunzip2 code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
sub _bunzip2_bin {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### check for /bin/gzip -- we need it ###
Packit c0c648
    unless( $self->bin_bunzip2 ) {
Packit c0c648
        $self->_error(loc("No '%1' program found", '/bin/bunzip2'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
        return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
                            $self->_gunzip_to, $! ));
Packit c0c648
Packit c0c648
    ### guard against broken bunzip2. See ->have_old_bunzip2()
Packit c0c648
    ### for details
Packit c0c648
    if( $self->have_old_bunzip2 and $self->archive !~ /\.bz2$/i ) {
Packit c0c648
        return $self->_error(loc("Your bunzip2 version is too old and ".
Packit c0c648
                                 "can only extract files ending in '%1'",
Packit c0c648
                                 '.bz2'));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $cmd = [ $self->bin_bunzip2, '-cd', $self->archive ];
Packit c0c648
Packit c0c648
    my $buffer;
Packit c0c648
    unless( scalar run( command => $cmd,
Packit c0c648
                        verbose => $DEBUG,
Packit c0c648
                        buffer  => \$buffer )
Packit c0c648
    ) {
Packit c0c648
        return $self->_error(loc("Unable to bunzip2 '%1': %2",
Packit c0c648
                                    $self->archive, $buffer));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### no buffers available?
Packit c0c648
    if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
Packit c0c648
        $self->_error( $self->_no_buffer_content( $self->archive ) );
Packit c0c648
    }
Packit c0c648
Packit c0c648
    $self->_print($fh, $buffer) if defined $buffer;
Packit c0c648
Packit c0c648
    close $fh;
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
### using cz2, the compact versions... this we use mainly in archive::tar
Packit c0c648
### extractor..
Packit c0c648
# sub _bunzip2_cz1 {
Packit c0c648
#     my $self = shift;
Packit c0c648
#
Packit c0c648
#     my $use_list = { 'IO::Uncompress::Bunzip2' => '0.0' };
Packit c0c648
#     unless( can_load( modules => $use_list ) ) {
Packit c0c648
#         return $self->_error(loc("You do not have '%1' installed - Please " .
Packit c0c648
#                         "install it as soon as possible.",
Packit c0c648
#                         'IO::Uncompress::Bunzip2'));
Packit c0c648
#     }
Packit c0c648
#
Packit c0c648
#     my $bz = IO::Uncompress::Bunzip2->new( $self->archive ) or
Packit c0c648
#                 return $self->_error(loc("Unable to open '%1': %2",
Packit c0c648
#                             $self->archive,
Packit c0c648
#                             $IO::Uncompress::Bunzip2::Bunzip2Error));
Packit c0c648
#
Packit c0c648
#     my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
#         return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
#                             $self->_gunzip_to, $! ));
Packit c0c648
#
Packit c0c648
#     my $buffer;
Packit c0c648
#     $fh->print($buffer) while $bz->read($buffer) > 0;
Packit c0c648
#     $fh->close;
Packit c0c648
#
Packit c0c648
#     ### set what files where extract, and where they went ###
Packit c0c648
#     $self->files( [$self->_gunzip_to] );
Packit c0c648
#     $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
#
Packit c0c648
#     return 1;
Packit c0c648
# }
Packit c0c648
Packit c0c648
sub _bunzip2_bz2 {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    my $use_list = { 'IO::Uncompress::Bunzip2' => '0.0' };
Packit c0c648
    unless( can_load( modules => $use_list ) ) {
Packit c0c648
        $self->_error(loc("You do not have '%1' installed - Please " .
Packit c0c648
                          "install it as soon as possible.",
Packit c0c648
                          'IO::Uncompress::Bunzip2'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    IO::Uncompress::Bunzip2::bunzip2($self->archive => $self->_gunzip_to)
Packit c0c648
        or return $self->_error(loc("Unable to uncompress '%1': %2",
Packit c0c648
                            $self->archive,
Packit c0c648
                            $IO::Uncompress::Bunzip2::Bunzip2Error));
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# UnXz code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
sub _unxz_bin {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### check for /bin/unxz -- we need it ###
Packit c0c648
    unless( $self->bin_unxz ) {
Packit c0c648
        $self->_error(loc("No '%1' program found", '/bin/unxz'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
        return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
                            $self->_gunzip_to, $! ));
Packit c0c648
Packit c0c648
    my $cmd = [ $self->bin_unxz, '-c', '-d', '-f', $self->archive ];
Packit c0c648
Packit c0c648
    my $buffer;
Packit c0c648
    unless( scalar run( command => $cmd,
Packit c0c648
                        verbose => $DEBUG,
Packit c0c648
                        buffer  => \$buffer )
Packit c0c648
    ) {
Packit c0c648
        return $self->_error(loc("Unable to unxz '%1': %2",
Packit c0c648
                                    $self->archive, $buffer));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### no buffers available?
Packit c0c648
    if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
Packit c0c648
        $self->_error( $self->_no_buffer_content( $self->archive ) );
Packit c0c648
    }
Packit c0c648
Packit c0c648
    $self->_print($fh, $buffer) if defined $buffer;
Packit c0c648
Packit c0c648
    close $fh;
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
sub _unxz_cz {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    my $use_list = { 'IO::Uncompress::UnXz' => '0.0' };
Packit c0c648
    unless( can_load( modules => $use_list ) ) {
Packit c0c648
        $self->_error(loc("You do not have '%1' installed - Please " .
Packit c0c648
                          "install it as soon as possible.",
Packit c0c648
                          'IO::Uncompress::UnXz'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    IO::Uncompress::UnXz::unxz($self->archive => $self->_gunzip_to)
Packit c0c648
        or return $self->_error(loc("Unable to uncompress '%1': %2",
Packit c0c648
                            $self->archive,
Packit c0c648
                            $IO::Uncompress::UnXz::UnXzError));
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# unlzma code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
sub _unlzma_bin {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### check for /bin/unlzma -- we need it ###
Packit c0c648
    unless( $self->bin_unlzma ) {
Packit c0c648
        $self->_error(loc("No '%1' program found", '/bin/unlzma'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
        return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
                            $self->_gunzip_to, $! ));
Packit c0c648
Packit c0c648
    my $cmd = [ $self->bin_unlzma, '-c', $self->archive ];
Packit c0c648
Packit c0c648
    my $buffer;
Packit c0c648
    unless( scalar run( command => $cmd,
Packit c0c648
                        verbose => $DEBUG,
Packit c0c648
                        buffer  => \$buffer )
Packit c0c648
    ) {
Packit c0c648
        return $self->_error(loc("Unable to unlzma '%1': %2",
Packit c0c648
                                    $self->archive, $buffer));
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### no buffers available?
Packit c0c648
    if( !IPC::Cmd->can_capture_buffer and !$buffer ) {
Packit c0c648
        $self->_error( $self->_no_buffer_content( $self->archive ) );
Packit c0c648
    }
Packit c0c648
Packit c0c648
    $self->_print($fh, $buffer) if defined $buffer;
Packit c0c648
Packit c0c648
    close $fh;
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
sub _unlzma_cz {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    my $use_list1 = { 'IO::Uncompress::UnLzma' => '0.0' };
Packit c0c648
    my $use_list2 = { 'Compress::unLZMA' => '0.0' };
Packit c0c648
Packit c0c648
    if (can_load( modules => $use_list1 ) ) {
Packit c0c648
        IO::Uncompress::UnLzma::unlzma($self->archive => $self->_gunzip_to)
Packit c0c648
            or return $self->_error(loc("Unable to uncompress '%1': %2",
Packit c0c648
                                $self->archive,
Packit c0c648
                                $IO::Uncompress::UnLzma::UnLzmaError));
Packit c0c648
    }
Packit c0c648
    elsif (can_load( modules => $use_list2 ) ) {
Packit c0c648
Packit c0c648
        my $fh = FileHandle->new('>'. $self->_gunzip_to) or
Packit c0c648
            return $self->_error(loc("Could not open '%1' for writing: %2",
Packit c0c648
                                $self->_gunzip_to, $! ));
Packit c0c648
Packit c0c648
        my $buffer;
Packit c0c648
        $buffer = Compress::unLZMA::uncompressfile( $self->archive );
Packit c0c648
        unless ( defined $buffer ) {
Packit c0c648
            return $self->_error(loc("Could not unlzma '%1': %2",
Packit c0c648
                                        $self->archive, $@));
Packit c0c648
        }
Packit c0c648
Packit c0c648
        $self->_print($fh, $buffer) if defined $buffer;
Packit c0c648
Packit c0c648
        close $fh;
Packit c0c648
    }
Packit c0c648
    else {
Packit c0c648
        $self->_error(loc("You do not have '%1' or '%2' installed - Please " .
Packit c0c648
                    "install it as soon as possible.", 'Compress::unLZMA', 'IO::Uncompress::UnLzma'));
Packit c0c648
        return METHOD_NA;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    ### set what files where extract, and where they went ###
Packit c0c648
    $self->files( [$self->_gunzip_to] );
Packit c0c648
    $self->extract_path( File::Spec->rel2abs(cwd()) );
Packit c0c648
Packit c0c648
    return 1;
Packit c0c648
}
Packit c0c648
Packit c0c648
#####################################
Packit c0c648
#
Packit c0c648
# unzip heuristics for FreeBSD-alikes
Packit c0c648
#
Packit c0c648
#####################################
Packit c0c648
Packit c0c648
sub _is_infozip_esque {
Packit c0c648
  my $unzip = shift;
Packit c0c648
Packit c0c648
  my @strings;
Packit c0c648
  my $buf = '';
Packit c0c648
Packit c0c648
  {
Packit c0c648
    open my $file, '<', $unzip or die "$!\n";
Packit c0c648
    binmode $file;
Packit c0c648
    local $/ = \1;
Packit c0c648
    local $_;
Packit c0c648
    while(<$file>) {
Packit c0c648
      if ( m![[:print:]]! ) {
Packit c0c648
        $buf .= $_;
Packit c0c648
        next;
Packit c0c648
      }
Packit c0c648
      if ( $buf and m![^[:print:]]! ) {
Packit c0c648
        push @strings, $buf if length $buf >= 4;
Packit c0c648
        $buf = '';
Packit c0c648
        next;
Packit c0c648
      }
Packit c0c648
    }
Packit c0c648
  }
Packit c0c648
  push @strings, $buf if $buf;
Packit c0c648
  foreach my $part ( @strings ) {
Packit c0c648
    if ( $part =~ m!ZIPINFO! or $part =~ m!usage:.+?Z1! ) {
Packit c0c648
      return $unzip;
Packit c0c648
    }
Packit c0c648
  }
Packit c0c648
  return;
Packit c0c648
}
Packit c0c648
Packit c0c648
#################################
Packit c0c648
#
Packit c0c648
# Error code
Packit c0c648
#
Packit c0c648
#################################
Packit c0c648
Packit c0c648
# For printing binaries that avoids interfering globals
Packit c0c648
sub _print {
Packit c0c648
    my $self = shift;
Packit c0c648
    my $fh = shift;
Packit c0c648
Packit c0c648
    local( $\, $", $, ) = ( undef, ' ', '' );
Packit c0c648
    return print $fh @_;
Packit c0c648
}
Packit c0c648
Packit c0c648
sub _error {
Packit c0c648
    my $self    = shift;
Packit c0c648
    my $error   = shift;
Packit c0c648
    my $lerror  = Carp::longmess($error);
Packit c0c648
Packit c0c648
    push @{$self->_error_msg},      $error;
Packit c0c648
    push @{$self->_error_msg_long}, $lerror;
Packit c0c648
Packit c0c648
    ### set $Archive::Extract::WARN to 0 to disable printing
Packit c0c648
    ### of errors
Packit c0c648
    if( $WARN ) {
Packit c0c648
        carp $DEBUG ? $lerror : $error;
Packit c0c648
    }
Packit c0c648
Packit c0c648
    return;
Packit c0c648
}
Packit c0c648
Packit c0c648
sub error {
Packit c0c648
    my $self = shift;
Packit c0c648
Packit c0c648
    ### make sure we have a fallback aref
Packit c0c648
    my $aref = do {
Packit c0c648
        shift()
Packit c0c648
            ? $self->_error_msg_long
Packit c0c648
            : $self->_error_msg
Packit c0c648
    } || [];
Packit c0c648
Packit c0c648
    return join $/, @$aref;
Packit c0c648
}
Packit c0c648
Packit c0c648
=head2 debug( MESSAGE )
Packit c0c648
Packit c0c648
This method outputs MESSAGE to the default filehandle if C<$DEBUG> is
Packit c0c648
true. It's a small method, but it's here if you'd like to subclass it
Packit c0c648
so you can so something else with any debugging output.
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
### this is really a stub for subclassing
Packit c0c648
sub debug {
Packit c0c648
    return unless $DEBUG;
Packit c0c648
Packit c0c648
    print $_[1];
Packit c0c648
}
Packit c0c648
Packit c0c648
sub _no_buffer_files {
Packit c0c648
    my $self = shift;
Packit c0c648
    my $file = shift or return;
Packit c0c648
    return loc("No buffer captured, unable to tell ".
Packit c0c648
               "extracted files or extraction dir for '%1'", $file);
Packit c0c648
}
Packit c0c648
Packit c0c648
sub _no_buffer_content {
Packit c0c648
    my $self = shift;
Packit c0c648
    my $file = shift or return;
Packit c0c648
    return loc("No buffer captured, unable to get content for '%1'", $file);
Packit c0c648
}
Packit c0c648
1;
Packit c0c648
Packit c0c648
=pod
Packit c0c648
Packit c0c648
=head1 HOW IT WORKS
Packit c0c648
Packit c0c648
C<Archive::Extract> tries first to determine what type of archive you
Packit c0c648
are passing it, by inspecting its suffix. It does not do this by using
Packit c0c648
Mime magic, or something related. See C<CAVEATS> below.
Packit c0c648
Packit c0c648
Once it has determined the file type, it knows which extraction methods
Packit c0c648
it can use on the archive. It will try a perl solution first, then fall
Packit c0c648
back to a commandline tool if that fails. If that also fails, it will
Packit c0c648
return false, indicating it was unable to extract the archive.
Packit c0c648
See the section on C<GLOBAL VARIABLES> to see how to alter this order.
Packit c0c648
Packit c0c648
=head1 CAVEATS
Packit c0c648
Packit c0c648
=head2 File Extensions
Packit c0c648
Packit c0c648
C<Archive::Extract> trusts on the extension of the archive to determine
Packit c0c648
what type it is, and what extractor methods therefore can be used. If
Packit c0c648
your archives do not have any of the extensions as described in the
Packit c0c648
C<new()> method, you will have to specify the type explicitly, or
Packit c0c648
C<Archive::Extract> will not be able to extract the archive for you.
Packit c0c648
Packit c0c648
=head2 Supporting Very Large Files
Packit c0c648
Packit c0c648
C<Archive::Extract> can use either pure perl modules or command line
Packit c0c648
programs under the hood. Some of the pure perl modules (like
Packit c0c648
C<Archive::Tar> and Compress::unLZMA) take the entire contents of the archive into memory,
Packit c0c648
which may not be feasible on your system. Consider setting the global
Packit c0c648
variable C<$Archive::Extract::PREFER_BIN> to C<1>, which will prefer
Packit c0c648
the use of command line programs and won't consume so much memory.
Packit c0c648
Packit c0c648
See the C<GLOBAL VARIABLES> section below for details.
Packit c0c648
Packit c0c648
=head2 Bunzip2 support of arbitrary extensions.
Packit c0c648
Packit c0c648
Older versions of C</bin/bunzip2> do not support arbitrary file
Packit c0c648
extensions and insist on a C<.bz2> suffix. Although we do our best
Packit c0c648
to guard against this, if you experience a bunzip2 error, it may
Packit c0c648
be related to this. For details, please see the C<have_old_bunzip2>
Packit c0c648
method.
Packit c0c648
Packit c0c648
=head1 GLOBAL VARIABLES
Packit c0c648
Packit c0c648
=head2 $Archive::Extract::DEBUG
Packit c0c648
Packit c0c648
Set this variable to C<true> to have all calls to command line tools
Packit c0c648
be printed out, including all their output.
Packit c0c648
This also enables C<Carp::longmess> errors, instead of the regular
Packit c0c648
C<carp> errors.
Packit c0c648
Packit c0c648
Good for tracking down why things don't work with your particular
Packit c0c648
setup.
Packit c0c648
Packit c0c648
Defaults to C<false>.
Packit c0c648
Packit c0c648
=head2 $Archive::Extract::WARN
Packit c0c648
Packit c0c648
This variable controls whether errors encountered internally by
Packit c0c648
C<Archive::Extract> should be C<carp>'d or not.
Packit c0c648
Packit c0c648
Set to false to silence warnings. Inspect the output of the C<error()>
Packit c0c648
method manually to see what went wrong.
Packit c0c648
Packit c0c648
Defaults to C<true>.
Packit c0c648
Packit c0c648
=head2 $Archive::Extract::PREFER_BIN
Packit c0c648
Packit c0c648
This variables controls whether C<Archive::Extract> should prefer the
Packit c0c648
use of perl modules, or commandline tools to extract archives.
Packit c0c648
Packit c0c648
Set to C<true> to have C<Archive::Extract> prefer commandline tools.
Packit c0c648
Packit c0c648
Defaults to C<false>.
Packit c0c648
Packit c0c648
=head1 TODO / CAVEATS
Packit c0c648
Packit c0c648
=over 4
Packit c0c648
Packit c0c648
=item Mime magic support
Packit c0c648
Packit c0c648
Maybe this module should use something like C<File::Type> to determine
Packit c0c648
the type, rather than blindly trust the suffix.
Packit c0c648
Packit c0c648
=item Thread safety
Packit c0c648
Packit c0c648
Currently, C<Archive::Extract> does a C<chdir> to the extraction dir before
Packit c0c648
extraction, and a C<chdir> back again after. This is not necessarily
Packit c0c648
thread safe. See C<rt.cpan.org> bug C<#45671> for details.
Packit c0c648
Packit c0c648
=back
Packit c0c648
Packit c0c648
=head1 BUG REPORTS
Packit c0c648
Packit c0c648
Please report bugs or other issues to E<lt>bug-archive-extract@rt.cpan.orgE<gt>.
Packit c0c648
Packit c0c648
=head1 AUTHOR
Packit c0c648
Packit c0c648
This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
Packit c0c648
Packit c0c648
=head1 COPYRIGHT
Packit c0c648
Packit c0c648
This library is free software; you may redistribute and/or modify it
Packit c0c648
under the same terms as Perl itself.
Packit c0c648
Packit c0c648
=cut
Packit c0c648
Packit c0c648
# Local variables:
Packit c0c648
# c-indentation-style: bsd
Packit c0c648
# c-basic-offset: 4
Packit c0c648
# indent-tabs-mode: nil
Packit c0c648
# End:
Packit c0c648
# vim: expandtab shiftwidth=4:
Packit c0c648