=head1 NAME

ALPM - XS module for libalpm (ArchLinux Package Manager)

=head1 VERSION

0.06 -- compatible with pacman 3.3's alpm

=head1 SYNOPSIS

  use ALPM ( root        => '/',
             dbpath      => '/var/lib/pacman/',
             cachedirs   => [ '/var/cache/pacman/pkg' ],
             logfile     => '/var/log/pacman.log',
             xfercommand => '/usr/bin/wget --passive-ftp -c -O %o %u' );

  # It's easier just to load a configuration file:
  use ALPM qw(/etc/pacman.conf);

  # My new favorite way to get/set options.  A tied hash.  (TMTOWTDI)
  my %alpm;
  tie %alpm, 'ALPM';
  $alpm{root} = '/';
  printf "Root Dir = %s\n", $alpm{root};
  my ($root, $dbpath, $cachedir) = @alpm{qw/root dbpath cachedir/};

  # Callback options...
  $alpm{logcb} = sub { my ($lvl, $msg) = @_; print "[$lvl] $msg\n" };

  # Querying databases & packages
  my $localdb = ALPM->localdb;
  my $pkg     = $localdb->get_pkg('perl');

  # Lots of different ways to get package attributes...
  my $attribs_ref    = $pkg->attribs_ref;
  my $name           = $pkg->name;
  my ($size, $isize) = $pkg->attribs('size', 'isize');
  print "$name $attribs_ref->{version} $attribs_ref->{arch} $size/$isize";

  my $syncdb   = ALPM->register( 'extra',
                                 'ftp://ftp.archlinux.org/$repo/os/i686' );
  my @perlpkgs = $syncdb->search('perl');
  printf "%d perl packages found.\n", scalar @perlpkgs;

  # Search all databases/repos.
  print map { sprintf "%10s: %s %s\n", $_->db->get_name, $_->name, $_->version }
            ALPM->search('perl');

  # Find a database by name of repository.
  my $coredb = ALPM->db('core');

  # Transactions
  my $trans = ALPM->transaction( type => 'sync' );
  $trans->add( 'perl', 'perl-alpm', 'pacman' );
  #$trans->prepare; # unnecessary, commit calls prepare if needed
  $trans->commit;
  undef $trans;     # to force releasing, now!

=head1 DESCRIPTION

This version of ALPM works with the libalpm included with version 3.30
of pacman.

Archlinux uses a package manager called pacman.  Pacman internally
uses the alpm library for handling its database of packages.  This
module is an attempt at creating a perlish object-oriented interface
to the libalpm C library.

=head1 EXPORT

None.

Because all alpm functions have been converted to class methods,
classes, and object methods, nothing is exported.

=head1 IMPORT OPTIONS

There are a few different options you can specify after C<use ALPM>.
These help to set configuration options for ALPM.  These options are
global for everyone who is using the module.  You can specify either:

=over

=item 1. The path to a pacman.conf configuration file.  Example:

  use ALPM qw( /etc/pacman.conf );

=item 2. A hash of options to use for ALPM.  Example:

  use ALPM qw( root        => '/',
               dbpath      => '/var/lib/pacman/',
               cachedirs   => [ '/var/cache/pacman/pkg' ],
               logfile     => '/var/log/pacman.log' );

=back

It is important to set options as soon as possible.

=head1 ALPM OPTIONS

ALPM has a number of options corresponding to the
C<alpm_option_get_...> and C<alpm_option_set...> C functions in the
library.  Options which take multiple values (hint: they have a plural
name) expect an array reference as an argument.  Similarly the same
options return multiple values as an array reference.

=head2 Read-write options

=over

=item B<root>

root directory of entire system

=item B<dbpath>

path to the ALPM/pacman database

=item B<cachedirs>*

paths containing package caches

=item B<logfile>

path to the pacman logfile

=item B<usesyslog>

if true, log to the system log as well

=item B<noupgrades>*

a list of package names to not upgrade

=item B<noextracts>*

a list of package names to not extract

=item B<ignorepkgs>*

a list of package names to ignore for upgrade

