Added lots more modules from lintian. Maemian appears to work.
[maemian] / checks / control-file
1 # control-file -- lintian check script -*- perl -*-
2 #
3 # Copyright (C) 2004 Marc Brockschmidt
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, you can find it on the World Wide
17 # Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
18 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 package Maemian::control_file;
22 use strict;
23
24 use lib "$ENV{'MAEMIAN_ROOT'}/checks/";
25 use common_data;
26
27 use Maemian::Relation ();
28 use Tags;
29 use Util;
30
31 # The list of libc packages, used for checking for a hard-coded dependency
32 # rather than using ${shlibs:Depends}.
33 our @LIBCS = qw(libc6 libc6.1 libc0.1 libc0.3);
34
35 sub run {
36
37 my $pkg = shift;
38 my $type = shift;
39
40 if (-l "debfiles/control") {
41     tag "debian-control-file-is-a-symlink", "";
42 }
43
44 # check that control is UTF-8 encoded
45 my $line = file_is_encoded_in_non_utf8("debfiles/control", $type, $pkg);
46 if ($line) {
47     tag "debian-control-file-uses-obsolete-national-encoding", "at line $line"
48 }
49
50 # Check that each field is only used once:
51 my $seen_fields = {};
52 open (CONTROL, '<', "debfiles/control")
53     or fail "Couldn't read debfiles/control: $!";
54 while (<CONTROL>) {
55         s/\s*\n$//;
56         next if /^\#/;
57
58         #Reset seen_fields if we enter a new section:
59         $seen_fields = {} if /^$/;
60
61         #line with field:
62         if (/^(\S+):/) {
63                 my $field = lc ($1);
64                 if ($seen_fields->{$field}) {
65                         tag "debian-control-with-duplicate-fields", "$field: $$seen_fields{$field}, $.";
66                 }
67                 $seen_fields->{$field} = $.;
68                 if ($field =~ /^xs-vcs-/) {
69                         my $base = $field;
70                         $base =~ s/^xs-//;
71                         tag "xs-vcs-header-in-debian-control", "$field"
72                             if $known_source_fields{$base};
73                 }
74                 unless (/^\S+: \S/) {
75                         tag 'debian-control-has-unusual-field-spacing', "line $.";
76                 }
77         }
78 }
79 close CONTROL;
80
81 my ($header, @binary_controls) = read_dpkg_control("debfiles/control");
82
83 for my $binary_control (@binary_controls) {
84         tag "build-info-in-binary-control-file-section", "Package ".$binary_control->{"package"}
85             if ($binary_control->{"build-depends"} || $binary_control->{"build-depends-indep"} ||
86                 $binary_control->{"build-conflicts"} || $binary_control->{"build-conflicts-indep"});
87         for my $field (keys %$binary_control) {
88                 tag 'binary-control-field-duplicates-source', "field \"$field\" in package ".$binary_control->{'package'},
89                     if ($header->{$field} && $binary_control->{$field} eq $header->{$field});
90         }
91
92         # If two substvars aren't separated by a comma, but at least one of
93         # them expands to an empty string, there will be a lurking bug.  The
94         # result will be syntactically correct, but as soon as both expand
95         # into something non-empty, there will be a syntax error.  Catch that
96         # mistake to avoid problems later.
97         #
98         # Only check the fields that use comma-separated values.
99         for my $field (qw(pre-depends depends recommends suggests breaks
100                           conflicts provides replaces enhances)) {
101                 next unless $binary_control->{$field};
102                 if ($binary_control->{$field} =~ /(\$\{\S+\})\s+[a-zA-Z0-9\$]/) {
103                         tag 'missing-comma-after-substvar', "in $field field near $1";
104                 }
105         }
106 }
107
108 # Check if comma-separated values that span multiple lines omit commas as in
109 # the following example:
110 #   Depends: foo, bar
111 #    baz
112 for my $control ($header, @binary_controls) {
113         for my $field (qw(pre-depends depends recommends suggests breaks
114                           conflicts provides replaces enhances
115                           build-depends build-depends-indep
116                           build-conflics build-conflicts-indep)) {
117                 next unless $control->{$field};
118                 if ($control->{$field} =~ /
119                         ([^,]+)         # previous entry
120                         \s*\n\s+        # new line + space
121                         ([a-z][^,]+)    # next entry, must start with a letter
122                         /x) {
123
124                         my ($prev, $next) = ($1, $2);
125                         for ($prev, $next) {
126                                 s/^\s+//; s/\s+$//;
127                         }
128                         tag "missing-comma-between-items",
129                             "in $field field between '$prev' and '$next', " .
130                             ($control->{source} ? 'source' : $control->{package});
131                 }
132         }
133 }
134
135 # Make sure that a stronger dependency field doesn't imply any of the elements
136 # of a weaker dependency field.  dpkg-gencontrol will fix this up for us, but
137 # we want to check the source package since dpkg-gencontrol may silently "fix"
138 # something that's a more subtle bug.
139 #
140 # Also check if a package declares a simple dependency on itself, since
141 # similarly dpkg-gencontrol will clean this up for us but it may be a sign of
142 # another problem, and check that the package doesn't hard-code a dependency
143 # on libc.  We have to do the latter check here rather than in checks/fields
144 # to distinguish from dependencies created by ${shlibs:Depends}.
145 my @dep_fields = qw(pre-depends depends recommends suggests);
146 my $libcs = Maemian::Relation->new(join(' | ', @LIBCS));
147 for my $control (@binary_controls) {
148         for my $strong (0 .. $#dep_fields) {
149                 next unless $control->{$dep_fields[$strong]};
150                 my $relation = Maemian::Relation->new($control->{$dep_fields[$strong]});
151                 tag "package-depends-on-itself", $control->{package}, $dep_fields[$strong]
152                     if $relation->implies($control->{package});
153                 tag 'package-depends-on-hardcoded-libc', $control->{package}, $dep_fields[$strong]
154                     if ($relation->implies($libcs) and $pkg ne "glibc");
155                 for my $weak (($strong + 1) .. $#dep_fields) {
156                         next unless $control->{$dep_fields[$weak]};
157                         for my $dependency (split /\s*,\s*/, $control->{$dep_fields[$weak]}) {
158                                 next unless $dependency;
159                                 tag "stronger-dependency-implies-weaker", $control->{package}, "$dep_fields[$strong] -> $dep_fields[$weak]", $dependency
160                                     if $relation->implies($dependency);
161                         }
162                 }
163         }
164 }
165
166 # Check that every package is in the same archive area, except that
167 # sources in main can deliver both main and contrib packages.  The source
168 # package may or may not have a section specified; if it doesn't, derive the
169 # expected archive area from the first binary package by leaving $area
170 # undefined until parsing the first binary section.  Missing sections will be
171 # caught by other checks.
172 #
173 # Also accumulate short and long descriptions for each package so that we can
174 # check for duplication, but skip udeb packages.  Ideally, we should check the
175 # udeb package descriptions separately for duplication, but udeb packages
176 # should be able to duplicate the descriptions of non-udeb packages and the
177 # package description for udebs is much less important or significant to the
178 # user.
179 my $area;
180 if ($header->{'section'}) {
181         if ($header->{'section'} =~ m%^([^/]+)/%) {
182                 $area = $1;
183         } else {
184                 $area = '';
185         }
186 } else {
187         tag "no-section-field-for-source", "";
188 }
189 my @descriptions;
190 for my $binary_control (@binary_controls) {
191         if ($binary_control->{'description'}
192             and (not $binary_control->{'xc-package-type'}
193                  or $binary_control->{'xc-package-type'} ne 'udeb')) {
194                 push(@descriptions,
195                      [ $binary_control->{'package'},
196                        split("\n", $binary_control->{'description'}, 2) ]);
197         }
198         next unless $binary_control->{'section'};
199         if (!defined ($area)) {
200                 if ($binary_control->{'section'} =~ m%^([^/]+)/%) {
201                         $area = ($1 eq 'contrib') ? '' : $1;
202                 } else {
203                         $area = '';
204                 }
205                 next;
206         }
207         tag "section-area-mismatch", "Package " . $binary_control->{'package'}
208                 if ($area && $binary_control->{'section'} !~ m%^$area/%);
209         tag "section-area-mismatch", "Package " . $binary_control->{'package'}
210                 if (!$area && $binary_control->{'section'} =~ m%^([^/]+)/% && $1 ne 'contrib');
211 }
212
213 # Check for duplicate descriptions.
214 my (%seen_short, %seen_long);
215 for my $i (0 .. $#descriptions) {
216         my (@short, @long);
217         for my $j (($i + 1) .. $#descriptions) {
218                 if ($descriptions[$i][1] eq $descriptions[$j][1]) {
219                         my $package = $descriptions[$j][0];
220                         push(@short, $package) unless $seen_short{$package};
221                 }
222                 next unless ($descriptions[$i][2] and $descriptions[$j][2]);
223                 if ($descriptions[$i][2] eq $descriptions[$j][2]) {
224                         my $package = $descriptions[$j][0];
225                         push(@long, $package) unless $seen_long{$package};
226                 }
227         }
228         if (@short) {
229                 tag 'duplicate-short-description', $descriptions[$i][0], @short;
230                 for (@short) { $seen_short{$_} = 1 }
231         }
232         if (@long) {
233                 tag 'duplicate-long-description', $descriptions[$i][0], @long;
234                 for (@long) { $seen_long{$_} = 1 }
235         }
236 }
237
238 }
239
240 1;
241
242 # Local Variables:
243 # indent-tabs-mode: t
244 # cperl-indent-level: 8
245 # End:
246 # vim: syntax=perl sw=4 ts=4 noet shiftround