Archive-SCS

 view release on metacpan or  search on metacpan

lib/Archive/SCS/Zip.pm  view on Meta::CPAN

use v5.34;
use warnings;
use Object::Pad 0.73;

class Archive::SCS::Zip 1.09
  :isa( Archive::SCS::Mountable );

use builtin::compat qw( blessed true );
use stable 0.031 'isa';

use Archive::SCS::CityHash 'cityhash64';
use Archive::SCS::DirIndex;
use Carp 'croak';
use IO::Compress::Zip qw( :constants $ZipError );
use IO::Uncompress::Unzip qw( $UnzipError );
use Path::Tiny 0.054 ();

our @CARP_NOT = qw( Archive::SCS );

my $LOCAL_HEADER_SIGNATURE = "PK\x03\x04";

field $path :param :reader;
field $zip;

field %entries;
field @dirs;
field @files;

ADJUST {
  $path isa Path::Tiny && $path->exists or croak
    "Param file must be a valid Path::Tiny object";
}


method handles_path :common ($path, $header) {
  $header =~ /^\Q$LOCAL_HEADER_SIGNATURE\E/
  # According to the "appnote" spec, ZIP files don't begin with magic. Instead,
  # we'd need to scan the file's last 66 KB for the "end of central directory
  # record" signature, then heuristically determine if it looks like a ZIP file.
  # However, in practice, the overwhelming majority of ZIP files starts with a
  # "local file header" signature, which we can simply check for here.
}


method mount () {
  my $filename = $path->basename;
  $self->is_mounted and croak sprintf "%s: Already mounted", $filename;

  $zip = IO::Uncompress::Unzip->new( "$path", Append => 1 ) or croak
    "$filename: IO::Uncompress init: $UnzipError";

  my $status = 1;
  while ($status > 0) {
    my $entry = $zip->getHeaderInfo;

    my $name = $entry->{Name};
    my $is_empty = blessed $entry->{UncompressedLength} # type seems to vary by version
      ? $entry->{UncompressedLength}->isZero
      : $entry->{UncompressedLength} == 0;

    $entry->{is_dir} = $is_empty && $name =~ s|/\z||;
    $entries{ cityhash64 $name } = $entry;

    if ($entry->{is_dir}) {
      push @dirs, $name;
      next;
    }
    push @files, $name;

    $entry->{__data} = '';
    $status = $zip->read($entry->{__data}) while $status > 0;
    $status < 0 and croak
      "$filename: IO::Uncompress read ($entry->{Name}): $UnzipError ($status $!)";
  }
  continue {
    $status = $zip->nextStream;
    $status < 0 and croak
      "$filename: IO::Uncompress nextStream: $UnzipError ($!)";
  }
  $zip->close;

  return $self;
}


method unmount () {
  undef $zip;
  undef %entries;
  undef @dirs;
  undef @files;
}


method is_mounted () {
  !! $zip
}


method read_dir_tree (@roots) {



( run in 1.485 second using v1.01-cache-2.11-cpan-39bf76dae61 )