File Coverage

blib/lib/Archive/Libarchive/FFI/Callback.pm
Criterion Covered Total %
statement 30 39 76.9
branch 1 6 16.6
condition 1 3 33.3
subroutine 10 12 83.3
pod 0 2 0.0
total 42 62 67.7


line stmt bran cond sub pod time code
1             package Archive::Libarchive::FFI::Callback;
2              
3 27     27   144 use strict;
  27         46  
  27         769  
4 27     27   210 use warnings;
  27         110  
  27         2722  
5              
6             # ABSTRACT: Libarchive callbacks
7             our $VERSION = '0.0900'; # VERSION
8              
9             package
10             Archive::Libarchive::FFI;
11              
12             BEGIN {
13              
14 27 50   27   62 if(eval { require FFI::Sweet })
  27         10298  
15             {
16 0         0 FFI::Sweet->import;
17             }
18             else
19             {
20 27         136 require Archive::Libarchive::FFI::SweetLite;
21 27         157 Archive::Libarchive::FFI::SweetLite->import;
22             }
23             }
24 27     27   10386 use FFI::Util qw( deref_ptr_set _size_t );
  27         53  
  27         197  
25              
26             use constant {
27 27         4000 CB_DATA => 0,
28             CB_READ => 1,
29             CB_CLOSE => 2,
30             CB_OPEN => 3,
31             CB_WRITE => 4,
32             CB_SKIP => 5,
33             CB_SEEK => 6,
34             CB_SWITCH => 7,
35             CB_BUFFER => 8,
36 27     27   2750 };
  27         52  
