Archive-Libarchive-FFI
view release on metacpan or search on metacpan
lib/Archive/Libarchive/FFI/Callback.pm view on Meta::CPAN
package Archive::Libarchive::FFI::Callback;
use strict;
use warnings;
use 5.008;
# ABSTRACT: Libarchive callbacks
our $VERSION = '0.0902'; # VERSION
package
Archive::Libarchive::FFI;
BEGIN {
if(eval { require FFI::Sweet })
{
FFI::Sweet->import;
}
else
{
require Archive::Libarchive::FFI::SweetLite;
Archive::Libarchive::FFI::SweetLite->import;
}
}
use FFI::Util qw( deref_ptr_set _size_t );
use constant {
CB_DATA => 0,
CB_READ => 1,
CB_CLOSE => 2,
CB_OPEN => 3,
CB_WRITE => 4,
CB_SKIP => 5,
CB_SEEK => 6,
CB_SWITCH => 7,
CB_BUFFER => 8,
};
my %callbacks;
do {
no warnings 'redefine';
sub _attach_function ($$$;$)
{
eval {
attach_function($_[0], $_[1], $_[2], $_[3]);
};
warn $@ if $@ && $ENV{ARCHIVE_LIBARCHIVE_FFI_VERBOSE};
}
};
my $myopen = FFI::Raw::Callback->new(sub {
my($archive) = @_;
my $status = eval {
$callbacks{$archive}->[CB_OPEN]->($archive, $callbacks{$archive}->[CB_DATA]);
};
if($@)
{
warn $@;
return ARCHIVE_FATAL();
}
$status;
}, _int, _ptr, _ptr);
my $mywrite = FFI::Raw::Callback->new(sub
{
my($archive, $null, $ptr, $size) = @_;
my $buffer = buffer_to_scalar($ptr, $size);
my $status = eval {
$callbacks{$archive}->[CB_WRITE]->($archive, $callbacks{$archive}->[CB_DATA], $buffer);
};
if($@)
{
warn $@;
return ARCHIVE_FATAL();
}
$status;
}, _int, _ptr, _ptr, _ptr, _size_t);
my $myread = FFI::Raw::Callback->new(sub
{
my($archive, $null, $optr) = @_;
my($status, $buffer) = eval {
$callbacks{$archive}->[CB_READ]->($archive, $callbacks{$archive}->[CB_DATA]);
};
if($@)
{
warn $@;
return ARCHIVE_FATAL();
}
my($ptr, $size) = scalar_to_buffer($buffer);
deref_ptr_set($optr, $ptr);
$size;
}, _uint64, _ptr, _ptr, _ptr);
my $myskip = FFI::Raw::Callback->new(sub
{
my($archive, $null, $request) = @_;
my $status = eval {
$callbacks{$archive}->[CB_SKIP]->($archive, $callbacks{$archive}->[CB_DATA], $request);
};
if($@)
{
warn $@;
return ARCHIVE_FATAL();
}
$status;
}, _uint64, _ptr, _ptr, _uint64);
my $myseek = FFI::Raw::Callback->new(sub
{
my($archive, $null, $offset, $whence) = @_;
my $status = eval {
$callbacks{$archive}->[CB_SEEK]->($archive, $callbacks{$archive}->[CB_DATA], $offset, $whence);
};
if($@)
{
warn $@;
return ARCHIVE_FATAL();
}
$status;
}, _uint64, _ptr, _ptr, _uint64, _int);
my $myclose = FFI::Raw::Callback->new(sub
{
my($archive) = @_;
my $status = eval {
$callbacks{$archive}->[CB_CLOSE]->($archive, $callbacks{$archive}->[CB_DATA]);
};
if($@)
{
warn $@;
return ARCHIVE_FATAL();
}
$status;
}, _int, _ptr, _ptr);
_attach_function 'archive_write_open', [ _ptr, _ptr, _ptr, _ptr, _ptr ], _int, sub
{
my($cb, $archive, $cd, $open, $write, $close) = @_;
$callbacks{$archive}->[CB_DATA] = $cd;
if(defined $open)
{
$callbacks{$archive}->[CB_OPEN] = $open;
$open = $myopen;
}
if(defined $write)
{
$callbacks{$archive}->[CB_WRITE] = $write;
$write = $mywrite;
}
if(defined $close)
{
$callbacks{$archive}->[CB_CLOSE] = $close;
$close = $myclose;
}
$cb->($archive, undef, $open||0, $write||0, $close||0);
};
sub archive_read_open ($$$$$)
{
my($archive, $data, $open, $read, $close) = @_;
archive_read_open2($archive, $data, $open, $read, undef, $close);
}
_attach_function 'archive_read_open2', [ _ptr, _ptr, _ptr, _ptr, _ptr, _ptr ], _int, sub
{
my($cb, $archive, $cd, $open, $read, $skip, $close) = @_;
$callbacks{$archive}->[CB_DATA] = $cd;
if(defined $open)
{
$callbacks{$archive}->[CB_OPEN] = $open;
$open = $myopen;
}
if(defined $read)
{
$callbacks{$archive}->[CB_READ] = $read;
$read = $myread;
}
if(defined $skip)
{
$callbacks{$archive}->[CB_SKIP] = $skip;
$skip = $myskip;
}
if(defined $close)
{
$callbacks{$archive}->[CB_CLOSE] = $close;
$close = $myclose;
}
$cb->($archive, undef, $open||0, $read||0, $skip||0, $close||0);
};
sub archive_read_set_callback_data ($$)
{
my($archive, $data) = @_;
$callbacks{$archive}->[CB_DATA] = $data;
ARCHIVE_OK();
}
foreach my $name (qw( open read skip close seek ))
{
my $const = 'CB_' . uc $name;
my $wrapper = eval '# line '. __LINE__ . ' "' . __FILE__ . "\n" . qq{
sub
{
my(\$cb, \$archive, \$callback) = \@_;
\$callbacks{\$archive}->[$const] = \$callback;
\$cb->(\$archive, \$my$name);
}
};die $@ if $@;
_attach_function "archive_read_set_$name\_callback", [ _ptr, _ptr ], _int;
}
if(archive_version_number() >= 3000000)
{
_attach_function 'archive_read_open_memory', [ _ptr, _ptr, _size_t ], _int, sub
{
my($cb, $archive, $buffer) = @_;
my $length = do { use bytes; length $buffer };
my $ptr = FFI::Raw::MemPtr->new_from_buf($buffer, $length);
$callbacks{$archive}->[CB_BUFFER] = $ptr; # TODO: CB_BUFFER or CB_DATA (or something else?)
$cb->($archive, $ptr, $length);
};
}
else
{
sub _archive_read_open_memory_read
{
my($archive, $data) = @_;
if($data->{done})
{
return (ARCHIVE_OK(), '');
}
else
{
$data->{done} = 1;
return (ARCHIVE_OK(), $data->{buffer});
}
}
*archive_read_open_memory = sub ($$) {
my($archive, $buffer) = @_;
my $r = archive_read_open($archive, { buffer => $buffer, done => 0 }, undef, \&_archive_read_open_memory_read, undef);
unless($r == ARCHIVE_OK())
{
warn "error: " . archive_error_string($archive);
}
$r;
};
}
_attach_function archive_version_number() >= 3000000 ? 'archive_read_free' : [ archive_read_finish => 'archive_read_free' ], [ _ptr ], _int, sub
{
my($cb, $archive) = @_;
my $ret = $cb->($archive);
delete $callbacks{$archive};
$ret;
};
_attach_function archive_version_number() >= 3000000 ? 'archive_write_free' : [ archive_write_finish => 'archive_write_free' ], [ _ptr ], _int, sub
{
my($cb, $archive) = @_;
my $ret = $cb->($archive);
delete $callbacks{$archive};
$ret;
};
my %lookups;
use constant {
CB_LOOKUP_USER => 0,
CB_LOOKUP_GROUP => 1,
};
my $mylook_write_user_lookup = FFI::Raw::Callback->new(sub {
my($archive, $name, $id) = @_;
my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_USER] };
return $id unless defined $look_cb;
$look_cb->($data, $name, $id);
}, _int64, _ptr, _str, _int64);
my $mylook_write_group_lookup = FFI::Raw::Callback->new(sub {
my($archive, $name, $id) = @_;
my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_GROUP] };
return $id unless defined $look_cb;
$look_cb->($data, $name, $id);
}, _int64, _ptr, _str, _int64);
my $mylook_read_user_lookup = FFI::Raw::Callback->new(sub {
my($archive, $id) = @_;
my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_USER] };
return undef unless defined $look_cb;
my $name = $look_cb->($data, $id);
return $name if defined $name;
return;
}, _str, _ptr, _int64);
my $mylook_read_group_lookup = FFI::Raw::Callback->new(sub {
my($archive, $id) = @_;
my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_GROUP] };
return undef unless defined $look_cb;
my $name = $look_cb->($data, $id);
return $name if defined $name;
return;
}, _str, _ptr, _int64);
my $mylook_user_cleanup = FFI::Raw::Callback->new(sub {
my($archive) = @_;
my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_USER] };
$clean_cb->($data) if defined $clean_cb;
delete $lookups{$archive};
}, _void, _ptr);
my $mylook_group_cleanup = FFI::Raw::Callback->new(sub {
my($archive) = @_;
my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_GROUP] };
$clean_cb->($data) if defined $clean_cb;
delete $lookups{$archive};
}, _void, _ptr);
_attach_function 'archive_write_disk_set_user_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
{
my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
if(defined $look_cb || defined $clean_cb)
{
$lookups{$archive}->[CB_LOOKUP_USER] = [ $data, $look_cb, $clean_cb ];
return $cb->($archive, $archive, $mylook_write_user_lookup, $mylook_user_cleanup);
}
return $cb->($archive, undef, undef, undef);
};
_attach_function 'archive_write_disk_set_group_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
{
my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
if(defined $look_cb || defined $clean_cb)
{
$lookups{$archive}->[CB_LOOKUP_GROUP] = [ $data, $look_cb, $clean_cb ];
return $cb->($archive, $archive, $mylook_write_group_lookup, $mylook_group_cleanup);
}
return $cb->($archive, undef, undef, undef);
};
_attach_function 'archive_read_disk_set_uname_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
{
my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
if(defined $look_cb || defined $clean_cb)
{
$lookups{$archive}->[CB_LOOKUP_USER] = [ $data, $look_cb, $clean_cb ];
return $cb->($archive, $archive, $mylook_read_user_lookup, $mylook_user_cleanup);
}
return $cb->($archive, undef, undef, undef);
};
_attach_function 'archive_read_disk_set_gname_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
{
my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
if(defined $look_cb || defined $clean_cb)
{
$lookups{$archive}->[CB_LOOKUP_GROUP] = [ $data, $look_cb, $clean_cb ];
return $cb->($archive, $archive, $mylook_read_group_lookup, $mylook_group_cleanup);
}
return $cb->($archive, undef, undef, undef);
};
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Archive::Libarchive::FFI::Callback - Libarchive callbacks
=head1 VERSION
version 0.0902
=head1 SYNOPSIS
use Archive::Libarchive::FFI qw( :all );
# read
my $archive = archive_read_new();
archive_read_open($archive, $data, \&myopen, \&myread, \&myclose);
# write
my $archive = archive_write_new();
archive_write_open($archive, $data, \&myopen, \&mywrite, \&myclose);
=head1 DESCRIPTION
This document provides information of callback routines for writing
custom input/output interfaces to the libarchive perl bindings. The
first two arguments passed into all callbacks are:
=over 4
=item $archive
The archive object (actually a pointer to the C structure that managed
the archive object).
=item $data
The callback data object (any legal Perl data structure).
=back
For the variable name / types conventions used in this document, see
L<Archive::Libarchive::FFI::Function>.
The expected return value for all callbacks EXCEPT the read callback
is a standard integer libarchive status value (example: C<ARCHIVE_OK>
or C<ARCHIVE_FATAL>).
If your callback dies (throws an exception), it will be caught at the
Perl level. The error will be sent to standard error via L<warn|perlfunc#warn>
and C<ARCHIVE_FATAL> will be passed back to libarchive.
=head2 data
There is a data field for callbacks associated with each $archive object.
It can be any native Perl type (example: scalar, hashref, coderef, etc).
You can set this by calling
L<archive_read_set_callback_data|Archive::Libarchive::FFI::Function#archive_read_set_callback_data>,
or by passing the data argument when you "open" the archive using
L<archive_read_open|Archive::Libarchive::FFI::Function#archive_read_open>,
L<archive_read_open2|Archive::Libarchive::FFI::Function#archive_read_open2> or
L<archive_write_open|Archive::Libarchive::FFI::Function#archive_write_open>.
The data field will be passed into each callback as its second argument.
=head2 open
my $status1 = archive_read_set_open_callback($archive, sub {
my($archive, $data) = @_;
...
return $status2;
});
According to the libarchive, this is never needed, but you can register
a callback to happen when you open.
Can also be set when you call
L<archive_read_open|Archive::Libarchive::FFI::Function#archive_read_open>,
L<archive_read_open2|Archive::Libarchive::FFI::Function#archive_read_open2> or
L<archive_write_open|Archive::Libarchive::FFI::Function#archive_write_open>.
=head2 read
my $status1 = archive_read_set_read_callback($archive, sub {
my($archive, $data) = @_;
...
return ($status2, $buffer)
});
This callback is called whenever libarchive is ready for more data to
process. It doesn't take in any additional arguments, but it expects
two return values, a status and a buffer containing the data.
Can also be set when you call
L<archive_read_open|Archive::Libarchive::FFI::Function#archive_read_open> or
L<archive_read_open2|Archive::Libarchive::FFI::Function#archive_read_open2>.
=head2 write
my $mywrite = sub {
my($archive, $data, $buffer) = @_;
...
return $bytes_written_or_status;
};
my $status2 = archive_write_open($archive, undef, $mywrite, undef);
This callback is called whenever libarchive has data it wants to send
to output. The callback itself takes one additional argument, a
buffer containing the data to write.
It should return the actual number of bytes written by you, or an
status value for an error.
=head2 skip
( run in 0.979 second using v1.01-cache-2.11-cpan-ceb78f64989 )