#!/usr/bin/perl -w

# Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2015 Kevin Ryde
#
# This file is part of apt-reverse.
#
# apt-reverse is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later
# version.
#
# apt-reverse is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with apt-reverse.  If not, see <http://www.gnu.org/licenses/>.

use 5.004; # or AptPkg itself is 5.005_62 or some such
use strict;
use AptPkg::Config;
use AptPkg::Cache;
use Getopt::Long;

use vars '$VERSION';
$VERSION = 5;

# uncomment this to run the ### lines
# use Smart::Comments;

my $option_all = 0;
my $option_descriptions = 0;
my $option_installed = 0;
my $option_version = 0;

GetOptions('all|a'          => \$option_all,
           'descriptions|d' => \$option_descriptions,
           'installed|i'    => \$option_installed,
           'version'        => sub {
  print "apt-reverse version $main::VERSION\n";
  exit 0;
},
           'help|?' => sub {
  print "apt-reverse [--options] package...\n";
  print "   -a, --all           show all usages, meaning Suggests and Recommends too\n";
  print "   -d, --descriptions  print descriptions of depending packages\n";
  print "   -i, --installed     print only installed depending packages\n";
  print "   --help              print this help\n";
  print "   --version           print program version number\n";
  exit 0;
}) or exit 1;

unless (@ARGV) {
  print "Usage: apt-reverse [--options] package...\n";
  print "run \"apt-reverse --help\" for a summary of the options\n";
  exit 1;
}

{
  ## no critic (ProtectPrivateVars)
  $AptPkg::Config::_config->init;
  $AptPkg::Config::_config->{'quiet'} = 2;   # supress cache building messages
  $AptPkg::System::_system = $AptPkg::Config::_config->system;
}
my $cache = AptPkg::Cache->new;      # AptPkg::Cache
my $pkgrecords = $cache->packages;   # AptPkg::PkgRecords

for my $pack (@ARGV)
{
  my $p = $cache->{$pack};     # AptPkg::Cache::Package
  unless ($p) {
    warn "apt-reverse: don't know anything about package: $pack\n";
    next;
  }

  my $revdeps = $p->{'RevDependsList'};
  if (! $revdeps) {
    # print "  (no reverse depends)\n";
    next;
  }

  my @revdeps = sort
    {$a->{'ParentPkg'}{'Name'} cmp $b->{'ParentPkg'}{'Name'}} @$revdeps;

  my $prev_parent = '';
  for my $r (@revdeps) {
    my $parent = $r->{'ParentPkg'}{'Name'};

    # avoid duplicate versions of the same package
    next if ($parent eq $prev_parent);
    $prev_parent = $parent;

    if ($option_installed) {
      my $pp = $cache->{$parent};
      next if (! defined $pp->{'CurrentVer'});
    }

    my $type;
    if ($r->{'DepType'} eq 'Depends') {
      $type = '';
    } elsif (! $option_all) {
      next;
    } elsif ($r->{'DepType'} eq 'Conflicts'
             || $r->{'DepType'} eq 'Replaces'
             || $r->{'DepType'} eq 'Suggests') {
      next;
    } elsif (defined ($r->{'TargetVer'})) {
      $type = " ($r->{'DepType'} $r->{'CompType'} $r->{'TargetVer'})";
    } else {
      $type = " ($r->{'DepType'})";
    }
    my $pr = $cache->packages()->lookup($parent);

    my $str = $pr->{'ShortDesc'};
    if ($option_descriptions) {
      my $href = $pkgrecords->lookup($parent);
      ### $href
      if ($href && $href->{'LongDesc'}) {
        $str = $href->{'LongDesc'};
      }
    }
    print "$parent$type - $str\n";
  }
}

exit 0;

__END__

=for stopwords ie gnuplot AptPkg Ryde online

=head1 NAME

apt-reverse -- list packages which depend on (ie. use) a given package

=head1 SYNOPSIS

 apt-reverse [--options] package...

=head1 DESCRIPTION

C<apt-reverse> lists all the packages which depend on those given on the
command line.  These are the "reverse dependencies".  For example

    apt-reverse gnuplot

might print

    battery-stats - Collects statistics about charge of laptop batteries
    feedgnuplot - Pipe-oriented frontend to Gnuplot
    gausssum - parse and display Gaussian, GAMESS, and etc's output
    libgraphics-gnuplotif-perl - dynamic Perl interface to gnuplot

which means each of those packages needs gnuplot.  Options below allow
"Suggests" and "Recommends" to be included too, or restrict to currently
installed packages.

This can find programming language interfaces to something (like the Perl
above).  Or before deleting a package you might see what uses it, so keep it
if there's something of future interest which will need it.

=for mynotes -- patched here to /etc/bash_completion.d/apt-reverse for deb

An F<apt-reverse.bash> is included with C<apt-reverse> for Bash tab
completion on package names and program options.  It can be used with or
without the "bash_completion" system.

=head1 OPTIONS

The command line options are

=over 4

=item -a, --all

Show all uses, which means Recommends and Suggests as well as plain Depends.

=item -d, --description

Print the long description of each package.  This will require the usual apt
"Translations" files.  If apt has no translations (Languages "none" in
F<apt.conf>) then the long descriptions are empty.

The default is the one-line short description.

=item -i, --installed

Print only currently installed packages, not all in the database.

=item --help

Print some brief help information.

=item --version

Print the program version number and exit.

=back

=head1 PREREQUISITES

=over 4

=item *

AptPkg -- Perl interface to the apt database (Debian package
C<libapt-pkg-perl>)

=back

=head1 OTHER WAYS TO DO IT

C<apt-cache showpkg> gives the same information, but without the
descriptions and not in the simple format of C<apt-reverse>.

C<apt-rdepends> can do the same too, though again without the descriptions,
and oriented towards recursive "depends of depends" tracing.

=head1 SEE ALSO

=for mynotes -- patched here to /etc/bash_completion.d/apt-reverse for deb

L<AptPkg>, L<apt-cache(8)>, L<apt-rdepends(8)>, F<apt-reverse.bash>

=head1 HOME PAGE

http://user42.tuxfamily.org/apt-reverse/index.html

=head1 LICENSE

Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2015 Kevin Ryde

apt-reverse is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

apt-reverse is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along
with apt-reverse.  If not, see <http://www.gnu.org/licenses/>.

=cut