37              
38             my %callbacks;
39              
40             do {
41 27     27   137 no warnings 'redefine';
  27         52  
  27         40133  
42             sub _attach_function ($$$;$)
43             {
44 6750     6750   9191 eval {
45 6750         21991 attach_function($_[0], $_[1], $_[2], $_[3]);
46             };
47 6750 0 33     53892 warn $@ if $@ && $ENV{ARCHIVE_LIBARCHIVE_FFI_VERBOSE};
48             }
49             };
50              
51             my $myopen = FFI::Raw::Callback->new(sub {
52             my($archive) = @_;
53             my $status = eval {
54             $callbacks{$archive}->[CB_OPEN]->($archive, $callbacks{$archive}->[CB_DATA]);
55             };
56             if($@)
57             {
58             warn $@;
59             return ARCHIVE_FATAL();
60             }
61             $status;
62             }, _int, _ptr, _ptr);
63              
64             my $mywrite = FFI::Raw::Callback->new(sub
65             {
66             my($archive, $null, $ptr, $size) = @_;
67             my $buffer = buffer_to_scalar($ptr, $size);
68             my $status = eval {
69             $callbacks{$archive}->[CB_WRITE]->($archive, $callbacks{$archive}->[CB_DATA], $buffer);
70             };
71             if($@)
72             {
73             warn $@;
74             return ARCHIVE_FATAL();
75             }
76             $status;
77             }, _int, _ptr, _ptr, _ptr, _size_t);
78              
79             my $myread = FFI::Raw::Callback->new(sub
80             {
81             my($archive, $null, $optr) = @_;
82             my($status, $buffer) = eval {
83             $callbacks{$archive}->[CB_READ]->($archive, $callbacks{$archive}->[CB_DATA]);
84             };
85             if($@)
86             {
87             warn $@;
88             return ARCHIVE_FATAL();
89             }
90             my($ptr, $size) = scalar_to_buffer($buffer);
91             deref_ptr_set($optr, $ptr);
92             $size;
93             }, _uint64, _ptr, _ptr, _ptr);
94              
95             my $myskip = FFI::Raw::Callback->new(sub
96             {
97             my($archive, $null, $request) = @_;
98             my $status = eval {
99             $callbacks{$archive}->[CB_SKIP]->($archive, $callbacks{$archive}->[CB_DATA], $request);
100             };
101             if($@)
102             {
103             warn $@;
104             return ARCHIVE_FATAL();
105             }
106             $status;
107             }, _uint64, _ptr, _ptr, _uint64);
108              
109             my $myseek = FFI::Raw::Callback->new(sub
110             {
111             my($archive, $null, $offset, $whence) = @_;
112             my $status = eval {
113             $callbacks{$archive}->[CB_SEEK]->($archive, $callbacks{$archive}->[CB_DATA], $offset, $whence);
114             };
115             if($@)
116             {
117             warn $@;
118             return ARCHIVE_FATAL();
119             }
120             $status;
121             }, _uint64, _ptr, _ptr, _uint64, _int);
122              
123             my $myclose = FFI::Raw::Callback->new(sub
124             {
125             my($archive) = @_;
126             my $status = eval {
127             $callbacks{$archive}->[CB_CLOSE]->($archive, $callbacks{$archive}->[CB_DATA]);
128             };
129             if($@)
130             {
131             warn $@;
132             return ARCHIVE_FATAL();
133             }
134             $status;
135             }, _int, _ptr, _ptr);
136              
137             _attach_function 'archive_write_open', [ _ptr, _ptr, _ptr, _ptr, _ptr ], _int, sub
138             {
139             my($cb, $archive, $cd, $open, $write, $close) = @_;
140             $callbacks{$archive}->[CB_DATA] = $cd;
141             if(defined $open)
142             {
143             $callbacks{$archive}->[CB_OPEN] = $open;
144             $open = $myopen;
145             }
146             if(defined $write)
147             {
148             $callbacks{$archive}->[CB_WRITE] = $write;
149             $write = $mywrite;
150             }
151             if(defined $close)
152             {
153             $callbacks{$archive}->[CB_CLOSE] = $close;
154             $close = $myclose;
155             }
156             $cb->($archive, undef, $open||0, $write||0, $close||0);
157             };
158              
159             sub archive_read_open ($$$$$)
160             {
161 6     6 0 1253 my($archive, $data, $open, $read, $close) = @_;
162 6         26 archive_read_open2($archive, $data, $open, $read, undef, $close);
163             }
164              
165             _attach_function 'archive_read_open2', [ _ptr, _ptr, _ptr, _ptr, _ptr, _ptr ], _int, sub
166             {
167             my($cb, $archive, $cd, $open, $read, $skip, $close) = @_;
168             $callbacks{$archive}->[CB_DATA] = $cd;
169             if(defined $open)
170             {
171             $callbacks{$archive}->[CB_OPEN] = $open;
172             $open = $myopen;
173             }
174             if(defined $read)
175             {
176             $callbacks{$archive}->[CB_READ] = $read;
177             $read = $myread;
178             }
179             if(defined $skip)
180             {
181             $callbacks{$archive}->[CB_SKIP] = $skip;
182             $skip = $myskip;
183             }
184             if(defined $close)
185             {
186             $callbacks{$archive}->[CB_CLOSE] = $close;
187             $close = $myclose;
188             }
189             $cb->($archive, undef, $open||0, $read||0, $skip||0, $close||0);
190             };
191              
192             sub archive_read_set_callback_data ($$)
193             {
194 0     0 0   my($archive, $data) = @_;
195 0           $callbacks{$archive}->[CB_DATA] = $data;
196 0           ARCHIVE_OK();
197             }
198              
199             foreach my $name (qw( open read skip close seek ))
200             {
201             my $const = 'CB_' . uc $name;
202             my $wrapper = eval '# line '. __LINE__ . ' "' . __FILE__ . "\n" . qq{
203             sub
204             {
205             my(\$cb, \$archive, \$callback) = \@_;
206             \$callbacks{\$archive}->[$const] = \$callback;
207             \$cb->(\$archive, \$my$name);
208             }
209             };die $@ if $@;
210            
211             _attach_function "archive_read_set_$name\_callback", [ _ptr, _ptr ], _int;
212             }
213              
214             if(archive_version_number() >= 3000000)
215             {
216             _attach_function 'archive_read_open_memory', [ _ptr, _ptr, _size_t ], _int, sub
217             {
218             my($cb, $archive, $buffer) = @_;
219 27     27   189 my $length = do { use bytes; length $buffer };
  27         132  
  27         226  
220             my $ptr = FFI::Raw::MemPtr->new_from_buf($buffer, $length);
221             $callbacks{$archive}->[CB_BUFFER] = $ptr; # TODO: CB_BUFFER or CB_DATA (or something else?)
222             $cb->($archive, $ptr, $length);
223             };
224             }
225             else
226             {
227             sub _archive_read_open_memory_read
228             {
229 0     0     my($archive, $data) = @_;
230 0 0         if($data->{done})
231             {
232 0           return (ARCHIVE_OK(), '');
233             }
234             else
235             {
236 0           $data->{done} = 1;
237 0           return (ARCHIVE_OK(), $data->{buffer});
238             }
239             }
240              
241             *archive_read_open_memory = sub ($$) {
242             my($archive, $buffer) = @_;
243             my $r = archive_read_open($archive, { buffer => $buffer, done => 0 }, undef, \&_archive_read_open_memory_read, undef);
244             unless($r == ARCHIVE_OK())
245             {
246             warn "error: " . archive_error_string($archive);
247             }
248             $r;
249             };
250             }
251              
252             _attach_function archive_version_number() >= 3000000 ? 'archive_read_free' : [ archive_read_finish => 'archive_read_free' ], [ _ptr ], _int, sub
253             {
254             my($cb, $archive) = @_;
255             my $ret = $cb->($archive);
256             delete $callbacks{$archive};
257             $ret;
258             };
259              
260             _attach_function archive_version_number() >= 3000000 ? 'archive_write_free' : [ archive_write_finish => 'archive_write_free' ], [ _ptr ], _int, sub
261             {
262             my($cb, $archive) = @_;
263             my $ret = $cb->($archive);
264             delete $callbacks{$archive};
265             $ret;
266             };
267              
268             my %lookups;
269              
270             use constant {
271 27         31351 CB_LOOKUP_USER => 0,
272             CB_LOOKUP_GROUP => 1,
273 27     27   12410 };
  27         65  
