#!/usr/bin/perl # # Debian Archive Overrides Sublimator # # Copyright © 2013, 2017-2018 Guillem Jover # # 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, see . use strict; use warnings; no warnings qw(experimental::smartmatch); use feature qw(switch state); my $VERSION = '0.0'; use Getopt::Long qw(:config posix_default bundling no_ignorecase); use AptPkg::Config '$_config'; use AptPkg::System '$_system'; use AptPkg::Cache; (my $self = $0) =~ s#.*/##; my %options = ( help => sub { usage(); exit 0 }, version => sub { version(); exit 0 }, filter => undef, suite => 'experimental|unstable|sid|testing|wheezy', format => 'detail', chunk => undef, verbose => 0, ); my $bts_from_name = (getpwuid($<))[6] =~ s/,.*$//r; my $bts_from_mail = $ENV{DEBEMAIL}; my $bts_from = "$bts_from_name <$bts_from_mail>"; my $bts_addr = 'submit@bugs.debian.org'; my $bts_user = 'guardians@namespace.hadrons.org'; my $bts_tags = 'sectionspace-rift'; sub version { print "$self $VERSION\n", } sub usage { printf "Usage: %s [options] Options: --suite=NAME Set the suite (default: %s). --filter=NAME Set the section filter to use (default: '') --format=NAME Set the output format (default: %s). Supported values: bts, micro, bulk, detail. --chunk=N Chunk output formats by N items (default none). --verbose=N Set the verbose level (default: %d). -?, --help Show this help message. --version Show the version. ", $self, $options{suite}, $options{format}, $options{verbose}; } my $rc = GetOptions(\%options, 'help|?', 'version', 'filter=s', 'suite=s', 'format=s', 'chunk=i', 'verbose=i', ); usage() and exit 1 unless $rc or @ARGV; # Initialise the global config object with the default values and # setup the $_system object. $_config->init; $_system = $_config->system; # Suppress cache building messages. $_config->{quiet} = 2; # Set up the cache. my $cache = AptPkg::Cache->new; sub header { given ($options{format}) { when ('bts') { print "From: $bts_from\n"; print "To: $bts_addr\n"; print 'Subject: override: '; } } } sub footer { given ($options{format}) { when ('bts') { print "\n\n"; print "Package: ftp.debian.org\n"; print "Severity: wishlist\n"; print "User: ftp.debian.org\@packages.debian.org\n"; print "Usertags: override\n"; print "User: $bts_user\n"; print "Usertags: $bts_tags\n"; } } } sub skip { my ($pkgname, $section, $excuse) = @_; return if $options{format} ne 'detail'; print " skip: $pkgname:$section ($excuse)\n" if $options{verbose} > 0; } sub relay { my ($line) = @_; return if $options{format} ne 'detail'; print "$line\n" if $options{verbose} > 0; } sub override { my ($pkgname, $version, $cursection, $section, $priority) = @_; state $chunked = 0; return if defined $options{filter} and !($section =~ $options{filter}); # Normalize priority, Debian does not have extra anymore. $priority = 'optional' if $priority eq 'extra'; given ($options{format}) { when ('detail') { print "override: $pkgname:$section/$priority ($version ; $cursection)\n"; } when ('bts') { print "$pkgname:$section/$priority"; $chunked++; if (defined $options{chunk} and $chunked >= $options{chunk}) { $chunked = 0; footer(); print "\n<<==>>\n"; header(); } else { print ", "; } } when ('micro') { print "$pkgname $section\n"; } when ('bulk') { print "$pkgname\t$section\t$priority\n"; } default { die "format not supported $options{format}\n"; } } } sub check_change { my ($pkgname, $section, $oldsection) = @_; print "D: $pkgname $section\n" if $options{verbose} > 1; # Patterns are not currently supported. if ($pkgname =~ m/\*/) { skip($pkgname, $section, "pattern"); return; } my $pkg = $cache->{$pkgname}; unless ($pkg) { skip($pkgname, $section, "unknown package"); return; } my $available = $pkg->{VersionList}; unless ($available) { skip($pkgname, $section, "virtual package"); return; } my $cursection = $pkg->{Section}; if ($cursection =~ m/^$section$/) { skip($pkgname, $section, "correct section ; $cursection"); return; } if (defined $oldsection and $cursection ne $oldsection) { skip($pkgname, $section, "changed section ; $cursection"); return; } my $pkg_found = 0; FIND_PKG: for my $v (@$available) { for my $f (map $_->{File}, @{$v->{FileList}}) { if ($f->{Archive} =~ m/$options{suite}/) { my $version = $v->{VerStr}; my $priority = $v->{Priority}; override($pkgname, $version, $cursection, $section, $priority); $pkg_found = 1; last FIND_PKG; } } } if (!$pkg_found) { skip($pkgname, $section, "not in suite = $options{suite}"); } } header(); while (<>) { chomp; next if /^#/; if (/^==== finish ====/) { relay($_); last; } if (/^(?:==|--)/) { relay($_); next; } if (/^\s*$/) { relay(''); next; } # Override fix proposal if (m{^([*a-z0-9.+-]+)\s+([a-z|/-]+)}) { my $pkgname = $1; my $section = $2; check_change($pkgname, $section); next; } # Override fix proposal w/ check if (m{^([*a-z0-9.+-]+)\s+!([a-z/-]+)\s+->\s([a-z|/-]+)}) { my $pkgname = $1; my $oldsection = $2; my $section = $3; check_change($pkgname, $section, $oldsection); next; } } footer(); 1;