#! /bin/sh /usr/share/dpatch/dpatch-run ## 500-backport-1.23.22-frontend.dpatch by Eero Häkkinen ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: Backported frontends from lintian 1.23.22 @DPATCH@ diff -urNad lintian-1.23.8/frontend/lintian lintian-1.23.22/frontend/lintian --- lintian-1.23.8/frontend/lintian 2005-01-02 00:29:42.000000000 +0000 +++ lintian-1.23.22/frontend/lintian 2006-07-19 11:57:17.000000000 +0000 @@ -17,8 +17,8 @@ # 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., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA. +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +# MA 02110-1301, USA. # }}} # {{{ libraries and such @@ -64,6 +64,8 @@ my $LINTIAN_CFG = ""; #config file to use our $LINTIAN_ROOT; #location of the lintian modules +my $experimental_output_opts = undef; + my @packages; my $action; @@ -72,7 +74,7 @@ my $unpack_info; my $cwd; my $cleanup_filename; -my $exit_code; +my $exit_code = 0; my $LAB; my %collection_info; @@ -258,6 +260,9 @@ "unpack-info|U=s" => \&record_unpack_info, "md5sums|m" => \$check_md5sums, "allow-root" => \$allow_root, + # Note: Ubuntu has (and other derivatives might gain) a + # -D/--debian option to make lintian behave like in Debian, that + # is, to revert distribution-specific changes # ------------------ configuration options "cfg=s" => \$LINTIAN_CFG, @@ -274,6 +279,9 @@ "source|s" => \&record_pkgmode, "udeb" => \&record_pkgmode, "packages-file|p=s" => \$packages_file, + + # ------------------ experimental + "exp-output:s" => \$experimental_output_opts, ); # init commandline parser @@ -385,7 +393,7 @@ # LINTIAN_ARCH must have a value. unless (defined $LINTIAN_ARCH) { if ($LINTIAN_DIST) { - chop($LINTIAN_ARCH=`dpkg --print-installation-architecture`); + chop($LINTIAN_ARCH=`dpkg --print-architecture`); } else { $LINTIAN_ARCH = 'any'; } @@ -475,7 +483,14 @@ import Util; import Pipeline; -# }}} +require Tags; +import Tags; + +my @l_secs = read_dpkg_control("$LINTIAN_ROOT/checks/lintian.desc"); +shift(@l_secs); +map Tags::add_tag($_), @l_secs; + +# }}} # {{{ No clue why this code is here... @@ -607,6 +622,7 @@ next; } + Tags::set_pkg( $arg, $arg_name, "", "", 'binary' ); # check distribution field if (! (($data->{'distribution'} eq 'stable') or ($data->{'distribution'} eq 'testing') @@ -616,7 +632,8 @@ or ($data->{'distribution'} =~ /\w+-security/)) ) { # bad distribution entry - print "E: $arg_name: bad-distribution-in-changes-file $data->{'distribution'}\n"; + tag("bad-distribution-in-changes-file", + $data->{'distribution'}); } # process all listed `files:' @@ -636,7 +653,7 @@ if (-s $filename ne $size) { print "N: size is $size, argname is $arg_name, filename is $filename\n"; - print "E: $arg_name: file-size-mismatch-in-changes-file $file\n"; + tag( "file-size-mismatch-in-changes-file", $file ); } # check md5sums @@ -644,13 +661,13 @@ my $real_md5sum = get_file_md5($filename); if ($real_md5sum ne $md5sum) { - print "E: $arg_name: md5sum-mismatch-in-changes-file $file\n"; + tag( "md5sum-mismatch-in-changes-file", $file ); } } # check section if (($section eq 'non-free') or ($section eq 'contrib')) { - print "E: $arg_name: bad-section-in-changes-file $file $section\n"; + tag( "bad-section-in-changes-file", $file, $section ); } # process file? @@ -668,6 +685,14 @@ $info->{'version'}, $filename); } } + + unless ($exit_code) { + my $stats = Tags::get_stats( $arg ); + if ($stats->{severity}{4}) { + $exit_code = 1; + } + } + } else { fail("bad package file name $arg (neither .deb, .udeb or .dsc file)"); } @@ -850,10 +875,22 @@ # {{{ Now we're ready to load info about checks & tags -require Tags; -import Tags; +no warnings 'once'; +if (defined $experimental_output_opts) { + $Tags::output_formatter = \&Tags::print_tag_new; + my %opts = map { split(/=/) } split( /,/, $experimental_output_opts ); + foreach (keys %opts) { + if ($_ eq 'format') { + if ($opts{$_} eq 'colons') { + require Tags::ColonSeparated; + $Tags::output_formatter = \&Tags::ColonSeparated::print_tag; + } + } + no strict 'refs'; + ${"Tags::$_"} = $opts{$_}; + } +} -no warnings; $Tags::show_info = $display_infotags; $Tags::show_overrides = $show_overrides; use warnings; @@ -920,10 +957,10 @@ } } + shift(@secs); + map Tags::add_tag($_), @secs; } # end: if ne lintian - shift(@secs); - map Tags::add_tag($_), @secs; } closedir(CHECKDIR); @@ -1056,8 +1093,6 @@ printf "N: Selected checks: %s\n",join(',',keys %checks); } -$exit_code = 0; - require Checker; # for each package (the `reverse sort' is to make sure that source packages are @@ -1203,7 +1238,7 @@ } if ($action eq 'check') { # read override file - Tags::pkg_reset($long_type eq 'binary' ? $pkg : "$pkg $long_type"); + Tags::set_pkg( $file, $pkg, "", "", $long_type ); unless ($no_override) @@ -1259,21 +1294,25 @@ print "N: Skipping $action of $long_type package $pkg\n"; next PACKAGE; } + + } + unless ($exit_code) { + my $stats = Tags::get_stats( $file ); + if ($stats->{severity}{4}) { + $exit_code = 1; + } } # report unused overrides -# if (not $no_override and $verbose) { -# my $ppkg = $type eq 'b' ? quotemeta($pkg) : quotemeta("$pkg $long_type"); -# for my $o (sort keys %overridden) { -# next unless $o =~ /^$ppkg:/; -# next if $overridden{$o}; -# -# print "I: $pkg: unused-override $o\n"; -# -# # mark override entry as used -# $overridden{$o} = 99999; -# } -# } + if (not $no_override and $verbose) { + my $overrides = Tags::get_overrides( $file ); + + for my $o (sort keys %$overrides) { + next if $overrides->{$o}; + + tag( "unused-override", $o ); + } + } } # chdir to lintian root directory (to unlock $base so it can be removed below) diff -urNad lintian-1.23.8/frontend/lintian-info lintian-1.23.22/frontend/lintian-info --- lintian-1.23.8/frontend/lintian-info 2004-11-24 22:44:09.000000000 +0000 +++ lintian-1.23.22/frontend/lintian-info 2006-07-19 11:57:17.000000000 +0000 @@ -17,8 +17,8 @@ # 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., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA. +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +# MA 02110-1301, USA. use strict; @@ -48,6 +48,7 @@ my ($type, $pkg, @pieces) = split(/:\s+/); if ($type =~ m/^[OEWIX]$/) { $tag = shift @pieces; + next if not defined $tag; ($tag) = split(/\s+/, $tag, 2); next if not exists $tag_info{$tag} or $already_displayed{$tag}++; diff -urNad lintian-1.23.8/lib/Tags/ColonSeparated.pm lintian-1.23.22/lib/Tags/ColonSeparated.pm --- lintian-1.23.8/lib/Tags/ColonSeparated.pm 1970-01-01 00:00:00.000000000 +0000 +++ lintian-1.23.22/lib/Tags/ColonSeparated.pm 2006-07-19 11:58:28.000000000 +0000 @@ -0,0 +1,55 @@ +# Tags::ColonSeparated -- Perl tags functions for lintian +# $Id: Tags.pm 489 2005-09-17 00:06:30Z djpig $ + +# Copyright (C) 2005 Frank Lichtenheld +# +# 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 Tags::ColonSeparated; +use strict; +use warnings; + +sub quote_char { + my ( $char, @items ) = @_; + + foreach (@items) { + s/\\/\\\\/go; + s/\Q$char\E/\\$char/go; + } + + return @items; +} + +sub print_tag { + my ( $pkg_info, $tag_info, $information ) = @_; + + my $extra = "@$information"; + + print join(':', quote_char( ':', + $tag_info->{severity}, + $tag_info->{significance}, + @{$tag_info->{overridden}}{'override', + 'severity', + 'significance'}, + @{$pkg_info}{'pkg','version','arch','type'}, + $tag_info->{tag}, + $extra, + ))."\n"; +} + +1; + diff -urNad lintian-1.23.8/lib/Tags.pm lintian-1.23.22/lib/Tags.pm --- lintian-1.23.8/lib/Tags.pm 2006-07-18 13:39:52.000000000 +0000 +++ lintian-1.23.22/lib/Tags.pm 2006-07-19 11:57:40.000000000 +0000 @@ -1,7 +1,8 @@ # Tags -- Perl tags functions for lintian -# $Id: Tags.pm 364 2004-11-13 21:07:48Z djpig $ +# $Id: Tags.pm 510 2005-10-14 00:19:49Z djpig $ # Copyright (C) 1998-2004 Various authors +# Copyright (C) 2005 Frank Lichtenheld # # 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 @@ -16,30 +17,30 @@ # 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., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA. +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +# MA 02110-1301, USA. package Tags; use strict; - -use Util; +use warnings; use Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(tag); -my $LINTIAN_ROOT = $::LINTIAN_ROOT; - -# Can also be more precise later on (only verbose with lab actions) but for -# now this will do --Jeroen -my $verbose = $::verbose; -my $debug = $::debug; - -# What to print between the "E:" and the tag, f.e. "package source" -our $prefix = undef; +# configuration variables and defaults +our $verbose = $::verbose; +our $debug = $::debug; our $show_info = 0; +our $show_overrides = 0; +our $output_formatter = \&print_tag; +our $min_severity = 1; +our $max_severity = 99; +our $min_significance = 1; +our $max_significance = 99; -# The master hash with all tag info. Key is a hash too, with these stuff: +# The master hash with all tag info. Key is the tag name, value another hash +# with the following keys: # - tag: short name # - type: error/warning/info/experimental # - info: Description in HTML @@ -47,53 +48,265 @@ # - experimental: experimental status (possibly undef) my %tags; -our $show_overrides; -# in the form overrides->tag or full thing -my %overrides; +# Statistics per file. Key is the filename, value another hash with the +# following keys: +# - overrides +# - tags +# - severity +# - significance +my %stats; -my $codes = { 'error' => 'E' , 'warning' => 'W' , 'info' => 'I' }; +# Info about a specific file. Key is the the filename, value another hash +# with the following keys: +# - pkg: package name +# - version: package version +# - arch: package architecture +# - type: one of 'binary', 'udeb' or 'source' +# - overrides: hash with all overrides for this file as keys +my %info; +# Currently selected file (not package!) +my $current; -# TODO -# - override support back in --> just the unused reporting -# - be able to return whether any errors were there, better, full stats +# Compatibility stuff +my %codes = ( 'error' => 'E' , 'warning' => 'W' , 'info' => 'I' ); +our %type_to_sev = ( error => 4, warning => 2, info => 0 ); +our @sev_to_type = qw( info warning warning error error ); -# Call this function to add a certain tag, by supplying the info as a hash +my @sig_to_qualifier = ( '??', '?', '', '!' ); +my @sev_to_code = qw( I W W E E ); + +# Add a new tag, supplied as a hash reference sub add_tag { my $newtag = shift; - fail("Duplicate tag: $newtag->{'tag'}") - if exists $tags{$newtag->{'tag'}}; - + if (exists $tags{$newtag->{tag}}) { + warn "Duplicate tag: $newtag->{tag}\n"; + return 0; + } + + # smooth transition + $newtag->{type} = $sev_to_type[$newtag->{severity}] + unless $newtag->{type}; + $newtag->{significance} = 2 unless exists $newtag->{significance}; + $newtag->{severity} = $type_to_sev{$newtag->{type}} + unless exists $newtag->{severity}; $tags{$newtag->{'tag'}} = $newtag; + return 1; } -# Used to reset the matched tags data -sub pkg_reset { - $prefix = shift; - %overrides = (); +# Add another file, will fail if there is already stored info about +# the file +sub set_pkg { + my ( $file, $pkg, $version, $arch, $type ) = @_; + + if (exists $info{$file}) { + warn "File $file was already processed earlier\n"; + return 0; + } + + $current = $file; + $info{$file} = { + pkg => $pkg, + version => $version, + arch => $arch, + type => $type, + overrides => {}, + }; + $stats{$file} = { + severity => {}, + significance => {}, + tags => {}, + overrides => {}, + }; + + return 1; } -# Add an override, string tag, string rest +# select another file as 'current' without deleting or adding any information +# the file must have been added with add_pkg +sub select_pkg { + my ( $file ) = @_; + + unless (exists $info{$file}) { + warn "Can't select package $file"; + return 0; + } + + $current = $file; + return 1; +} + +# only delete the value of 'current' without deleting any stored information +sub reset_pkg { + undef $current; + return 1; +} + +# delete all the stored information (including tags) +sub reset { + undef %stats; + undef %info; + undef %tags; + undef $current; + return 1; +} + +# Add an override. If you specifiy two arguments, the first will be taken +# as file to add the override to, otherwise 'current' will be assumed sub add_override { - my $tag = shift; - $overrides{$tag} = 0; + my ($tag, $file) = ( "", "" ); + if (@_ > 1) { + ($file, $tag) = @_; + } else { + ($file, $tag) = ($current, @_); + } + + unless ($file) { + warn "Don't know which package to add override $tag to"; + return 0; + } + + $info{$file}{overrides}{$tag} = 0; + + return 1; +} + +sub get_overrides { + my ($file) = @_; + + unless ($file) { + warn "Don't know which package to get overrides from"; + return undef; + } + + return $info{$file}{overrides}; +} + +# Get the info hash for a tag back as a reference. The hash will be +# copied first so that you can edit it safely +sub get_tag_info { + my ( $tag ) = @_; + return { %{$tags{$tag}} } if exists $tags{$tag}; + return undef; +} + +sub check_range { + my ( $x, $min, $max ) = @_; + + return -1 if $x < $min; + return 1 if $x > $max; + return 0; +} + +# check if a certain tag has a override for the 'current' package +sub check_overrides { + my ( $tag_info, $information ) = @_; + + my $extra = ''; + $extra = " @$information" if @$information; + $extra = '' if $extra eq ' '; + if( exists $info{$current}{overrides}{$tag_info->{tag}}) { + $info{$current}{overrides}{$tag_info->{tag}}++; + return $tag_info->{tag}; + } elsif( exists $info{$current}{overrides}{"$tag_info->{tag}$extra"} ) { + $info{$current}{overrides}{"$tag_info->{tag}$extra"}++; + return "$tag_info->{tag}$extra"; + } + + return ''; +} + +# sets all the overridden fields of a tag_info hash correctly +sub check_need_to_show { + my ( $tag_info, $information ) = @_; + $tag_info->{overridden}{override} = check_overrides( $tag_info, + $information ); + my $min_sev = $show_info ? 0 : $min_severity; # compat hack + $tag_info->{overridden}{severity} = check_range( $tag_info->{severity}, + $min_sev, + $max_severity ); + $tag_info->{overridden}{significance} = check_range( $tag_info->{significance}, + $min_significance, + $max_significance ); } +# records the stats for a given tag_info hash +sub record_stats { + my ( $tag_info ) = @_; + + for my $k (qw( severity significance tag )) { + $stats{$current}{$k}{$tag_info->{$k}}++ + unless $tag_info->{overridden}{$k}; + } + for my $k (qw( severity significance override )) { + $stats{$current}{overrides}{$k}{$tag_info->{overridden}{$k}}++ + if $tag_info->{overridden}{$k}; + } +} + +# get the statistics for a file (one argument) or for all files (no argument) +sub get_stats { + my ( $file ) = @_; + + return $stats{$file} if $file; + return \%stats; +} + +sub print_tag { + my ( $pkg_info, $tag_info, $information ) = @_; + + my $extra = ''; + $extra = " @$information" if @$information; + $extra = '' if $extra eq ' '; + my $code = $codes{$tag_info->{type}}; + $code = 'O' if $tag_info->{overridden}{override}; + my $type = ''; + $type = " $pkg_info->{type}" if $pkg_info->{type} ne 'binary'; + + print "$code: $pkg_info->{pkg}$type: $tag_info->{tag}$extra\n"; +} + +sub print_tag_new { + my ( $pkg_info, $tag_info, $information ) = @_; + + my $extra = ''; + $extra = " @$information" if @$information; + $extra = '' if $extra eq ' '; + my $code = $sev_to_code[$tag_info->{severity}]; + $code = 'O' if $tag_info->{overridden}{override}; + my $qualifier = $sig_to_qualifier[$tag_info->{significance}]; + $qualifier = '' if $code eq 'O'; + my $type = ''; + $type = " $pkg_info->{type}" if $pkg_info->{type} ne 'binary'; + + print "$code$qualifier: $pkg_info->{pkg}$type: $tag_info->{tag}$extra\n"; + +} sub tag { - my $tag = shift; - my $info = $tags{$tag}; - return if not $show_info and $info->{'type'} eq 'info'; - my $extra = ''; - $extra = ' '.join(' ', @_) if $#_ >=0; - $extra = '' if $extra eq ' '; - my $code = $codes->{$info->{'type'}}; - if (exists $overrides{$tag} or exists $overrides{"$tag$extra"}) { - return unless $show_overrides or $verbose; - $code = 'O'; - } + my ( $tag, @information ) = @_; + unless ($current) { + warn "Tried to issue tag $tag without setting package\n"; + return 0; + } - print "$code: $prefix: $tag$extra\n"; + my $tag_info = get_tag_info( $tag ); + unless ($tag_info) { + warn "Tried to issue unknown tag $tag\n"; + return 0; + } + check_need_to_show( $tag_info, \@information ); + + record_stats( $tag_info ); + + return 1 if + $tag_info->{overridden}{severity} != 0 + || $tag_info->{overridden}{significance} != 0 + || ( $tag_info->{overridden}{override} && + !$show_overrides); + + &$output_formatter( $info{$current}, $tag_info, \@information ); + return 1; } 1;