Added lots more modules from lintian. Maemian appears to work.
[maemian] / checks / fields
diff --git a/checks/fields b/checks/fields
new file mode 100644 (file)
index 0000000..571d664
--- /dev/null
@@ -0,0 +1,1119 @@
+# fields -- lintian check script (rewrite) -*- perl -*-
+#
+# Copyright (C) 2004 Marc Brockschmidt
+#
+# Parts of the code were taken from the old check script, which
+# was Copyright (C) 1998 Richard Braakman (also licensed under the
+# GPL 2 or higher)
+#
+# 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 Maemian::fields;
+use strict;
+
+use lib "$ENV{'MAEMIAN_ROOT'}/checks/";
+use common_data;
+use Tags;
+use Util;
+
+use Maemian::Data ();
+use Maemian::Check qw(check_maintainer);
+use Maemian::Relation ();
+use Maemian::Relation::Version qw(versions_compare);
+
+our $KNOWN_ARCHS = Maemian::Data->new ('fields/architectures');
+
+our %known_archive_parts = map { $_ => 1 }
+    ('non-free', 'contrib');
+
+our %known_sections = map { $_ => 1 }
+    ('admin', 'comm', 'cli-mono', 'database', 'debug', 'devel', 'doc',
+     'editors', 'electronics', 'embedded', 'fonts', 'games', 'gnome', 'gnu-r',
+     'gnustep', 'graphics', 'hamradio', 'haskell', 'httpd', 'interpreters',
+     'java', 'kde', 'libdevel', 'libs', 'lisp', 'localization', 'kernel', 'mail',
+     'math', 'misc', 'net', 'news', 'ocaml', 'oldlibs', 'otherosfs', 'perl',
+     'php', 'python', 'ruby', 'science', 'shells', 'sound', 'tex', 'text',
+     'utils', 'vcs', 'video', 'web', 'x11', 'xfce', 'zope'
+    );
+
+our %known_prios = map { $_ => 1 }
+    ('required', 'important', 'standard', 'optional', 'extra');
+
+# The Ubuntu original-maintainer field is handled separately.
+our %known_binary_fields = map { $_ => 1 }
+    ('package', 'version', 'architecture', 'depends', 'pre-depends',
+     'recommends', 'suggests', 'enhances', 'conflicts', 'provides',
+     'replaces', 'breaks', 'essential', 'maintainer', 'section', 'priority',
+     'source', 'description', 'installed-size', 'python-version', 'homepage',
+     'bugs', 'origin');
+
+# The Ubuntu original-maintainer field is handled separately.
+our %known_udeb_fields = map { $_ => 1 }
+    ('package', 'version', 'architecture', 'subarchitecture', 'depends',
+     'recommends', 'enhances', 'provides', 'replaces', 'breaks', 'replaces',
+     'maintainer', 'section', 'priority', 'source', 'description',
+     'installed-size', 'kernel-version', 'installer-menu-item', 'bugs',
+     'origin');
+
+our %known_obsolete_fields = map { $_ => 1 }
+    ('revision', 'package-revision', 'package_revision',
+     'recommended', 'optional', 'class');
+
+our %known_build_essential = map { $_ => 1 }
+    ('libc6-dev', 'libc-dev', 'gcc', 'g++', 'make', 'dpkg-dev');
+
+# Still in the archive but shouldn't be the primary Emacs dependency.
+our %known_obsolete_emacs = map { $_ => 1 }
+    ('emacs21');
+
+our %known_libstdcs = map { $_ => 1 }
+    ('libstdc++2.9-glibc2.1', 'libstdc++2.10', 'libstdc++2.10-glibc2.2',
+     'libstdc++3', 'libstdc++3.0', 'libstdc++4', 'libstdc++5',
+     'libstdc++6', 'lib64stdc++6',
+    );
+
+our %known_tcls = map { $_ => 1 }
+    ( 'tcl74', 'tcl8.0', 'tcl8.2', 'tcl8.3', 'tcl8.4', 'tcl8.5', );
+
+our %known_tclxs = map { $_ => 1 }
+    ( 'tclx76', 'tclx8.0.4', 'tclx8.2', 'tclx8.3', 'tclx8.4', );
+
+our %known_tks = map { $_ => 1 }
+    ( 'tk40', 'tk8.0', 'tk8.2', 'tk8.3', 'tk8.4', 'tk8.5', );
+
+our %known_tkxs = map { $_ => 1 }
+    ( 'tkx8.2', 'tkx8.3', );
+
+our %known_libpngs = map { $_ => 1 }
+    ( 'libpng12-0', 'libpng2', 'libpng3', );
+
+our %known_x_metapackages = map { $_ => 1 }
+    ( 'x-window-system', 'x-window-system-dev', 'x-window-system-core',
+      'xorg', 'xorg-dev', );
+
+# The allowed Python dependencies currently.  This is the list of alternatives
+# that, either directly or through transitive dependencies that can be relied
+# upon, ensure /usr/bin/python will exist for the use of dh_python.
+our $PYTHON_DEPEND = 'python | python-dev | python-all | python-all-dev | '
+    . join (' | ', map { "python$_ | python$_-dev" } qw(2.4 2.5));
+
+# Certain build tools must be listed in Build-Depends even if there are no
+# arch-specific packages because they're required in order to run the clean
+# rule.  (See Policy 7.6.)  The following is a list of package dependencies;
+# regular expressions that, if they match anywhere in the debian/rules file,
+# say that this package is allowed (and required) in Build-Depends; and
+# optional tags to use for reporting the problem if some information other
+# than the default is required.
+our @GLOBAL_CLEAN_DEPENDS = (
+       [ ant => qr'^include\s*/usr/share/cdbs/1/rules/ant\.mk' ],
+       [ cdbs => qr'^include\s+/usr/share/cdbs/' ],
+       [ dbs => qr'^include\s+/usr/share/dbs/' ],
+       [ 'dh-make-php' => qr'^include\s+/usr/share/cdbs/1/class/pear\.mk' ],
+       [ debhelper => qr'^include\s+/usr/share/cdbs/1/rules/debhelper\.mk' ],
+       [ dpatch => qr'^include\s+/usr/share/cdbs/1/rules/dpatch\.mk' ],
+       [ quilt => qr'^include\s+/usr/share/cdbs/1/rules/patchsys-quilt\.mk' ],
+       [ dpatch => qr'^include\s+/usr/share/dpatch/' ],
+       [ quilt => qr'^include\s+/usr/share/quilt/' ],
+       [ $PYTHON_DEPEND => qr'/usr/share/cdbs/1/class/python-distutils\.mk', 'missing-python-build-dependency' ],
+);
+
+# These are similar, but the resulting dependency is only allowed, not
+# required.
+#
+# The xsfclean rule is specific to the packages maintained by the X Strike
+# Force, but there are enough of those to make the rule worthwhile.
+my @GLOBAL_CLEAN_ALLOWED = (
+       [ patchutils => qr'^include\s+/usr/share/cdbs/1/rules/dpatch\.mk' ],
+       [ patchutils => qr'^include\s+/usr/share/cdbs/1/rules/patchsys-quilt\.mk' ],
+       [ patchutils => qr'^include\s+/usr/share/cdbs/1/rules/simple-patchsys\.mk' ],
+       [ 'python-central' => qr'^DEB_PYTHON_SYSTEM\s*:?=\s*pycentral' ],
+       [ 'python-support' => qr'^DEB_PYTHON_SYSTEM\s*:?=\s*pysupport' ],
+       [ 'python-setuptools' => qr'/usr/share/cdbs/1/class/python-distutils\.mk' ],
+       [ quilt => qr'^clean:\s+xsfclean\b' ],
+);
+
+# A list of packages; regular expressions that, if they match anywhere in the
+# debian/rules file, this package must be listed in either Build-Depends or
+# Build-Depends-Indep as appropriate; and optional tags as above.
+my @GLOBAL_DEPENDS = (
+       [ $PYTHON_DEPEND => qr'^\t\s*dh_python\s', 'missing-dh_python-build-dependency' ],
+       [ 'python-central' => qr'^\t\s*dh_pycentral\s' ],
+       [ 'python-support' => qr'^\t\s*dh_pysupport\s' ],
+       [ 'python-central' => qr'^DEB_PYTHON_SYSTEM\s*:?=\s*pycentral' ],
+       [ 'python-support' => qr'^DEB_PYTHON_SYSTEM\s*:?=\s*pysupport' ],
+);
+
+# Similarly, this list of packages, regexes, and optional tags say that if the
+# regex matches in one of clean, build-arch, binary-arch, or a rule they
+# depend on, this package is allowed (and required) in Build-Depends.
+my @RULE_CLEAN_DEPENDS = (
+       [ ant => qr'^\t\s*ant\s' ],
+       [ debhelper => qr'^\t\s*dh_.+' ],
+       [ dpatch => qr'^\t\s*dpatch\s' ],
+       [ "po-debconf" => qr'^\t\s*debconf-updatepo\s' ],
+       [ $PYTHON_DEPEND => qr'^\t\s*python\s', 'missing-python-build-dependency' ],
+       [ $PYTHON_DEPEND => qr'\ssetup\.py\b', 'missing-python-build-dependency' ],
+       [ quilt => qr'^\t\s*(\S+=\S+\s+)*quilt\s' ],
+       [ yada => qr'^\t\s*yada\s' ],
+);
+
+# Similar, but the resulting dependency is only allowed, not required.  We
+# permit a versioned dependency on perl-base because that used to be the
+# standard suggested dependency.  No package should be depending on just
+# perl-base, since it's Priority: required.
+my @RULE_CLEAN_ALLOWED = (
+       [ patch => q'^\t\s*(?:perl debian/)?yada\s+unpatch' ],
+       [ 'perl | perl-base (>= 5.6.0-16)' => qr'(^\t|\|\|)\s*(perl|\$\(PERL\))\s' ],
+       [ 'perl-modules (>= 5.10) | libmodule-build-perl' => qr'(^\t|\|\|)\s*(perl|\$\(PERL\))\s+Build\b' ],
+       [ 'python-setuptools' => qr'\ssetup\.py\b' ],
+);
+
+# A simple list of regular expressions which, if they match anywhere in
+# debian/rules, indicate the requirements for debian/rules clean are complex
+# enough that we can't know what packages are permitted in Build-Depends and
+# should bypass the build-depends-without-arch-dep check completely.
+my @GLOBAL_CLEAN_BYPASS = (
+       qr'^include\s*/usr/share/cdbs/1/class/ant\.mk',
+       qr'^\s+dh\s+'
+);
+
+# Mapping of package names to section names
+my @NAME_SECTION_MAPPINGS = (
+    [ qr/-docs?$/      => 'doc'      ],
+    [ qr/-dbg$/        => 'debug'    ],
+    [ qr/^python-/     => 'python'   ],
+    [ qr/^r-cran-/     => 'gnu-r'    ],
+    [ qr/^lib.*-perl$/ => 'perl'     ],
+    [ qr/^lib.*-cil$/  => 'cli-mono' ],
+    [ qr/^lib.*-java$/ => 'java'     ],
+    [ qr/^(?:lib)php-/ => 'php'      ],
+    [ qr/^lib(?:hugs|ghc6)-/ => 'haskell'   ],
+    [ qr/^lib.*-ruby(?:1\.\d)?$/ => 'ruby'  ],
+    [ qr/^lib.*-ocaml-dev$/ => 'ocaml'      ],
+    [ qr/^lib.*-dev$/  => 'libdevel' ],
+);
+
+# Valid URI formats for the Vcs-* fields
+# currently only checks the protocol, not the actual format of the URI
+my %VCS_RECOMMENDED_URIS = (
+    browser => qr;^https?://;,
+    arch    => qr;^https?://;,
+    bzr     => qr;^(lp:~|(?:nosmart\+)?https?://);,
+    cvs     => qr;^:pserver:;,
+    darcs   => qr;^https?://;,
+    hg      => qr;^https?://;,
+    git     => qr;^(?:git|https?|rsync)://;,
+    svn     => qr;^(?:svn|(?:svn\+)?https?)://;,
+    mtn     => qr;^[\w.-]+\s+\S+;, # that's a hostname followed by a module name
+);
+my %VCS_VALID_URIS = (
+    arch    => qr;^https?://;,
+    bzr     => qr;^(?:sftp|(?:bzr\+)?ssh)://;,
+    cvs     => qr;^(?:-d\s*)?:(?:ext|pserver):;,
+    git     => qr;^(?:git\+)?ssh://;,
+    svn     => qr;^(?:svn\+)?ssh://;,
+);
+
+our $PERL_CORE_PROVIDES = Maemian::Data->new('fields/perl-provides', '\s+');
+our $OBSOLETE_PACKAGES  = Maemian::Data->new('fields/obsolete-packages');
+our $VIRTUAL_PACKAGES   = Maemian::Data->new('fields/virtual-packages');
+
+sub run {
+
+my $pkg = shift;
+my $type = shift;
+my $info = shift;
+my $version;
+my $arch_indep;
+
+unless (-d "fields") {
+       fail("directory in lintian laboratory for $type package $pkg missing: fields");
+}
+
+#---- Format
+
+if ($type eq 'source') {
+       my $format = $info->field('format');
+       if (defined($format) and $format !~ /^\s*1\.0\s*\z/) {
+               tag 'unsupported-source-format', $format;
+       }
+}
+
+#---- Package
+
+if ($type eq "binary"){
+       if (not defined $info->field('package')) {
+               tag "no-package-name", "";
+       } else {
+               my $name = $info->field('package');
+
+               unfold("package", \$name);
+               tag "bad-package-name", "" unless $name =~ /^[A-Z0-9][-+\.A-Z0-9]+$/i;
+               tag "package-not-lowercase", "" if ($name =~ /[A-Z]/)
+       }
+}
+
+#---- Version
+
+if (not defined $info->field('version')) {
+       tag "no-version-field", "";
+} else {
+       $version = $info->field('version');
+
+       unfold("version", \$version);
+
+       if (@_ = _valid_version($version)) {
+               my ($epoch, $upstream, $debian) = @_;
+               if ($upstream !~ /^\d/i) {
+                       tag "upstream-version-not-numeric", "$version";
+               }
+               if (defined $debian) {
+                       tag "debian-revision-should-not-be-zero", "$version"
+                               if $debian eq '-0';
+                       my $ubuntu;
+                       $debian =~ /^-([^.]+)(?:\.[^.]+)?(?:\.[^.]+)?(\..*)?$/;
+                       my $extra = $2;
+                       if (defined $extra) {
+                               $debian =~ /^-([^.]+ubuntu[^.]+)(?:\.\d+){1,3}(\..*)?$/;
+                               $ubuntu = 1;
+                               $extra = $2;
+                       }
+                       if (not defined $1 or defined $extra) {
+                               tag "debian-revision-not-well-formed", "$version";
+                       }
+                       if ($debian =~ /^-[^.-]+\.[^.-]+\./ and not $ubuntu) {
+                               tag "binary-nmu-uses-old-version-style", "$version"
+                                       if $type eq 'binary';
+                               tag "binary-nmu-debian-revision-in-source", "$version"
+                                       if $type eq 'source';
+                       }
+               }
+               if ($version =~ /\+b\d+$/ && $type eq "source") {
+                       tag "binary-nmu-debian-revision-in-source", "$version";
+               }
+
+               # Checks for the dfsg convention for repackaged upstream
+               # source.  Only check these against the source package to not
+               # repeat ourselves too much.
+               if ($type eq 'source') {
+                       if ($version =~ /dfsg/ and $info->native) {
+                               tag 'dfsg-version-in-native-package', $version;
+                       } elsif ($version =~ /\.dfsg/) {
+                               tag 'dfsg-version-with-period', $version;
+                       } elsif ($version =~ /dsfg/) {
+                               tag 'dfsg-version-misspelled', $version;
+                       }
+               }
+
+               my $name = $info->field('package');
+               if ($name && $PERL_CORE_PROVIDES->known($name) &&
+                   perl_core_has_version($name, '>=', $upstream)) {
+                       my $core_version = $PERL_CORE_PROVIDES->value($name);
+                       tag "package-superseded-by-perl", "with $core_version"
+               }
+       } else {
+               tag "bad-version-number", "$version";
+       }
+}
+
+#---- Architecture
+
+if (not defined $info->field('architecture')) {
+       tag "no-architecture-field", "";
+} else {
+       my $archs = $info->field('architecture');
+
+       unfold("architecture", \$archs);
+
+       my @archs = split / /, $archs;
+
+       if (@archs > 1 && grep { $_ eq "any" || ($type ne "source" && $_ eq "all") } @archs) {
+               tag "magic-arch-in-arch-list", "";
+       }
+
+       for my $arch (@archs) {
+               tag "unknown-architecture", "$arch" unless $KNOWN_ARCHS->known($arch);
+       }
+
+       if ($type eq "binary") {
+               tag "too-many-architectures", "" if (@archs > 1);
+               tag "arch-any-in-binary-pkg", "" if (grep { $_ eq "any" } @archs);
+                tag "aspell-package-not-arch-all", ""
+                    if ($pkg =~ /^aspell-[a-z]{2}(-.*)?$/ && (@archs > 1 || $archs[0] ne 'all'));
+       }
+
+       # Used for later tests.
+       $arch_indep = 1 if (@archs == 1 && $archs[0] eq 'all');
+}
+
+#---- Subarchitecture (udeb)
+
+if (defined $info->field('subarchitecture')) {
+       my $subarch = $info->field('subarchitecture');
+
+       unfold("subarchitecture", \$subarch);
+}
+
+#---- Maintainer
+#---- Uploaders
+
+for my $f (qw(maintainer uploaders)) {
+       if (not defined $info->field($f)) {
+               tag "no-maintainer-field", "" if $f eq "maintainer";
+       } else {
+               my $maintainer = $info->field($f);
+
+               # Note, not expected to hit on uploaders anymore, as dpkg now strips
+               # newlines for the .dsc, and the newlines don't hurt in debian/control
+               unfold($f, \$maintainer);
+
+               if ($f eq "uploaders") {
+                       my @uploaders = split /\s*,\s*/, $maintainer;
+                       my %duplicate_uploaders;
+                       for my $uploader (@uploaders) {
+                               check_maintainer($uploader, "uploader");
+                               if ( ((grep { $_ eq $uploader } @uploaders) > 1) and
+                                    ($duplicate_uploaders{$uploader}++ == 0)) {
+                                       tag 'duplicate-uploader', $uploader;
+                               }
+                       }
+               } else {
+                       check_maintainer($maintainer, $f);
+                       if ($type eq 'source'
+                           && $maintainer =~ /\@lists(\.alioth)?\.debian\.org\b/
+                           && ! defined $info->field('uploaders')) {
+                               tag 'no-human-maintainers';
+                       }
+               }
+       }
+}
+
+if (defined $info->field('uploaders') && defined $info->field('maintainer')) {
+       my $maint = $info->field('maintainer');
+       tag 'maintainer-also-in-uploaders'
+               if $info->field('uploaders') =~ m/\Q$maint/;
+}
+
+#---- Source
+
+if (not defined $info->field('source')) {
+       tag "no-source-field" if $type eq "source";
+} else {
+       my $source = $info->field('source');
+
+       unfold("source", \$source);
+
+       if ($type eq 'source') {
+               if ($source ne $pkg) {
+                       tag "source-field-does-not-match-pkg-name", "$source != $pkg";
+               }
+       } else {
+               if ($source !~ /[A-Z0-9][-+\.A-Z0-9]+                      #Package name
+                               \s*
+                               (?:\((?:\d+:)?(?:[-\.+:A-Z0-9~]+?)(?:-[\.+A-Z0-9~]+)?\))?\s*$/ix) { #Version
+                       tag "source-field-malformed", "$source";
+               }
+       }
+}
+
+#---- Essential
+
+if (defined $info->field('essential')) {
+       my $essential = $info->field('essential');
+
+       unfold("essential", \$essential);
+
+       tag "essential-in-source-package", "" if ($type eq "source");
+       tag "essential-no-not-needed", "" if ($essential eq "no");
+       tag "unknown-essential-value", "" if ($essential ne "no" and $essential ne "yes");
+       tag "new-essential-package", "" if ($essential eq "yes" and ! $known_essential{$pkg});
+}
+
+#---- Section
+
+if (not defined $info->field('section')) {
+       tag 'no-section-field' if ($type eq 'binary');
+} else {
+       my $section = $info->field('section');
+
+       unfold("section", \$section);
+
+       if ($type eq 'udeb') {
+           unless ($section eq 'debian-installer') {
+               tag "wrong-section-for-udeb", "$section";
+           }
+       } else {
+           my @parts = split /\//, $section, 2;
+
+           if (scalar @parts > 1) {
+               tag "unknown-section", "$section" unless $known_archive_parts{$parts[0]};
+               tag "unknown-section", "$section" unless $known_sections{$parts[1]};
+           } elsif ($parts[0] eq 'unknown') {
+               tag "section-is-dh_make-template";
+           } else {
+               tag "unknown-section", "$section" unless $known_sections{$parts[0]};
+           }
+
+           # Check package name <-> section.
+           foreach my $map (@NAME_SECTION_MAPPINGS) {
+               if ($pkg =~ $map->[0]) {
+                   tag "wrong-section-according-to-package-name", "$pkg => $map->[1]"
+                       unless $parts[-1] eq $map->[1];
+                   last;
+               }
+           }
+        }
+}
+
+#---- Priority
+
+if (not defined $info->field('priority')) {
+       tag "no-priority-field", "" if $type eq "binary";
+} else {
+       my $priority = $info->field('priority');
+
+       unfold("priority", \$priority);
+
+       tag "unknown-priority", "$priority" if (! $known_prios{$priority});
+
+       if ($pkg =~ /-dbg$/) {
+               tag "debug-package-should-be-priority-extra", $pkg
+                   unless $priority eq 'extra';
+        }
+}
+
+#---- Standards-Version
+# handled in checks/standards-version
+
+#---- Description
+# handled in checks/description
+
+#--- Homepage
+
+if (defined $info->field('homepage')) {
+       my $homepage = $info->field('homepage');
+
+       unfold("homepage", \$homepage);
+
+       if ($homepage =~ /^<(?:UR[LI]:)?.*>$/i) {
+               tag "superfluous-clutter-in-homepage", $homepage;
+       }
+
+       require URI;
+       my $uri = URI->new($homepage);
+
+       unless ($uri->scheme) { # not an absolute URI
+               tag "bad-homepage", $homepage;
+       }
+
+       if ($homepage =~ m,/search\.cpan\.org/.*-[0-9._]+/*$,) {
+               tag 'homepage-for-cpan-package-contains-version', $homepage;
+       }
+} elsif ($type eq "binary" and not $info->native) {
+       tag "no-homepage-field";
+}
+
+#---- Installer-Menu-Item (udeb)
+
+if (defined $info->field('installer-menu-item')) {
+       my $menu_item = $info->field('installer-menu-item');
+
+       unfold('installer-menu-item', \$menu_item);
+
+       $menu_item =~ /^\d+$/ or tag "bad-menu-item", "$menu_item";
+}
+
+
+#---- Package relations (binary package)
+
+# Check whether the package looks like a meta-package, used for later
+# dependency checks.  We consider a package to possibly be a meta-package if
+# it is a binary package, arch: all, with no files outside of /usr/share/doc.
+my $metapackage = 0;
+if ($type eq 'binary' && $arch_indep) {
+       $metapackage = 1;
+       foreach my $file (keys %{$info->index}) {
+               $metapackage = 0 unless ($info->index->{$file}->{type} =~ /^d/
+                   || $file =~ m%^usr/share/doc/%);
+       }
+}
+if (($type eq "binary") || ($type eq 'udeb')) {
+       my (%deps, %fields, %parsed);
+       for my $field (qw(depends pre-depends recommends suggests conflicts provides enhances replaces breaks)) {
+               if (defined $info->field($field)) {
+                       #Get data and clean it
+                       my $data = $info->field($field);;
+                       unfold($field, \$data);
+                       $fields{$field} = $data;
+
+                       my (@seen_libstdcs, @seen_tcls, @seen_tclxs, @seen_tks, @seen_tkxs, @seen_libpngs);
+
+                       my $is_dep_field = sub { grep { $_ eq $_[0] } qw(depends pre-depends recommends suggests) };
+
+                       tag "alternates-not-allowed", "$field"
+                           if ($data =~ /\|/ && ! &$is_dep_field($field));
+
+                       for my $dep (split /\s*,\s*/, $data) {
+                               my (@alternatives, @seen_obsolete_packages);
+                               push @alternatives, [_split_dep($_), $_] for (split /\s*\|\s*/, $dep);
+
+                               if (&$is_dep_field($field)) {
+                                       push @seen_libstdcs, $alternatives[0]->[0]
+                                           if defined $known_libstdcs{$alternatives[0]->[0]};
+                                       push @seen_tcls, $alternatives[0]->[0]
+                                           if defined $known_tcls{$alternatives[0]->[0]};
+                                       push @seen_tclxs, $alternatives[0]->[0]
+                                           if defined $known_tclxs{$alternatives[0]->[0]};
+                                       push @seen_tks, $alternatives[0]->[0]
+                                           if defined $known_tks{$alternatives[0]->[0]};
+                                       push @seen_tkxs, $alternatives[0]->[0]
+                                           if defined $known_tkxs{$alternatives[0]->[0]};
+                                       push @seen_libpngs, $alternatives[0]->[0]
+                                           if defined $known_libpngs{$alternatives[0]->[0]};
+                               }
+
+                               # Only for (Pre-)?Depends.
+                               tag "virtual-package-depends-without-real-package-depends", "$field: $alternatives[0]->[0]"
+                                   if ($VIRTUAL_PACKAGES->known($alternatives[0]->[0])
+                                       && ($field eq "depends" || $field eq "pre-depends"));
+
+                                # Check defaults for transitions.  Here, we only care that the first alternative is current.
+                                tag "depends-on-old-emacs", "$field: $alternatives[0]->[0]"
+                                    if (&$is_dep_field($field) && $known_obsolete_emacs{$alternatives[0]->[0]});
+
+                               for my $part_d (@alternatives) {
+                                       my ($d_pkg, $d_version, $d_arch, $rest, $part_d_orig) = @$part_d;
+
+                                       tag "versioned-provides", "$part_d_orig"
+                                           if ($field eq "provides" && $d_version->[0]);
+
+                                       tag "breaks-without-version", "$part_d_orig"
+                                           if ($field eq "breaks" && !$d_version->[0]);
+
+                                       tag "obsolete-relation-form", "$field: $part_d_orig"
+                                           if ($d_version && grep { $d_version->[0] eq $_ } ("<", ">"));
+
+                                       tag "bad-version-in-relation", "$field: $part_d_orig"
+                                           if ($d_version->[0] && ! defined((_valid_version($d_version->[1]))[1]));
+
+                                       tag "package-relation-with-self", "$field: $part_d_orig"
+                                           if ($pkg eq $d_pkg) && ($field ne 'conflicts');
+
+                                       tag "bad-relation", "$field: $part_d_orig"
+                                           if $rest;
+
+                                       push @seen_obsolete_packages, $part_d_orig
+                                           if ($OBSOLETE_PACKAGES->known($d_pkg) && &$is_dep_field($field));
+
+                                       tag "depends-on-x-metapackage", "$field: $part_d_orig"
+                                           if ($known_x_metapackages{$d_pkg} && ! $metapackage && &$is_dep_field($field));
+
+                                       tag "depends-on-essential-package-without-using-version", "$field: $part_d_orig"
+                                           if ($known_essential{$d_pkg} && ! $d_version->[0] && &$is_dep_field($field));
+
+                                       tag "package-depends-on-an-x-font-package", "$field: $part_d_orig"
+                                           if ($field =~ /^(pre-)?depends$/ && $d_pkg =~ /^xfont.*/ && $d_pkg ne 'xfonts-utils' && $d_pkg ne 'xfongs-encodings');
+
+                                       tag "needlessly-depends-on-awk", "$field"
+                                           if ($d_pkg eq "awk" && ! $d_version->[0] && &$is_dep_field($field));
+
+                                       tag "depends-on-libdb1-compat", "$field"
+                                           if ($d_pkg eq "libdb1-compat" && $pkg !~ /^libc(6|6.1|0.3)/ && $field =~ /^(pre-)depends$/);
+
+                                       tag "depends-on-python-minimal", "$field",
+                                           if ($d_pkg =~ /^python[\d.]*-minimal$/ && &$is_dep_field($field)
+                                               && $pkg !~ /^python[\d.]*-minimal$/);
+
+                                       tag "doc-package-depends-on-main-package", "$field"
+                                           if ("$d_pkg-doc" eq $pkg && $field =~ /^(pre-)depends$/);
+
+                                       tag "old-versioned-python-dependency", "$field: $part_d_orig"
+                                           if ($d_pkg eq 'python' && $d_version->[0] eq '<<' && &$is_dep_field($field)
+                                               && $arch_indep && $pkg =~ /^python-/ && ! defined $info->field('python-version')
+                                               && ! $info->relation('depends')->implies('python-support'));
+
+                                       # only trigger this for the the preferred alternative
+                                       tag "versioned-dependency-satisfied-by-perl", "$field: $part_d_orig"
+                                               if $alternatives[0][-1] eq $part_d_orig
+                                               && &$is_dep_field($field)
+                                               && perl_core_has_version($d_pkg, $d_version->[0], $d_version->[1]);
+
+                                       tag "depends-exclusively-on-makedev", "$field",
+                                           if ($field eq 'depends' && $d_pkg eq 'makedev' && @alternatives == 1);
+                               }
+
+                               for my $pkg (@seen_obsolete_packages) {
+                                       if ($pkg eq $alternatives[0]->[0] or
+                                           scalar @seen_obsolete_packages == scalar @alternatives) {
+                                               tag "depends-on-obsolete-package", "$field: $pkg";
+                                       } else {
+                                               tag "ored-depends-on-obsolete-package", "$field: $pkg";
+                                       }
+                               }
+                       }
+                       tag "package-depends-on-multiple-libstdc-versions", @seen_libstdcs
+                           if (scalar @seen_libstdcs > 1);
+                       tag "package-depends-on-multiple-tcl-versions", @seen_tcls
+                           if (scalar @seen_tcls > 1);
+                       tag "package-depends-on-multiple-tclx-versions", @seen_tclxs
+                           if (scalar @seen_tclxs > 1);
+                       tag "package-depends-on-multiple-tk-versions", @seen_tks
+                           if (scalar @seen_tks > 1);
+                       tag "package-depends-on-multiple-tkx-versions", @seen_tkxs
+                           if (scalar @seen_tkxs > 1);
+                       tag "package-depends-on-multiple-libpng-versions", @seen_libpngs
+                           if (scalar @seen_libpngs > 1);
+               }
+       }
+
+       # If Conflicts or Breaks is set, make sure it's not inconsistent with
+       # the other dependency fields.
+       for my $conflict (qw/conflicts breaks/) {
+               next unless $fields{$conflict};
+               for my $field (qw(depends pre-depends recommends suggests)) {
+                       next unless $info->field($field);
+                       my $relation = $info->relation($field);
+                       for my $package (split /\s*,\s*/, $fields{$conflict}) {
+                               tag "conflicts-with-dependency", $field, $package
+                                   if $relation->implies($package);
+                       }
+               }
+       }
+}
+
+#---- Package relations (source package)
+
+if ($type eq "source") {
+
+       my $binpkgs = $info->binaries;
+
+       #Get number of arch-indep packages:
+       my $arch_indep_packages = 0;
+       my $arch_dep_packages = 0;
+       foreach my $binpkg (keys %$binpkgs) {
+               my $arch = $info->binary_field($binpkg, 'architecture');
+               if ($arch eq 'all') {
+                       $arch_indep_packages++;
+               } else {
+                       $arch_dep_packages++;
+               }
+       }
+
+       # Search through rules and determine which dependencies are required.
+       # The keys in %needed and %needed_clean are the dependencies; the
+       # values are the tags to use or the empty string to use the default
+       # tag.
+       my (%needed, %needed_clean, %allowed_clean, $bypass_needed_clean);
+       open (RULES, '<', "debfiles/rules")
+           or fail("cannot read debfiles/rules: $!");
+       my $target = "none";
+       my @rules = qw(clean binary-arch build-arch);
+        my $maybe_skipping;
+       while (<RULES>) {
+               if (/^ifn?(eq|def)\s/) {
+                       $maybe_skipping++;
+               } elsif (/^endif\s/) {
+                       $maybe_skipping--;
+               }
+               for my $rule (@GLOBAL_CLEAN_DEPENDS) {
+                       if ($_ =~ /$rule->[1]/) {
+                               if ($maybe_skipping) {
+                                       $allowed_clean{$rule->[0]} = 1;
+                               } else {
+                                       $needed_clean{$rule->[0]} = $rule->[2] || $needed_clean{$rule->[0]} || '';
+                               }
+                       }
+               }
+               for my $rule (@GLOBAL_CLEAN_ALLOWED) {
+                       if ($_ =~ /$rule->[1]/) {
+                               $allowed_clean{$rule->[0]} = 1;
+                       }
+               }
+               for my $rule (@GLOBAL_CLEAN_BYPASS) {
+                       if ($_ =~ /$rule/) {
+                               $bypass_needed_clean = 1;
+                       }
+               }
+               for my $rule (@GLOBAL_DEPENDS) {
+                       if ($_ =~ /$rule->[1]/ && !$maybe_skipping) {
+                               $needed{$rule->[0]} = $rule->[2] || $needed{$rule->[0]} || '';
+                       }
+               }
+               if (/^(\S+?):+(.*)/) {
+                       $target = $1;
+                       if (grep ($_ eq $target, @rules)) {
+                               push (@rules, split (' ', $2));
+                       }
+               }
+               if (grep ($_ eq $target, @rules)) {
+                       for my $rule (@RULE_CLEAN_DEPENDS) {
+                               if ($_ =~ /$rule->[1]/) {
+                                       if ($maybe_skipping) {
+                                               $allowed_clean{$rule->[0]} = 1;
+                                       } else {
+                                               $needed_clean{$rule->[0]} = $rule->[2] || $needed_clean{$rule->[0]} || '';
+                                       }
+                               }
+                       }
+                       for my $rule (@RULE_CLEAN_ALLOWED) {
+                               if ($_ =~ /$rule->[1]/) {
+                                       $allowed_clean{$rule->[0]} = 1;
+                               }
+                       }
+               }
+       }
+       close RULES;
+
+       tag "build-depends-indep-without-arch-indep", ""
+               if (defined $info->field('build-depends-indep') && $arch_indep_packages == 0);
+
+       my $is_dep_field = sub { grep { $_ eq $_[0] } qw(build-depends build-depends-indep) };
+
+       my %depend;
+       for my $field (qw(build-depends build-depends-indep build-conflicts build-conflicts-indep)) {
+               if (defined $info->field($field)) {
+                       #Get data and clean it
+                       my $data = $info->field($field);;
+                       unfold($field, \$data);
+                       $depend{$field} = $data;
+
+                       for my $dep (split /\s*,\s*/, $data) {
+                               my (@alternatives, @seen_obsolete_packages);
+                               push @alternatives, [_split_dep($_), $_] for (split /\s*\|\s*/, $dep);
+
+                               tag "virtual-package-depends-without-real-package-depends", "$field: $alternatives[0]->[0]"
+                                   if ($VIRTUAL_PACKAGES->known($alternatives[0]->[0]) && &$is_dep_field($field));
+
+                               for my $part_d (@alternatives) {
+                                       my ($d_pkg, $d_version, $d_arch, $rest, $part_d_orig) = @$part_d;
+
+                                       for my $arch (@{$d_arch->[0]}) {
+                                               if (!$KNOWN_ARCHS->known($arch)) {
+                                                       tag "invalid-arch-string-in-source-relation", "$arch [$field: $part_d_orig]"
+                                               }
+                                       }
+
+                                       tag "build-depends-on-build-essential", $field
+                                           if ($d_pkg eq "build-essential");
+
+                                       tag "depends-on-build-essential-package-without-using-version", "$d_pkg [$field: $part_d_orig]"
+                                           if ($known_build_essential{$d_pkg} && ! $d_version->[1]);
+
+                                       tag "build-depends-on-essential-package-without-using-version", "$field: $part_d_orig"
+                                           if ($d_pkg ne "coreutils" && $known_essential{$d_pkg} && ! $d_version->[0]);
+                                       push @seen_obsolete_packages, $part_d_orig
+                                           if ($OBSOLETE_PACKAGES->known($d_pkg) && &$is_dep_field($field));
+
+                                       tag "build-depends-on-x-metapackage", "$field: $part_d_orig"
+                                           if ($known_x_metapackages{$d_pkg} && &$is_dep_field($field));
+
+                                       tag "build-depends-on-1-revision", "$field: $part_d_orig"
+                                           if ($d_version->[0] eq '>=' && $d_version->[1] =~ /-1$/ && &$is_dep_field($field));
+
+                                       tag "bad-relation", "$field: $part_d_orig"
+                                           if $rest;
+
+                                       # only trigger this for the the preferred alternative
+                                       tag "versioned-dependency-satisfied-by-perl", "$field: $part_d_orig"
+                                               if $alternatives[0][-1] eq $part_d_orig
+                                               && &$is_dep_field($field)
+                                               && perl_core_has_version($d_pkg, $d_version->[0], $d_version->[1]);
+                               }
+
+                               for my $pkg (@seen_obsolete_packages) {
+                                       if ($pkg eq $alternatives[0]->[0] or
+                                           scalar @seen_obsolete_packages == scalar @alternatives) {
+                                               tag "build-depends-on-obsolete-package", "$field: $pkg";
+                                       } else {
+                                               tag "ored-build-depends-on-obsolete-package", "$field: $pkg";
+                                       }
+                               }
+                       }
+               }
+       }
+
+       # Check for duplicates.
+       my $build_all = $info->relation('build-depends-all');
+       my @dups = $build_all->duplicates;
+       for my $dup (@dups) {
+               tag "package-has-a-duplicate-build-relation", join (', ', @$dup);
+       }
+
+       # Make sure build dependencies and conflicts are consistent.
+       my %parsed;
+       for ($depend{'build-conflicts'}, $depend{'build-conflicts-indep'}) {
+               next unless $_;
+               for my $conflict (split /\s*,\s*/, $_) {
+                       if ($build_all->implies($conflict)) {
+                               tag "build-conflicts-with-build-dependency", $conflict;
+                       }
+               }
+       }
+
+       # Make sure that all the required build dependencies are there.  Don't
+       # issue missing-build-dependency errors for debhelper, since there's
+       # another test that does that and it would just be a duplicate.
+       my $build_regular = $info->relation('build-depends');
+       my $build_indep   = $info->relation('build-depends-indep');
+       for my $package (keys %needed_clean) {
+               my $tag = $needed_clean{$package} || 'missing-build-dependency';
+               unless ($build_regular->implies($package)) {
+                       if ($build_indep->implies($package)) {
+                               tag "clean-should-be-satisfied-by-build-depends", $package;
+                       } else {
+                               if ($tag eq 'missing-build-dependency') {
+                                       tag $tag, $package if $package ne 'debhelper';
+                               } else {
+                                       tag $tag;
+                               }
+                       }
+               }
+       }
+       my $noarch = $info->relation_noarch('build-depends-all');
+       for my $package (keys %needed) {
+               my $tag = $needed{$package} || 'missing-build-dependency';
+
+               # dh_python deactivates itself if the new Python build policy
+               # is enabled.
+               if ($tag eq 'missing-dh_python-build-dependency') {
+                       next if -f 'debfiles/pycomat';
+                       next if defined $info->field('python-version');
+               }
+               unless ($noarch->implies($package)) {
+                       if ($tag eq 'missing-build-dependency') {
+                               tag $tag, $package;
+                       } else {
+                               tag $tag;
+                       }
+               }
+       }
+
+       # This check is a bit tricky.  We want to allow in Build-Depends a
+       # dependency with any version, since reporting this tag over version
+       # mismatches would be confusing and quite likely wrong.  The approach
+       # taken is to strip the version information off all dependencies
+       # allowed in Build-Depends, strip the version information off of the
+       # dependencies in Build-Depends, and then allow any dependency in
+       # Build-Depends that's implied by the dependencies we require or allow
+       # there.
+       #
+       # We also have to map | to , when building the list of allowed
+       # packages so that the implications will work properly.
+       #
+       # This is confusing.  There should be a better way to do this.
+       if (defined $info->field('build-depends') && $arch_dep_packages == 0 && !$bypass_needed_clean) {
+               my $build_depends = $info->field('build-depends');
+               my @packages = split /\s*,\s*/, $build_depends;
+               my @allowed = map { s/\([^\)]+\)//g; s/\|/,/g; $_ } keys (%needed_clean), keys (%allowed_clean);
+               my $dep = Maemian::Relation->new_noarch(join(',', @allowed));
+               foreach my $pkg (@packages) {
+                       my $name = $pkg;
+                       $name =~ s/[\[\(][^\)\]]+[\)\]]//g;
+                       $name =~ s/\s+$//;
+                       $name =~ s/\s+/ /g;
+                       unless ($dep->implies($name)) {
+                               tag "build-depends-without-arch-dep", $name;
+                       }
+               }
+       }
+
+       my (@arch_dep_pkgs, @dbg_pkgs);
+       foreach my $binpkg (keys %$binpkgs) {
+               if ($binpkg =~ m/-dbg$/) {
+                       push @dbg_pkgs, $binpkg;
+               } elsif ($info->binary_field($binpkg, 'architecture') ne 'all') {
+                       push @arch_dep_pkgs, $binpkg;
+               }
+       }
+       foreach (@dbg_pkgs) {
+               my $deps;
+               $deps  = $info->binary_field($_, 'pre-depends') . ', ';
+               $deps .= $info->binary_field($_, 'depends');
+               tag 'dbg-package-missing-depends', $_
+                  unless (grep {my $quoted_name = qr<\Q$_>; $deps =~ m/(\s|,|^)$quoted_name(\s|,|$)/} @arch_dep_pkgs);
+       }
+}
+
+#----- Origin
+
+if (defined $info->field('origin')) {
+       my $origin = $info->field('origin');
+
+       unfold('origin', \$origin);
+
+       tag "redundant-origin-field", "" if lc($origin) eq 'debian';
+}
+
+#----- Bugs
+
+if (defined $info->field('bugs')) {
+       my $bugs = $info->field('bugs');
+
+       unfold('bugs', \$bugs);
+
+       tag "redundant-bugs-field"
+           if $bugs =~ m,^debbugs://bugs.debian.org/?$,i;
+}
+
+#----- Python-Version
+
+if (defined $info->field('python-version')) {
+       my $pyversion = $info->field('python-version');
+
+       unfold('python-version', \$pyversion);
+
+       my @valid = ([ '\d+\.\d+', '\d+\.\d+' ],
+                    [ '\d+\.\d+' ],
+                    [ '\>=\s*\d+\.\d+', '\<\<\s*\d+\.\d+' ],
+                    [ '\>=\s*\d+\.\d+' ],
+                    [ 'current', '\>=\s*\d+\.\d+' ],
+                    [ 'current' ],
+                    [ 'all' ]);
+
+       my @pyversion = split(/\s*,\s*/, $pyversion);
+       if (@pyversion > 2) {
+               if (grep { !/^\d+\.\d+$/ } @pyversion) {
+                       tag "malformed-python-version", "$pyversion";
+               }
+       } else {
+               my $okay = 0;
+               for my $rule (@valid) {
+                       if ($pyversion[0] =~ /^$rule->[0]$/
+                           && (($pyversion[1] && $rule->[1] && $pyversion[1] =~ /^$rule->[1]$/)
+                                || (! $pyversion[1] && ! $rule->[1]))) {
+                               $okay = 1;
+                               last;
+                       }
+               }
+               tag "malformed-python-version", "$pyversion" unless $okay;
+       }
+}
+
+#----- Dm-Upload-Allowed
+
+if (defined $info->field('dm-upload-allowed')) {
+       my $dmupload = $info->field('dm-upload-allowed');
+
+       unfold('dm-upload-allowed', \$dmupload);
+
+       unless ($dmupload eq 'yes') {
+               tag "malformed-dm-upload-allowed", "$dmupload";
+       }
+}
+
+#----- Vcs-*
+
+while (my ($vcs, $regex) = each %VCS_RECOMMENDED_URIS) {
+    if (defined $info->field("vcs-$vcs")) {
+       my $uri = $info->field("vcs-$vcs");
+       if ($uri !~ $regex) {
+           if ($VCS_VALID_URIS{$vcs} and $uri =~ $VCS_VALID_URIS{$vcs}) {
+               tag "vcs-field-uses-not-recommended-uri-format", "vcs-$vcs", $uri;
+           } else {
+               tag "vcs-field-uses-unknown-uri-format", "vcs-$vcs", $uri;
+           }
+       }
+    }
+}
+
+
+#----- Field checks (without checking the value)
+
+for my $field (glob("fields/*")) {
+       $field =~ s!^fields/!!;
+
+       next if ($field eq 'original-maintainer') and $version =~ /ubuntu/;
+
+       tag "obsolete-field", "$field"
+           if $known_obsolete_fields{$field};
+
+       tag "unknown-field-in-dsc", "$field"
+           if ($type eq "source" && ! $known_source_fields{$field} && ! $known_obsolete_fields{$field});
+
+       tag "unknown-field-in-control", "$field"
+           if ($type eq "binary" && ! $known_binary_fields{$field} && ! $known_obsolete_fields{$field});
+
+       tag "unknown-field-in-control", "$field"
+           if ($type eq "udeb" && ! $known_udeb_fields{$field} && ! $known_obsolete_fields{$field});
+}
+
+}
+
+# splits "foo (>= 1.2.3) [!i386 ia64]" into
+# ( "foo", [ ">=", "1.2.3" ], [ [ "i386", "ia64" ], 1 ], "" )
+#                                                  ^^^   ^^
+#                                 true, if ! was given   ||
+#           rest (should always be "" for valid dependencies)
+sub _split_dep {
+       my $dep = shift;
+       my ($pkg, $version, $darch) = ("", ["",""], [[],""]);
+
+       $pkg = $1 if $dep =~ s/^\s*([^\s\[\(]+)\s*//;
+
+       if (length $dep) {
+               if ($dep =~ s/\s* \( \s* (<<|<=|<|=|>=|>>|>) \s* ([^\s(]+) \s* \) \s*//x) {
+                       @$version = ($1, $2);
+               }
+               if ($dep && $dep =~ s/\s*\[([^\]]+)\]\s*//) {
+                       my $t = $1;
+                       $darch->[1] = 1 if ($t =~ s/!//g);
+                       $darch->[0] = [ split /\s+/, $t ];
+               }
+       }
+
+       return ($pkg, $version, $darch, $dep);
+}
+
+sub _valid_version {
+       my $ver = shift;
+
+       # epoch check means nothing here... This check is only useful to detect
+       # weird characters in version (and to get the debian revision)
+       if ($ver =~ m/^(\d+:)?([-\.+:~A-Z0-9]+?)(-[\.+~A-Z0-9]+)?$/i) {
+               return ($1, $2, $3);
+       } else {
+               return ();
+       }
+}
+
+sub perl_core_has_version {
+       my ($package, $op, $version) = @_;
+       my $core_version = $PERL_CORE_PROVIDES->value($package);
+       return 0 if !defined $core_version;
+       my @version = _valid_version($version);
+       return 0 if !@version;
+       return versions_compare($core_version, $op, $version);
+}
+
+sub unfold {
+       my $field = shift;
+       my $line = shift;
+
+       $$line =~ s/\n$//;
+
+       if ($$line =~ s/\n//g) {
+               tag "multiline-field", "$field";
+       }
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: t
+# cperl-indent-level: 8
+# End:
+# vim: syntax=perl sw=4 ts=4 noet shiftround