<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*-
package CPAN::Tarzip;
use strict;
use vars qw($VERSION @ISA $BUGHUNTING);
use CPAN::Debug;
use File::Basename qw(basename);
$VERSION = "5.5012";
# module is internal to CPAN.pm

@ISA = qw(CPAN::Debug); ## no critic
$BUGHUNTING ||= 0; # released code must have turned off

# it's ok if file doesn't exist, it just matters if it is .gz or .bz2
sub new {
    my($class,$file) = @_;
    $CPAN::Frontend-&gt;mydie("CPAN::Tarzip-&gt;new called without arg") unless defined $file;
    my $me = { FILE =&gt; $file };
    if ($file =~ /\.(bz2|gz|zip|tbz|tgz)$/i) {
        $me-&gt;{ISCOMPRESSED} = 1;
    } else {
        $me-&gt;{ISCOMPRESSED} = 0;
    }
    if (0) {
    } elsif ($file =~ /\.(?:bz2|tbz)$/i) {
        unless ($me-&gt;{UNGZIPPRG} = $CPAN::Config-&gt;{bzip2}) {
            my $bzip2 = _my_which("bzip2");
            if ($bzip2) {
                $me-&gt;{UNGZIPPRG} = $bzip2;
            } else {
                $CPAN::Frontend-&gt;mydie(qq{
CPAN.pm needs the external program bzip2 in order to handle '$file'.
Please install it now and run 'o conf init bzip2' from the
CPAN shell prompt to register it as external program.
});
            }
        }
    } else {
        $me-&gt;{UNGZIPPRG} = _my_which("gzip");
    }
    $me-&gt;{TARPRG} = _my_which("tar") || _my_which("gtar");
    bless $me, $class;
}

sub _my_which {
    my($what) = @_;
    if ($CPAN::Config-&gt;{$what}) {
        return $CPAN::Config-&gt;{$what};
    }
    if ($CPAN::META-&gt;has_inst("File::Which")) {
        return File::Which::which($what);
    }
    my @cand = MM-&gt;maybe_command($what);
    return $cand[0] if @cand;
    require File::Spec;
    my $component;
  PATH_COMPONENT: foreach $component (File::Spec-&gt;path()) {
        next unless defined($component) &amp;&amp; $component;
        my($abs) = File::Spec-&gt;catfile($component,$what);
        if (MM-&gt;maybe_command($abs)) {
            return $abs;
        }
    }
    return;
}

sub gzip {
    my($self,$read) = @_;
    my $write = $self-&gt;{FILE};
    if ($CPAN::META-&gt;has_inst("Compress::Zlib")) {
        my($buffer,$fhw);
        $fhw = FileHandle-&gt;new($read)
            or $CPAN::Frontend-&gt;mydie("Could not open $read: $!");
        my $cwd = `pwd`;
        my $gz = Compress::Zlib::gzopen($write, "wb")
            or $CPAN::Frontend-&gt;mydie("Cannot gzopen $write: $! (pwd is $cwd)\n");
        binmode($fhw);
        $gz-&gt;gzwrite($buffer)
            while read($fhw,$buffer,4096) &gt; 0 ;
        $gz-&gt;gzclose() ;
        $fhw-&gt;close;
        return 1;
    } else {
        my $command = CPAN::HandleConfig-&gt;safe_quote($self-&gt;{UNGZIPPRG});
        system(qq{$command -c "$read" &gt; "$write"})==0;
    }
}


sub gunzip {
    my($self,$write) = @_;
    my $read = $self-&gt;{FILE};
    if ($CPAN::META-&gt;has_inst("Compress::Zlib")) {
        my($buffer,$fhw);
        $fhw = FileHandle-&gt;new("&gt;$write")
            or $CPAN::Frontend-&gt;mydie("Could not open &gt;$write: $!");
        my $gz = Compress::Zlib::gzopen($read, "rb")
            or $CPAN::Frontend-&gt;mydie("Cannot gzopen $read: $!\n");
        binmode($fhw);
        $fhw-&gt;print($buffer)
            while $gz-&gt;gzread($buffer) &gt; 0 ;
        $CPAN::Frontend-&gt;mydie("Error reading from $read: $!\n")
            if $gz-&gt;gzerror != Compress::Zlib::Z_STREAM_END();
        $gz-&gt;gzclose() ;
        $fhw-&gt;close;
        return 1;
    } else {
        my $command = CPAN::HandleConfig-&gt;safe_quote($self-&gt;{UNGZIPPRG});
        system(qq{$command -d -c "$read" &gt; "$write"})==0;
    }
}


