# watch-file -- lintian check script -*- perl -*-
#
# Copyright (C) 2008 Patrick Schoenfeld
# Copyright (C) 2008 Russ Allbery
# Copyright (C) 2008 Raphael Geissert
#
# This program 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 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.

package Lintian::watch_file;
use strict;
use warnings;

use Lintian::Collect;
use Lintian::Tags qw(tag);

use Lintian::Util qw(fail);

sub run {

my $pkg = shift;
my $type = shift;
my $info = shift;

my $template = 0;
my $wfile = $info->debfiles('watch');

if (! -f $wfile) {
    tag 'debian-watch-file-is-missing' unless ($info->native);
    return;
}

# Perform the other checks even if it is a native package
tag 'debian-watch-file-in-native-package' if ($info->native);

# Check if the Debian version contains anything that resembles a repackaged
# source package sign, for fine grained version mangling check
my $version = $info->field('version');
my $repack;
# If the version field is missing, we assume a neutral non-native one.
$version = '0-1' unless defined $version;
if ($version =~ /(dfsg|debian|ds)/) {
    $repack = $1;
}
my $prerelease;
if ($version =~ /(alpha|beta|rc)/i) {
    $prerelease = $1;
}

# Gather information from the watch file and look for problems we can
# diagnose on the first time through.
open(WATCH, '<', $wfile) or fail("cannot open watch file: $!");
local $_;
my ($watchver, %dversions);
while (<WATCH>) {
    $template = 1 if m/^\s*\#\s*Example watch control file for uscan/io;
    next if /^\s*\#/;
    next if /^\s*$/;
    s/^\s*//;

  CHOMP:
    chomp;
    if (s/(?<!\\)\\$//) {
        # This is caught by uscan.
        last if eof(WATCH);
        $_ .= <WATCH>;
        goto CHOMP;
    }

    if (/^version\s*=\s*(\d+)(?:\s|\Z)/) {
        if (defined $watchver) {
            tag 'debian-watch-file-declares-multiple-versions', "line $.";
        }
        $watchver = $1;
        if ($watchver ne '2' and $watchver ne '3') {
            tag 'debian-watch-file-unknown-version', $watchver;
        }
    } else {
        unless (defined($watchver)) {
            tag 'debian-watch-file-missing-version';
            $watchver = 1;
        }
        # Version 1 watch files are too broken to try checking them.
        next if ($watchver == 1);

        my ($repack_mangle, $repack_dmangle, $prerelease_mangle, $prerelease_umangle) = (0, 0, 0, 0);
        my ($opts, @opts);
        if (s/^opt(?:ion)?s=\"([^\"]+)\"\s+// || s/^opt(?:ion)?s=(\S+)\s+//) {
            $opts = $1;
            @opts = split(',', $opts);
            if (defined $repack or defined $prerelease) {
                for (@opts) {
                    $repack_mangle = 1 if defined $repack and /^[ud]?versionmangle\s*=.*$repack/;
                    $repack_dmangle = 1 if defined $repack and /^dversionmangle\s*=.*$repack/;
                    $prerelease_mangle = 1 if defined $prerelease and /^[ud]?versionmangle\s*=.*$prerelease/;
                    $prerelease_umangle = 1 if defined $prerelease and /^uversionmangle\s*=.*$prerelease/;
                }
            }
        }
        if (m%qa\.debian\.org/watch/sf\.php\?%) {
            tag 'debian-watch-file-uses-deprecated-sf-redirector-method',
                "line $.";
        }

        if (m%(?:https?|ftp)://(?:(?:.+\.)?dl|(?:pr)?downloads?|ftp\d?|upload)\.(?:sourceforge|sf)\.net%
            or m%https?://(?:www\.)?(?:sourceforge|sf)\.net/project/showfiles\.php%
            or m%https?://(?:www\.)?(?:sourceforge|sf)\.net/projects/.+/files%) {
            tag 'debian-watch-file-should-use-sf-redirector', "line $.";
        }

        # This bit is as-is from uscan.pl:
        my ($base, $filepattern, $lastversion, $action) = split ' ', $_, 4;
        if ($base =~ s%/([^/]*\([^/]*\)[^/]*)$%/%) {
            # Last component of $base has a pair of parentheses, so no
            # separate filepattern field; we remove the filepattern from the
            # end of $base and rescan the rest of the line
            $filepattern = $1;
            (undef, $lastversion, $action) = split ' ', $_, 3;
        }
        push @{$dversions{$lastversion}}, $. if (defined($lastversion));
        $lastversion = 'debian' unless (defined($lastversion));

        my $needs_repack_mangling = ($repack and $lastversion eq 'debian');
        # If the version of the package contains dfsg, assume that it needs
        # to be mangled to get reasonable matches with upstream.
        if ($needs_repack_mangling and not $repack_mangle) {
            tag 'debian-watch-file-should-mangle-version', "line $.";
        }
        if ($needs_repack_mangling and $repack_mangle and not $repack_dmangle) {
            tag 'debian-watch-file-should-dversionmangle-not-uversionmangle', "line $.";
        }

        my $needs_prerelease_mangling = ($prerelease and $lastversion eq 'debian');
        if ($needs_prerelease_mangling and $prerelease_mangle and not $prerelease_umangle) {
            tag 'debian-watch-file-should-uversionmangle-not-dversionmangle', "line $.";
        }
    }
}
close WATCH;

tag 'debian-watch-contains-dh_make-template' if ($template);

my $changes = $info->changelog;
if (defined $changes and %dversions) {
    my $data = $changes->data;
    my %changelog_versions;
    my $count = 1;
    for my $entry (@{$data}) {
        my $uversion = $entry->Version;
        $uversion =~ s/-[^-]+$//; # revision
        $uversion =~ s/^\d+://; # epoch
        $changelog_versions{'orig'}{$entry->Version} = $count;

        # Preserve the first value here to correctly detect old versions.
        $changelog_versions{'mangled'}{$uversion} = $count
            unless (exists($changelog_versions{'mangled'}{$uversion}));
        $count++;
    }

    while (my ($dversion, $lines) = each %dversions) {
        next if (!defined($dversion) || $dversion eq 'debian');
        local $" = ', ';
        if (!$info->native && exists($changelog_versions{'orig'}{$dversion})) {
            tag 'debian-watch-file-specifies-wrong-upstream-version',
                $dversion, "line @{$lines}";
            next;
        }
        if (exists($changelog_versions{'mangled'}{$dversion})
            && $changelog_versions{'mangled'}{$dversion} != 1) {
            tag 'debian-watch-file-specifies-old-upstream-version',
                $dversion, "line @{$lines}";
            next;
        }
    }
}

}

1;

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