Archive-Libarchive

 view release on metacpan or  search on metacpan

lib/Archive/Libarchive/ArchiveWrite.pm  view on Meta::CPAN

package Archive::Libarchive::ArchiveWrite;

use strict;
use warnings;
use 5.020;
use Archive::Libarchive::Lib;
use Carp ();
use Ref::Util qw( is_plain_coderef is_blessed_ref );
use FFI::Platypus::Buffer qw( window scalar_to_buffer );
use FFI::Platypus::Memory qw( strdup free );
use Scalar::Util qw( refaddr );
use experimental qw( signatures );
use parent qw( Archive::Libarchive::Archive );

# ABSTRACT: Libarchive write archive class
our $VERSION = '0.09'; # VERSION

my $ffi = Archive::Libarchive::Lib->ffi;
my %keep;


$ffi->mangler(sub ($name) { "archive_write_$name"  });

$ffi->attach( new => [] => 'opaque' => sub {
  my($xsub, $class) = @_;
  my $ptr = $xsub->();
  bless { ptr => $ptr }, $class;
});

$ffi->attach( [ free => 'DESTROY' ] => ['archive_write'] => 'int' => sub {
  my($xsub, $self) = @_;
  free delete $self->{passphrase} if defined $self->{passphrase};
  return if $self->{cb}                  # inside a callback, we don't own the archive pointer
    || ${^GLOBAL_PHASE} eq 'DESTRUCT';   # during global shutdown, the xsub might go away
  my $ret = $xsub->($self);
  delete $keep{refaddr $self};
  warn "destroying archive pointer did not return ARCHIVE_OK" unless $ret == 0;
});


$ffi->attach( open => ['archive_write', 'opaque', 'archive_open_callback', 'archive_write_callback', 'archive_close_callback'] => 'int' => sub {
  my($xsub, $self, %cb) = @_;

  foreach my $name (qw( open write close ))
  {
    if(defined $cb{$name} && !is_plain_coderef $cb{$name})
    {
      Carp::croak("The $name callback is not a subref");
    }
  }

  my $opener = delete $cb{open};
  my $writer = delete $cb{write};
  my $closer = delete $cb{close};

  Carp::croak("Write callback is required") unless $writer;
  Carp::croak("No such write callbacks: @{[ sort keys %cb ]}") if %cb;

  if($opener)
  {
    my $orig = $opener;
    $opener = FFI::Platypus->closure(sub ($w, $) {
      $w = bless { ptr => $w, cb => 1 }, __PACKAGE__;
      $orig->($w);
    });
    push @{ $keep{refaddr $self} }, $opener;
  }

  if($writer)
  {
    my $orig = $writer;
    $writer = FFI::Platypus->closure(sub ($w, $, $ptr, $size) {
      $w = bless { ptr => $w, cb => 1 }, __PACKAGE__;
      my $buffer;
      window $buffer, $ptr, $size;
      $orig->($w, \$buffer);
    });
    push @{ $keep{refaddr $self} }, $writer;
  }

  if($closer)
  {
    my $orig = $closer;
    $closer = FFI::Platypus->closure(sub ($w, $) {
      $w = bless { ptr => $w, cb => 1 }, __PACKAGE__;
      $orig->($w);
    });
    push @{ $keep{refaddr $self} }, $closer;
  }

  $xsub->($self, undef, $opener, $writer, $closer);
});


$ffi->attach( open_FILE => ['archive_write', 'opaque'] => 'int' => sub {
  my($xsub, $self, $fp) = @_;
  $fp = $$fp if is_blessed_ref $fp && $fp->isa('FFI::C::File');
  $xsub->($self, $fp);
});


sub open_memory ($self, $image)
{
  # TODO: it would be nice to pre-allocate $$ref with grow (FFI::Platypus::Buffer)
  # but that gave me scary errors, so look into it later.
  $self->open(
    write => sub ($w, $ref) {
      $$image .= $$ref;
      return length $$ref;
    },
  );
}


sub open_perlfile ($self, $fh)
{
  $self->open(

lib/Archive/Libarchive/ArchiveWrite.pm  view on Meta::CPAN

=pod

=encoding UTF-8

=head1 NAME

Archive::Libarchive::ArchiveWrite - Libarchive write archive class

=head1 VERSION

version 0.09

=head1 SYNOPSIS

 use 5.020;
 use Archive::Libarchive;
 use Path::Tiny qw( path );
 
 my $w = Archive::Libarchive::ArchiveWrite->new;
 $w->set_format_pax_restricted;
 $w->open_filename("outarchive.tar");
 
 path('.')->visit(sub ($path, $) {
   my $path = shift;
 
   return if $path->is_dir;
 
   my $e = Archive::Libarchive::Entry->new;
   $e->set_pathname("$path");
   $e->set_size(-s $path);
   $e->set_filetype('reg');
   $e->set_perm( oct('0644') );
   $w->write_header($e);
   $w->write_data(\$path->slurp_raw);
 
 }, { recurse => 1 });
 
 $w->close;

=head1 DESCRIPTION

This class represents an archive instance for writing to archives.

=head1 CONSTRUCTOR

=head2 new

 # archive_write_new
 my $w = Archive::Libarchive::ArchiveWrite->new;

Create a new archive write object.

=head1 METHODS

This is a subset of total list of methods available to all archive classes.
For the full list see L<Archive::Libarchive::API/Archive::Libarchive::ArchiveWrite>.

=head2 open

 # archive_write_open
 $w->open(%callbacks);

This is a basic open method, which relies on callbacks for its implementation.  The
only callback that is required is the C<write> callback.  The C<open> and C<close>
callbacks are made available mostly for the benefit of the caller.  All callbacks
should return a L<normal status code|Archive::Libarchive/CONSTANTS>, which is
C<ARCHIVE_OK> on success.

Unlike the C<libarchive> C-API, this interface doesn't provide a facility for
passing in "client" data.  In Perl this is implemented using a closure, which should
allow you to pass in arbitrary variables via proper scoping.

=over 4

=item open

 $w->open(open => sub ($w) {
   ...
 });

Called immediately when the archive is "opened";

=item write

 $w->open(write => sub ($w, $ref) {
   ... = $$ref;
   return $size;
 });

This callback is called when data needs to be written to the archive.  It is passed in
as a reference to a scalar that contains the raw data.  On success you should return the actual size of
the data written in bytes, and on failure return a L<normal status code|Archive::Libarchive/CONSTANTS>.

=item close

 $w->open(open => sub ($w) {
   ...
 });

This is called when the archive instance is closed.

=back

=head2 open_FILE

 # archive_write_open_FILE
 $w->open_FILE($file_pointer);

This takes either a L<FFI::C::File>, or an opaque pointer to a libc file pointer.

=head2 open_memory

 # archive_write_open_memory
 $w->open_memory(\$buffer);

This takes a reference to scalar and stores the archive in memory there.

=head2 open_perlfile

 $w->open_perlfile(*FILE);

This takes a perl file handle and stores the archive there.

=head2 write_data



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