sub gtest {
    my($self) = @_;
    return $self-&gt;{GTEST} if exists $self-&gt;{GTEST};
    defined $self-&gt;{FILE} or $CPAN::Frontend-&gt;mydie("gtest called but no FILE specified");
    my $read = $self-&gt;{FILE};
    my $success;
    if ($read=~/\.(?:bz2|tbz)$/ &amp;&amp; $CPAN::META-&gt;has_inst("Compress::Bzip2")) {
        my($buffer,$len);
        $len = 0;
        my $gz = Compress::Bzip2::bzopen($read, "rb")
            or $CPAN::Frontend-&gt;mydie(sprintf("Cannot gzopen %s: %s\n",
                                              $read,
                                              $Compress::Bzip2::bzerrno));
        while ($gz-&gt;bzread($buffer) &gt; 0 ) {
            $len += length($buffer);
            $buffer = "";
        }
        my $err = $gz-&gt;bzerror;
        $success = ! $err || $err == Compress::Bzip2::BZ_STREAM_END();
        if ($len == -s $read) {
            $success = 0;
            CPAN-&gt;debug("hit an uncompressed file") if $CPAN::DEBUG;
        }
        $gz-&gt;gzclose();
        CPAN-&gt;debug("err[$err]success[$success]") if $CPAN::DEBUG;
    } elsif ( $read=~/\.(?:gz|tgz)$/ &amp;&amp; $CPAN::META-&gt;has_inst("Compress::Zlib") ) {
        # After I had reread the documentation in zlib.h, I discovered that
        # uncompressed files do not lead to an gzerror (anymore?).
        my($buffer,$len);
        $len = 0;
        my $gz = Compress::Zlib::gzopen($read, "rb")
            or $CPAN::Frontend-&gt;mydie(sprintf("Cannot gzopen %s: %s\n",
                                              $read,
                                              $Compress::Zlib::gzerrno));
        while ($gz-&gt;gzread($buffer) &gt; 0 ) {
            $len += length($buffer);
            $buffer = "";
        }
        my $err = $gz-&gt;gzerror;
        $success = ! $err || $err == Compress::Zlib::Z_STREAM_END();
        if ($len == -s $read) {
            $success = 0;
            CPAN-&gt;debug("hit an uncompressed file") if $CPAN::DEBUG;
        }
        $gz-&gt;gzclose();
        CPAN-&gt;debug("err[$err]success[$success]") if $CPAN::DEBUG;
    } elsif (!$self-&gt;{ISCOMPRESSED}) {
        $success = 0;
    } else {
        my $command = CPAN::HandleConfig-&gt;safe_quote($self-&gt;{UNGZIPPRG});
        $success = 0==system(qq{$command -qdt "$read"});
    }
    return $self-&gt;{GTEST} = $success;
}


