#!/usr/bin/perl
#
# explain-lintian-tags -- transform lintian tags into descriptive text
#
# Copyright © 1998 Christian Schwarz and Richard Braakman
# Copyright © 2013 Niels Thykier
# Copyright © 2017 Chris Lamb <lamby@debian.org>
# Copyright © 2020 Felix Lechner
#
# This program is free software.  It is distributed under the terms of
# the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any
# later version.
#
# This program 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 this program.  If not, you can find it on the World Wide
# Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.

use v5.20;
use warnings;
use utf8;

# use Lintian modules that belong to this program
use FindBin;
use lib "$FindBin::RealBin/../lib";

# substituted during package build
my $LINTIAN_VERSION;

use Cwd qw(getcwd realpath);
use File::BaseDir qw(config_home config_files data_home);
use File::Basename;
use Getopt::Long ();
use List::MoreUtils qw(any);

use Lintian::Output::EWI;
use Lintian::Output::HTML;
use Lintian::Output::JSON;
use Lintian::Profile;
use Lintian::Version qw(guess_version);

use constant EMPTY => q{};
use constant SPACE => q{ };
use constant COLON => q{:};
use constant NEWLINE => qq{\n};

binmode(STDOUT, ':encoding(UTF-8)');

if (my $coverage_arg = $ENV{'LINTIAN_COVERAGE'}) {
    my $p5opt = $ENV{'PERL5OPT'}//EMPTY;
    $p5opt .= ' ' if $p5opt ne EMPTY;
    $ENV{'PERL5OPT'} = "${p5opt} ${coverage_arg}";
}

$ENV{LINTIAN_BASE} = realpath("$FindBin::RealBin/..")
  // die 'Cannot resolve LINTIAN_BASE';

my $format = 'ewi';
my @INCLUDE_DIRS;
my $list_tags;
my $profile_name;
my $tags;
my $user_dirs = 1;

my %options = (
    'format|f=s' => \$format,
    'help|h' => \&show_help,
    'include-dir=s' => \@INCLUDE_DIRS,
    'list-tags|l' => \$list_tags,
    'profile=s' => \$profile_name,
    'tags|tag|t' => \$tags,
    'user-dirs!' => \$user_dirs,
    'version' => \&show_version,
);

Getopt::Long::Configure('gnu_getopt');
Getopt::Long::GetOptions(%options)
  or die "error parsing options\n";

# only absolute paths
my @RESTRICTED_CONFIG_DIRS;

if ($user_dirs) {
    my $data_home;
    my $legacy_user_data;

    $data_home = data_home('lintian')
      if exists $ENV{'HOME'} || exists $ENV{'XDG_CONFIG_HOME'};

    $legacy_user_data = "$ENV{HOME}/.lintian"
      if exists $ENV{'HOME'};

    if (defined($data_home) and $data_home !~ m@^/@) {
        # Turn the path into an absolute one.  Just in case
        # someone sets a relative HOME dir.
        my $cwd = getcwd();
        $data_home = "${cwd}/${data_home}";
    }

    @RESTRICTED_CONFIG_DIRS = grep { -d }
      grep { length } ($data_home, $legacy_user_data, '/etc/lintian');
}

# only absolute paths
my @CONFIG_DIRS = grep { -d }
  grep { length } map { realpath($_) } ($ENV{'LINTIAN_BASE'}, @INCLUDE_DIRS);

my $profile = Lintian::Profile->new;
$profile->load($profile_name, \@CONFIG_DIRS,
    { 'restricted-search-dirs' => \@RESTRICTED_CONFIG_DIRS });

my $output;

$format = lc $format;
if ($format eq 'ewi') {
    $output = Lintian::Output::EWI->new;

} elsif ($format eq 'json') {
    $output = Lintian::Output::JSON->new;

} elsif ($format eq 'html') {
    $output = Lintian::Output::HTML->new;

} else {
    die "Invalid output format $format\n";
}

if ($list_tags) {
    say for sort { lc($a) cmp lc($b) } $profile->enabled_tags;
    exit;
}

# show all tags when none were specified
my @selected = @ARGV;
@selected = $profile->enabled_tags
  unless @selected;

my @available = grep { defined} map { $profile->get_tag($_) } @selected;

my @sorted = sort { lc($a->name) cmp lc($b->name) } @available;

$output->describe_tags(@sorted);

exit any { !defined $profile->get_tag($_) } @selected;

sub show_version {
    my $version = $LINTIAN_VERSION // guess_version($ENV{LINTIAN_BASE});

    die 'Unable to determine the version automatically!?'
      unless length $version;

    say "annotate-lintian-hints v$version";
    exit;
}

sub show_help {
    print <<"EOT";
Usage: explain-lintian-tags [log-file...] ...
       explain-lintian-tags [--tags] tag ...

Options:
    -l, --list-tags    list all tags Lintian knows about
    -t, --tag, --tags  display tag descriptions
    --profile X        use vendor profile X to determine severities
    --include-dir DIR  check for Lintian data in DIR
    --[no-]user-dirs   whether to include profiles from user directories
    --version          show version info and exit
EOT
    exit;
}

# Local Variables:
# indent-tabs-mode: nil
# cperl-indent-level: 4
# End:
# vim: syntax=perl sw=4 sts=4 sr et