=item B<holdpkgs>*

a list of package names to hold off upgrading

=item B<ignoregrps>*

a list of groups to ignore for upgrade

=item B<xfercommand>

shell command to use for downloading (ie wget)

=item B<nopassiveftp>+

if true, do not use passive ftp mode

=item B<usedelta>+

should you use deltas? not really sure, never tried it...

=back

=head2 Read-only options

=over

=item B<lockfile>

path to the lockfile

=item B<localdb>

the ALPM::DB object representing the local database

=item B<syncdbs>*

an array-ref of sync databases as ALPM::DB objects

=back

  * = the option is set with (and returns) an arrayref
  + = the option is boolean and is either 0 or 1

=head2 Callback options

Callbacks can only be set to code references.

=over

=item B<logcb> - Generic logging

The log message and level are passed to the provided code ref as
arguments.  I<level> can be: error, warning, debug, function, or
unknown.

=item B<dlcb> - Download callback

The filename, bytes transfered, and bytes total are passed to the
provided code ref as arguments.

=item B<totaldlcb> - Total download callback

The total number of bytes downloaded so far is passed to the provided
code ref as the only argument.

=back

=head1 TIED OPTION HASH

This is probably the easiest way to manipulate ALPM's options.  Perl
has an old mechanism which allows reading or writing to hashes act
sort of like objects.  Basically, you can I<tie> ALPM's options to a
hash variable and any changes to that hash change the same option.

  # Taken from SYNOPSIS
  tie my %alpm, 'ALPM';
  $alpm{root} = '/';
  print "Root Dir = $alpm{root}\n";
  my ($root, $dbpath, $cachedir) = @alpm{qw/root dbpath cachedir/};

But you cannot delete a key or empty the hash.  In fact assigning
undef also does not work for options that don't use arrayrefs.

  # Doesn't work!
  delete $alpm{root};
  undef $alpm{root};
  %alpm = ();

=head1 CLASS METHODS

ALPM has all its package specific and database specific functions
inside the package and database classes as methods.  Everything else
is accessed through class methods to ALPM.

As far as I can tell you cannot run multiple instances of libalpm.
Class methods help remind you of this.  The class method notation also
helps to differentiate between globally affecting ALPM functions and
package or database-specific functions.

=head2 set_options

  Params  : Pass set_options a hash or hashref to set many options at
            once.
  Returns : 1

=head2 set_opt

  Usage   : ALPM->set_opt( 'root', '/' );
  Params  : An option name and new option value.
  Returns : 1

=head2 get_options

  Usage   : my %alpm_opts = ALPM->get_options();
            my ($root, $dbpath) = ALPM->get_options( 'root', 'dbpath' );
  Params  : * When no params are given, returns a hash of all options.
            * Otherwise, return a list of option values in the same order
              as the parameters.
  Notes   : Unset options are undefined.
  Returns : A hashref or list.

=head2 get_opt

  Usage   : my $root = ALPM->get_opt('root');
  Params  : An option name.
  Returns : The given option's value or undef if it is unset.

=head2 register_db

  Usage   : my $localdb = ALPM->register_db;
            my $syncdb  = ALPM->register_db
                ( 'core' => 'ftp://ftp.archlinux.org/$repo/os/i686' );
  Params  : No parameters will return the local database.
            Two parameters will register a sync database:
            1) The name of the repository to connect to.
            2) The URL to the repository's online files.
               Like with pacman's mirrorlist config file, $repo will be replaced
               with the repository name (argument 1) ... use single quotes!
  Precond : You must set options before using register_db.
  Throws  : An 'ALPM DB Error: ...' message is croaked on errors.
  Returns : An ALPM::DB object.

You can also use the method C<register> which is a shorter alias for
C<register_db>.

See L<ALPM::DB>

=head2 localdb

  Usage   : my $db = ALPM->localdb;
  Returns : The local system database as an ALPM::DB object.
  Precond : You must set certain options first.
  Notes   : This is what is called by register_db without arguments.