sub TIEHANDLE {
    my($class,$file) = @_;
    my $ret;
    $class-&gt;debug("file[$file]");
    my $self = $class-&gt;new($file);
    if (0) {
    } elsif (!$self-&gt;gtest) {
        my $fh = FileHandle-&gt;new($file)
            or $CPAN::Frontend-&gt;mydie("Could not open file[$file]: $!");
        binmode $fh;
        $self-&gt;{FH} = $fh;
        $class-&gt;debug("via uncompressed FH");
    } elsif ($file =~ /\.(?:bz2|tbz)$/ &amp;&amp; $CPAN::META-&gt;has_inst("Compress::Bzip2")) {
        my $gz = Compress::Bzip2::bzopen($file,"rb") or
            $CPAN::Frontend-&gt;mydie("Could not bzopen $file");
        $self-&gt;{GZ} = $gz;
        $class-&gt;debug("via Compress::Bzip2");
    } elsif ($file =~/\.(?:gz|tgz)$/ &amp;&amp; $CPAN::META-&gt;has_inst("Compress::Zlib")) {
        my $gz = Compress::Zlib::gzopen($file,"rb") or
            $CPAN::Frontend-&gt;mydie("Could not gzopen $file");
        $self-&gt;{GZ} = $gz;
        $class-&gt;debug("via Compress::Zlib");
    } else {
        my $gzip = CPAN::HandleConfig-&gt;safe_quote($self-&gt;{UNGZIPPRG});
        my $pipe = "$gzip -d -c $file |";
        my $fh = FileHandle-&gt;new($pipe) or $CPAN::Frontend-&gt;mydie("Could not pipe[$pipe]: $!");
        binmode $fh;
        $self-&gt;{FH} = $fh;
        $class-&gt;debug("via external $gzip");
    }
    $self;
}


sub READLINE {
    my($self) = @_;
    if (exists $self-&gt;{GZ}) {
        my $gz = $self-&gt;{GZ};
        my($line,$bytesread);
        $bytesread = $gz-&gt;gzreadline($line);
        return undef if $bytesread &lt;= 0;
        return $line;
    } else {
        my $fh = $self-&gt;{FH};
        return scalar &lt;$fh&gt;;
    }
}


sub READ {
    my($self,$ref,$length,$offset) = @_;
    $CPAN::Frontend-&gt;mydie("read with offset not implemented") if defined $offset;
    if (exists $self-&gt;{GZ}) {
        my $gz = $self-&gt;{GZ};
        my $byteread = $gz-&gt;gzread($$ref,$length);# 30eaf79e8b446ef52464b5422da328a8
        return $byteread;
    } else {
        my $fh = $self-&gt;{FH};
        return read($fh,$$ref,$length);
    }
}


sub DESTROY {
    my($self) = @_;
    if (exists $self-&gt;{GZ}) {
        my $gz = $self-&gt;{GZ};
        $gz-&gt;gzclose() if defined $gz; # hard to say if it is allowed
                                       # to be undef ever. AK, 2000-09
    } else {
        my $fh = $self-&gt;{FH};
        $fh-&gt;close if defined $fh;
    }
    undef $self;
}

