Archive-Libarchive

 view release on metacpan or  search on metacpan

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

 # archive_version_number
 my $int = archive_version_number();

The C<libarchive> version expressed as an integer.  This will be the major, minor and patch
levels each using up to three digits, so 3.5.1 will be C<3005001>.

=head2 archive_version_string

 # archive_version_string
 my $string = archive_version_string();

The C<libarchive> version as a string.

=head2 archive_zlib_version

 # archive_zlib_version
 my $string = archive_zlib_version();

The C<zlib> version that C<libarchive> was built with.  This will return C<undef> if the library was
not found at build time.

=head2 versions

 my %versions = Archive::Libarchive->versions();

This returns a hash of C<libarchive> and L<Archive::Libarchive> versions and dependency versions.  This
may be useful in a test report diagnostic.

=head1 EXAMPLES

These examples are translated from the C<libarchive> C examples, which can be found here:

=over 4

=item L<https://github.com/libarchive/libarchive/wiki/Examples>

=back

=head2 List contents of archive stored in file

The main L<Archive::Libarchive> API is based around two basic type of classes.  The L<Archive::Libarchive::Archive>
class serves as a basis for all archive objects.  The L<Archive::Libarchive::Entry> represents the header or metadata
for files stored inside an archive (or as we will see later, files on disk).

The basic life cycle of an archive instance is:

=over 4

=item Create one using its C<new> constructor

The constructor does not take any arguments, instead you will configure it in the
next step.

=item Configure it using "support" or "set" calls

Support calls allow L<Archive::Libarchive> to decide when to use a feature; "set" calls
enable the feature unconditionally.

=item "Open" a particular data source

This can be using callbacks for a custom source, or one of the pre-canned data sources supported directly by
L<Archive::Libarchive>.

=item Iterate over the contents

Ask alternatively for "header" or entry/file metadata (which is represented by a L<Archive::Libarchive::Entry> instance),
and entry/file content.

=item Finish by calling "close"

This will be called automatically if the archive instance falls out of scope.

=back

Writing an archive is very similar, except that you provide the "header" and content data to L<Archive::Libarchive> instead
of asking for them.

Here is a very basic example that simply opens a file and lists the contents of the archive.

 use 5.020;
 use Archive::Libarchive qw( ARCHIVE_OK );
 
 my $r = Archive::Libarchive::ArchiveRead->new;
 $r->support_filter_all;
 $r->support_format_all;
 
 my $ret = $r->open_filename("archive.tar", 10240);
 if($ret != ARCHIVE_OK) {
   exit 1;
 }
 
 my $e = Archive::Libarchive::Entry->new;
 while($r->next_header($e) == ARCHIVE_OK) {
   say $e->pathname;
   $r->read_data_skip;
 }

Note that L<open_filename|Archive::Libarchive::ArchiveRead/open_filename> method inspects the file before deciding
how to handle the block size.  If the filename provided refers to a tape device, for example, it will use exactly
the block size you specify.  For other devices, it may adjust the requested block size in order to obtain better
performance.

Note that the call to L<read_data_skip|Archive::Libarchive::API/read_data_skip> here is not actually necessary, since
L<Archive::Libarchive> will invoke it automatically if you request the next header without reading the data for the
last entry.

The module L<Archive::Libarchive::Peek> also provides similar functionality to this example in a simple, less
powerful interface.

=head2 List contents of archive stored in memory

There are several variants of the open methods.  The "filename" variant used above is intended to be simple
to use in the common case of reading from a file from disk, but you may find the "memory" variant useful in other
cases.

 use 5.020;
 use Path::Tiny qw( path );
 use Archive::Libarchive qw( ARCHIVE_OK );
 
 my $r = Archive::Libarchive::ArchiveRead->new;
 $r->support_filter_all;
 $r->support_format_all;
 
 my $buffer = path('archive.tar')->slurp_raw;
 
 my $ret = $r->open_memory(\$buffer);
 if($ret != ARCHIVE_OK) {
   exit 1;
 }
 
 my $e = Archive::Libarchive::Entry->new;
 while($r->next_header($e) == ARCHIVE_OK) {
   say $e->pathname;
   $r->read_data_skip;
 }

There are also variants to read from an already-opened file descriptor, a C<libc> C<FILE> pointer, or a Perl
file handle.

=head2 List contents of archive with custom read functions

Sometimes, none of the packaged open methods will work for you.  In that case, you can use the lower-level C<open>
method, which accepts a number of callbacks.  For this example we will use the C<open>, C<read> and C<close>
callbacks.

 use 5.020;
 use Archive::Libarchive qw( :const );
 
 my $r = Archive::Libarchive::ArchiveRead->new;
 $r->support_filter_all;
 $r->support_format_all;
 
 my $fh;
 
 $r->open(
   open => sub {
     open $fh, '<', 'archive.tar';
     binmode $fh;
     return ARCHIVE_OK;
   },
   read => sub {
     my(undef, $ref) = @_;
     my $size = read $fh, $$ref, 512;
     return $size;
   },
   close => sub {
     close $fh;
     return ARCHIVE_OK;
   },
 ) == ARCHIVE_OK or die $r->error_string;
 
 my $e = Archive::Libarchive::Entry->new;
 while(1) {
   my $ret = $r->next_header($e);
   last if $ret == ARCHIVE_EOF;
   die $r->error_string if $ret < ARCHIVE_WARN;
   warn $r->error_string if $ret != ARCHIVE_OK;
   say $e->pathname;
 }
 
 $r->close;

For full power of read callbacks see the L<open method's documentation|Archive::Libarchive::ArchiveRead/open>.

When writing to an archive the L<Archive::Libarchive::ArchiveWrite> class also has its own
L<open method and callbacks|Archive::Libarchive::ArchiveWrite/open>.

=head2 A universal decompressor / defilter-er

The "raw" format handler treats arbitrary binary input as a single-element archive.  This allows you to get the
output of a libarchive filter chain, including files with multiple encodings, such as C<gz.uu> files:

 use 5.020;
 use Archive::Libarchive;
 
 my $r = Archive::Libarchive::ArchiveRead->new;
 $r->support_filter_all;
 $r->support_format_raw;
 $r->open_filename("hello.txt.uu");
 $r->next_header(Archive::Libarchive::Entry->new);
 
 my $buffer;
 while($r->read_data(\$buffer)) {
   print $buffer;
 }
 
 $r->close;

Note that the "raw" format is not enabled by the
L<support_format_all method on Archive::Libarchive::ArchiveRead|Archive::Libarchive::API/support_format_all>.
Also note that the "raw" format handler does not recognize or accept empty files.  If you specifically want to be
able to read empty files, you'll need to also invoke the
L<support_format_empty method on Archive::Libarchive::ArchiveRead|Archive::Libarchive::API/support_format_empty>.

The module L<Archive::Libarchive::Unwrap> also provides similar functionality to this example in a simple, less
powerful interface.

=head2 A basic write example

The following is a very simple example of using L<Archive::Libarchive> to write a group of files into a tar archive.
This is a little more complex than the read examples above because the write example actually does something with
the file bodies.

 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 });
 



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