=head2 syncdbs

  Usage   : my @dbs = ALPM->syncdbs;
  Purpose : Nicer looking wrapper to ALPM->get_option('syncdbs');
  Params  : None.
  Returns : A list (not an arrayref) of sync databases.

=head2 db

  Usage   : my $comm_db = ALPM->db('community');
            my $comm_db = ALPM->repodb('community');
  Params  : The name of a repository.
  Returns : An ALPM::DB object matching the repo name.

This is a shorter alias for the method C<repodb>.

=head2 dbs

  Usage   : my @dbs = ALPM->dbs;
            my @dbs = ALPM->databases;
  Params  : None
  Returns : A list of each database that is registered.

This is a shorter alias for the method C<databases>.

See L<ALPM::DB>

=head2 load_pkgfile

  Usage   : my $pkgfile = ALPM->load_pkgfile('perl-alpm-0.2.pkg.tar.gz');
  Params  : The path to a package tarball.
  Returns : An ALPM::Package object.
  Notes   : Technically we return an ALPM::PackageFree object that will
            automatically free itself from memory when it goes out of scope.

See L<ALPM::Package>

=head2 load_config

  Usage   : ALPM->load_config('/etc/pacman.conf');
  Params  : The path to a pacman.conf configuration file.
  Returns : 1

=head2 transaction

  Usage   : my $t = ALPM->transaction( type     => 'sync',
                                       flags    => 'nodeps force',
                                       event    => sub { ... },
                                       conv     => sub { ... },
                                       progress => sub { ... } );
  Purpose : Initializes a transaction
  Params  : A hash of the transaction settings
  Throws  : An 'ALPM Error: ...' is thrown if a transaction is
            already active.
  Returns : An ALPM::Transaction object.

See L<ALPM::Transaction>

=head3 transaction Parameters

Transactions have many named parameters, passed as a hash.

=over 4

=item type - A string, valid types are:

=over 4

=item * upgrade

=item * remove

=item * removeupgrade

=item * sync

=item * sysupgrade

=back

=item flags - A string of flags, each separated by whitespace.

=over 4

=item * nodeps

=item * force

=item * nosave

=item * cascade

=item * recurse

=item * dbonly

=item * alldeps

=item * dlonly

=item * noscriptlet

=item * noconflicts

=item * printuris

=item * needed

=item * allexplicit

=item * unneeded

=item * recurseall

=back

=item event - A coderef, used as an event callback.

When an event occurs, the coderef is passed a hashref representing the
event.

=item conv - A coderef, used as a conversation callback.

This callback is called when a question should be asked to the user.
It is passed a hashref representing the question.  The callback
returns 1 or 0 to answer yes or no.

=item progress - A coderef, used as a progress callback.

This callback is called to report on the progress of the operations
of the transaction.

=back

Callbacks are explained in greater detail in the L<ALPM::Transaction>
document.

=head1 ERRORS

Global ALPM errors are thrown with C<croak>.  The error messages match
the errors that the C library provides.  These errors are prefixed
with C<ALPM Error:>.  Errors that occur when using a ALPM::DB object
are prefixed with C<ALPM DB Error:>.

Transaction errors are prefixed with C<ALPM Transaction Error:>.
These are described fully at L<ALPM::Transaction>.

=head1 TROUBLESHOOTING

TODO: Common error messages and how to fix them?

=head1 TODO

Unimplemented functions:

  alpm_depcmp, alpm_checkdeps, alpm_deptest, alpm_dep_compute_string,
  alpm_trans_interrupt, alpm_checkconflicts, alpm_compute_md5sum,
  alpm_sync_newversion, alpm_delta_... functions

=head1 SEE ALSO

=over

=item * L<ALPM::DB>, L<ALPM::Package>, L<ALPM::Transaction>

=item * L<http://code.toofishes.net/cgit/> - GIT repository for pacman/libalpm

=item * L<http://code.toofishes.net/pacman/doc/> - libalpm doxygen docs

=item * L<pacman>(8)

=back

=head1 AUTHOR

Justin Davis, C<< <juster at cpan dot org> >>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2010 by Justin Davis

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.

=cut