sub untar {
    my($self) = @_;
    my $file = $self-&gt;{FILE};
    my($prefer) = 0;

    my $exttar = $self-&gt;{TARPRG} || "";
    $exttar = "" if $exttar =~ /^\s+$/; # user refuses to use it
    my $extgzip = $self-&gt;{UNGZIPPRG} || "";
    $extgzip = "" if $extgzip =~ /^\s+$/; # user refuses to use it

    if (0) { # makes changing order easier
    } elsif ($BUGHUNTING) {
        $prefer=2;
    } elsif ($CPAN::Config-&gt;{prefer_external_tar}) {
        $prefer = 1;
    } elsif (
             $CPAN::META-&gt;has_usable("Archive::Tar")
             &amp;&amp;
             $CPAN::META-&gt;has_inst("Compress::Zlib") ) {
        my $prefer_external_tar = $CPAN::Config-&gt;{prefer_external_tar};
        unless (defined $prefer_external_tar) {
            if ($^O =~ /(MSWin32|solaris)/) {
                $prefer_external_tar = 0;
            } else {
                $prefer_external_tar = 1;
            }
        }
        $prefer = $prefer_external_tar ? 1 : 2;
    } elsif ($exttar &amp;&amp; $extgzip) {
        # no modules and not bz2
        $prefer = 1;
        # but solaris binary tar is a problem
        if ($^O eq 'solaris' &amp;&amp; qx($exttar --version 2&gt;/dev/null) !~ /gnu/i) {
            $CPAN::Frontend-&gt;mywarn(&lt;&lt; 'END_WARN');

WARNING: Many CPAN distributions were archived with GNU tar and some of
them may be incompatible with Solaris tar.  We respectfully suggest you
configure CPAN to use a GNU tar instead ("o conf init tar") or install
a recent Archive::Tar instead;

END_WARN
        }
    } else {
        my $foundtar = $exttar ? "'$exttar'" : "nothing";
        my $foundzip = $extgzip ? "'$extgzip'" : $foundtar ? "nothing" : "also nothing";
        my $foundAT;
        if ($CPAN::META-&gt;has_usable("Archive::Tar")) {
            $foundAT = sprintf "'%s'", "Archive::Tar::"-&gt;VERSION;
        } else {
            $foundAT = "nothing";
        }
        my $foundCZ;
        if ($CPAN::META-&gt;has_inst("Compress::Zlib")) {
            $foundCZ = sprintf "'%s'", "Compress::Zlib::"-&gt;VERSION;
        } elsif ($foundAT) {
            $foundCZ = "nothing";
        } else {
            $foundCZ = "also nothing";
        }
        $CPAN::Frontend-&gt;mydie(qq{

CPAN.pm needs either the external programs tar and gzip -or- both
modules Archive::Tar and Compress::Zlib installed.

For tar I found $foundtar, for gzip $foundzip.

For Archive::Tar I found $foundAT, for Compress::Zlib $foundCZ;

Can't continue cutting file '$file'.
});
    }
    my $tar_verb = "v";
    if (defined $CPAN::Config-&gt;{tar_verbosity}) {
        $tar_verb = $CPAN::Config-&gt;{tar_verbosity} eq "none" ? "" :
            $CPAN::Config-&gt;{tar_verbosity};
    }
    if ($prefer==1) { # 1 =&gt; external gzip+tar
        my($system);
        my $is_compressed = $self-&gt;gtest();
        my $tarcommand = CPAN::HandleConfig-&gt;safe_quote($exttar);
        if ($is_compressed) {
            my $command = CPAN::HandleConfig-&gt;safe_quote($extgzip);
            $system = qq{$command -d -c }.
                qq{&lt; "$file" | $tarcommand x${tar_verb}f -};
        } else {
            $system = qq{$tarcommand x${tar_verb}f "$file"};
        }
        if (system($system) != 0) {
            # people find the most curious tar binaries that cannot handle
            # pipes
            if ($is_compressed) {
                (my $ungzf = $file) =~ s/\.gz(?!\n)\Z//;
                $ungzf = basename $ungzf;
                my $ct = CPAN::Tarzip-&gt;new($file);
                if ($ct-&gt;gunzip($ungzf)) {
                    $CPAN::Frontend-&gt;myprint(qq{Uncompressed $file successfully\n});
                } else {
                    $CPAN::Frontend-&gt;mydie(qq{Couldn\'t uncompress $file\n});
                }
                $file = $ungzf;
            }
            $system = qq{$tarcommand x${tar_verb}f "$file"};
            $CPAN::Frontend-&gt;myprint(qq{Using Tar:$system:\n});
            my $ret = system($system);
            if ($ret==0) {
                $CPAN::Frontend-&gt;myprint(qq{Untarred $file successfully\n});
            } else {
                if ($? == -1) {
                    $CPAN::Frontend-&gt;mydie(sprintf qq{Couldn\'t untar %s: '%s'\n},
                                           $file, $!);
                } elsif ($? &amp; 127) {
                    $CPAN::Frontend-&gt;mydie(sprintf qq{Couldn\'t untar %s: child died with signal %d, %s coredump\n},
                                           $file, ($? &amp; 127),  ($? &amp; 128) ? 'with' : 'without');
                } else {
                    $CPAN::Frontend-&gt;mydie(sprintf qq{Couldn\'t untar %s: child exited with value %d\n},
                                           $file, $? &gt;&gt; 8);
                }
            }
            return 1;
        } else {
            return 1;
        }
    } elsif ($prefer==2) { # 2 =&gt; modules
        unless ($CPAN::META-&gt;has_usable("Archive::Tar")) {
            $CPAN::Frontend-&gt;mydie("Archive::Tar not installed, please install it to continue");
        }
        # Make sure AT does not use uid/gid/permissions in the archive
        # This leaves it to the user's umask instead
        local $Archive::Tar::CHMOD = 1;
        local $Archive::Tar::SAME_PERMISSIONS = 0;
        # Make sure AT leaves current user as owner
        local $Archive::Tar::CHOWN = 0;
        my $tar = Archive::Tar-&gt;new($file,1);
        my $af; # archive file
        my @af;
        if ($BUGHUNTING) {
            # RCS 1.337 had this code, it turned out unacceptable slow but
            # it revealed a bug in Archive::Tar. Code is only here to hunt
            # the bug again. It should never be enabled in published code.
            # GDGraph3d-0.53 was an interesting case according to Larry
            # Virden.
            warn("&gt;&gt;&gt;Bughunting code enabled&lt;&lt;&lt; " x 20);
            for $af ($tar-&gt;list_files) {
                if ($af =~ m!^(/|\.\./)!) {
                    $CPAN::Frontend-&gt;mydie("ALERT: Archive contains ".
                                           "illegal member [$af]");
                }
                $CPAN::Frontend-&gt;myprint("$af\n");
                $tar-&gt;extract($af); # slow but effective for finding the bug
                return if $CPAN::Signal;
            }
        } else {
            for $af ($tar-&gt;list_files) {
                if ($af =~ m!^(/|\.\./)!) {
                    $CPAN::Frontend-&gt;mydie("ALERT: Archive contains ".
                                           "illegal member [$af]");
                }
                if ($tar_verb eq "v" || $tar_verb eq "vv") {
                    $CPAN::Frontend-&gt;myprint("$af\n");
                }
                push @af, $af;
                return if $CPAN::Signal;
            }
            $tar-&gt;extract(@af) or
                $CPAN::Frontend-&gt;mydie("Could not untar with Archive::Tar.");
        }

        Mac::BuildTools::convert_files([$tar-&gt;list_files], 1)
            if ($^O eq 'MacOS');

        return 1;
    }
}

sub unzip {
    my($self) = @_;
    my $file = $self-&gt;{FILE};
    if ($CPAN::META-&gt;has_inst("Archive::Zip")) {
        # blueprint of the code from Archive::Zip::Tree::extractTree();
        my $zip = Archive::Zip-&gt;new();
        my $status;
        $status = $zip-&gt;read($file);
        $CPAN::Frontend-&gt;mydie("Read of file[$file] failed\n")
            if $status != Archive::Zip::AZ_OK();
        $CPAN::META-&gt;debug("Successfully read file[$file]") if $CPAN::DEBUG;
        my @members = $zip-&gt;members();
        for my $member ( @members ) {
            my $af = $member-&gt;fileName();
            if ($af =~ m!^(/|\.\./)!) {
                $CPAN::Frontend-&gt;mydie("ALERT: Archive contains ".
                                       "illegal member [$af]");
            }
            $status = $member-&gt;extractToFileNamed( $af );
            $CPAN::META-&gt;debug("af[$af]status[$status]") if $CPAN::DEBUG;
            $CPAN::Frontend-&gt;mydie("Extracting of file[$af] from zipfile[$file] failed\n") if
                $status != Archive::Zip::AZ_OK();
            return if $CPAN::Signal;
        }
        return 1;
    } elsif ( my $unzip = $CPAN::Config-&gt;{unzip}  ) {
        my @system = ($unzip, $file);
        return system(@system) == 0;
    }
    else {
            $CPAN::Frontend-&gt;mydie(&lt;&lt;"END");

Can't unzip '$file':

You have not configured an 'unzip' program and do not have Archive::Zip
installed.  Please either install Archive::Zip or else configure 'unzip'
by running the command 'o conf init unzip' from the CPAN shell prompt.

END
    }
}

1;

__END__

=head1 NAME

CPAN::Tarzip - internal handling of tar archives for CPAN.pm

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=cut
</pre></body></html>