# 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 Lintian::Collect;
use Tags;

sub run {

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

if (! -f "debfiles/watch") {
    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 ($version =~ /(dfsg|debian|ds)/) {
    $repack = $1;
}

# Gather information from the watch file and look for problems we can
# diagnose on the first time through.
open(WATCH, '<', 'debfiles/watch') or fail("cannot open watch file: $!");
local $_;
my ($watchver, %dversions);
while (<WATCH>) {
    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 ($mangle, $dmangle) = (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) {
                for (@opts) {
                    $mangle = 1 if /^[ud]?versionmangle\s*=.*($repack)/;
                    $dmangle = 1 if /^dversionmangle\s*=.*($repack)/;
                }
            }
        }
        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_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_mangling and not $mangle) {
            tag 'debian-watch-file-should-mangle-version', "line $.";
        }
        if ($needs_mangling and $mangle and not $dmangle) {
            tag 'debian-watch-file-should-dversionmangle-not-uversionmangle', "line $.";
        }
    }
}
close WATCH;

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 ts=4 et shiftround