274              
275             my $mylook_write_user_lookup = FFI::Raw::Callback->new(sub {
276             my($archive, $name, $id) = @_;
277             my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_USER] };
278             return $id unless defined $look_cb;
279             $look_cb->($data, $name, $id);
280             }, _int64, _ptr, _str, _int64);
281              
282             my $mylook_write_group_lookup = FFI::Raw::Callback->new(sub {
283             my($archive, $name, $id) = @_;
284             my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_GROUP] };
285             return $id unless defined $look_cb;
286             $look_cb->($data, $name, $id);
287             }, _int64, _ptr, _str, _int64);
288              
289             my $mylook_read_user_lookup = FFI::Raw::Callback->new(sub {
290             my($archive, $id) = @_;
291             my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_USER] };
292             return undef unless defined $look_cb;
293             my $name = $look_cb->($data, $id);
294             return $name if defined $name;
295             return;
296             }, _str, _ptr, _int64);
297              
298             my $mylook_read_group_lookup = FFI::Raw::Callback->new(sub {
299             my($archive, $id) = @_;
300             my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_GROUP] };
301             return undef unless defined $look_cb;
302             my $name = $look_cb->($data, $id);
303             return $name if defined $name;
304             return;
305             }, _str, _ptr, _int64);
306              
307             my $mylook_user_cleanup = FFI::Raw::Callback->new(sub {
308             my($archive) = @_;
309             my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_USER] };
310             $clean_cb->($data) if defined $clean_cb;
311             delete $lookups{$archive};
312             }, _void, _ptr);
313              
314             my $mylook_group_cleanup = FFI::Raw::Callback->new(sub {
315             my($archive) = @_;
316             my($data, $look_cb, $clean_cb) = @{ $lookups{$archive}->[CB_LOOKUP_GROUP] };
317             $clean_cb->($data) if defined $clean_cb;
318             delete $lookups{$archive};
319             }, _void, _ptr);
320              
321             _attach_function 'archive_write_disk_set_user_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
322             {
323             my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
324             if(defined $look_cb || defined $clean_cb)
325             {
326             $lookups{$archive}->[CB_LOOKUP_USER] = [ $data, $look_cb, $clean_cb ];
327             return $cb->($archive, $archive, $mylook_write_user_lookup, $mylook_user_cleanup);
328             }
329             return $cb->($archive, undef, undef, undef);
330             };
331              
332             _attach_function 'archive_write_disk_set_group_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
333             {
334             my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
335             if(defined $look_cb || defined $clean_cb)
336             {
337             $lookups{$archive}->[CB_LOOKUP_GROUP] = [ $data, $look_cb, $clean_cb ];
338             return $cb->($archive, $archive, $mylook_write_group_lookup, $mylook_group_cleanup);
339             }
340             return $cb->($archive, undef, undef, undef);
341             };
342              
343             _attach_function 'archive_read_disk_set_uname_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
344             {
345             my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
346             if(defined $look_cb || defined $clean_cb)
347             {
348             $lookups{$archive}->[CB_LOOKUP_USER] = [ $data, $look_cb, $clean_cb ];
349             return $cb->($archive, $archive, $mylook_read_user_lookup, $mylook_user_cleanup);
350             }
351             return $cb->($archive, undef, undef, undef);
352             };
353              
354             _attach_function 'archive_read_disk_set_gname_lookup', [ _ptr, _ptr, _ptr, _ptr ], _int, sub
355             {
356             my($cb, $archive, $data, $look_cb, $clean_cb) = @_;
357             if(defined $look_cb || defined $clean_cb)
358             {
359             $lookups{$archive}->[CB_LOOKUP_GROUP] = [ $data, $look_cb, $clean_cb ];
360             return $cb->($archive, $archive, $mylook_read_group_lookup, $mylook_group_cleanup);
361             }
362             return $cb->($archive, undef, undef, undef);
363             };
364              
365             1;
366              
367             __END__