]> git.donarmstrong.com Git - wannabuild.git/blobdiff - bin/wanna-build
cleanup $arch in call_edos_depcheck
[wannabuild.git] / bin / wanna-build
index 06402eae847a7d2df84dee757b322e6da7357f9f..1fb5b901fab48469bb6b5759083d1b5f51a34579 100755 (executable)
@@ -3,7 +3,7 @@
 # wanna-build: coordination script for Debian buildds
 # Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
 # Copyright (C) 2005-2008 Ryan Murray <rmurray@debian.org>
-# Copyright (C) 2010      Andreas Barth <aba@not.so.argh.org>
+# Copyright (C) 2010,2011 Andreas Barth <aba@not.so.argh.org>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License as
@@ -43,6 +43,7 @@ use POSIX;
 use FileHandle;
 use File::Copy;
 use DBI;
+use Getopt::Long qw ( :config gnu_getopt );
 use lib '/org/wanna-build/lib';
 #use lib 'lib';
 use WannaBuild;
@@ -63,14 +64,14 @@ use Dpkg::Deps; # TODO: same
 
 our ($verbose, $mail_logs, $list_order, $list_state,
     $curr_date, $op_mode, $user, $real_user, $distribution,
-    $fail_reason, $opt_override, $import_from, $export_to, $opt_create_db,
+    $fail_reason, $opt_override, $import_from, $export_to,
     %prioval, %sectval,
     $info_all_dists, $arch,
-    $category, %catval, %short_category,
-    $short_date, $list_min_age, $dbbase, @curr_time,
+    $short_date, $list_min_age, $list_max_age, $dbbase, @curr_time,
     $build_priority, %new_vers, $binNMUver, %merge_srcvers, %merge_binsrc,
     $printformat, $ownprintformat, $privmode, $extra_depends, $extra_conflicts,
-    %distributions, %distribution_aliases
+    %distributions, %distribution_aliases, $actions,
+    $sshwrapper,
     );
 our $Pas = '/org/buildd.debian.org/etc/packages-arch-specific/Packages-arch-specific';
 our $simulate = 0;
@@ -88,180 +89,126 @@ $curr_date = strftime("%Y %b %d %H:%M:%S",@curr_time);
 $short_date = strftime("%m/%d/%y",@curr_time);
 $| = 1;
 
-# map program invocation names to operation modes
-my %prognames = ( "uploaded-build"  => "set-uploaded",
-                                 "failed-build"    => "set-failed",
-                                 "no-build"            => "set-not-for-us",
-                                 "give-back-build" => "set-needs-build",
-                                 "dep-wait-build"  => "set-dep-wait",
-                                 "forget-build"        => "forget",
-                                 "build-info"          => "info" );
-
-%short_category = ( u => "uploaded-fixed-pkg",
-                                   f => "fix-expected",
-                                       r => "reminder-sent",
-                                       n => "nmu-offered",
-                                       e => "easy",
-                                       m => "medium",
-                                       h => "hard",
-                                       c => "compiler-error",
-                                       "" => "none" );
-
-my $progname;
-($progname = $0) =~ s,.*/,,;
-if ($prognames{$progname}) {
-       $op_mode = $prognames{$progname};
-}
-elsif ($progname =~ /^list-(.*)$/) {
-       $op_mode = "list";
-       $list_state = ($1 eq "all") ? "" : $1;
-}
-
-my %options =
-       (# flags
-        simulate       => { flag => \$simulate }, # this is not supported by all operations (yet)!
-        "simulate-edos"  => { flag => \$simulate_edos },
-        "simulate-all" => { code => sub { $simulate = 1; $simulate_edos = 1; } },
-        api            => { arg => \$api, code => sub {
-                            # official apis are numeric
-                            die "$api isn't numeric" unless int($api) eq $api;
-                            die "$api too large" unless $api <= 1;
-                            } },
-        verbose        => { short => "v", flag => \$verbose },
-        override               => { short => "o", flag => \$opt_override },
-        "create-db"    => { flag => \$opt_create_db },
-        "correct-compare" => { flag => \$WannaBuild::opt_correct_version_cmp },
-        # TODO: remove after buildds no longer pass to wanna-build
-        "no-propagation" => { short => "N" },
-        "no-down-propagation" => { short => "D" },
-        # normal actions
-        take                   => { mode => "set-building" },
-        failed                 => { short => "f", mode => "set-failed" },
-        uploaded               => { short => "u", mode => "set-uploaded" },
-        "no-build"             => { short => "n", mode => "set-not-for-us" },
-        built                  => { mode => "set-built" },
-        attempted              => { mode => "set-attempted" },
-        "give-back"            => { mode => "set-needs-build" },
-        "dep-wait"             => { mode => "set-dep-wait" },
-        forget                 => { mode => "forget" },
-        'forget-user' => { mode => 'forget-user' },
-        update                 => { mode => "set-update" },
-        "merge-v3"      => { mode => "merge-v3" },
-        info                   => { short => "i", mode => "info" },
-        'binNMU' => { mode => 'set-binary-nmu', arg => \$binNMUver, 
-                            code => sub { die "Invalid binNMU version: $binNMUver\n"
-                               if $binNMUver !~ /^([\d]*)$/ and $1 >= 0; } },
-        'perm-build-priority'    => { mode => "set-permanent-build-priority", arg => \$build_priority,
-                             code => sub { die "Invalid build priority: $build_priority\n"
-                               if $build_priority !~ /^-?[\d]+$/; } },
-        'build-priority'    => { mode => "set-build-priority", arg => \$build_priority,
-                             code => sub { die "Invalid build priority: $build_priority\n"
-                               if $build_priority !~ /^-?[\d]+$/; } },
-        list                   =>
-        { short => "l", mode => "list", arg => \$list_state,
-          code => sub {
-                  die "Unknown state to list: $list_state\n"
-                          if !isin( $list_state, qw(needs-build building uploaded
-                                                built build-attempted failed installed dep-wait
-                                                not-for-us auto-not-for-us all failed-removed
-                                                install-wait reupload-wait bd-uninstallable));} },
-        # options with args
-        dist           =>
-        { short => "d", arg => \$distribution,
-          code => sub {
-                  if ($distribution eq "a" || $distribution eq "all") {
-                          $info_all_dists = 1;
-                          $distribution = "";
-                  }
-                  else {
-                          $distribution = "oldstable"   if $distribution eq "o";
-                          $distribution = "stable"   if $distribution eq "s";
-                          $distribution = "testing"  if $distribution eq "t";
-                          $distribution = "unstable" if $distribution eq "u";
-                  }
-          } },
-        order          =>
-        { short => "O", arg => \$list_order,
-          code => sub {
-                  die "Bad ordering character\n"
-                          if $list_order !~ /^[PSpsncbCWT]+$/;
-          } },
-        message        => { short => "m", arg => \$fail_reason },
-        # database is deprecated, use arch instead.
-        database       => { short => "b", arg => \$conf::dbbase },
-        arch           => { short => "A", arg => \$arch },
-        user           => { short => "U", arg => \$user },
-        category               => { short => "c", arg => \$category,
-                                                code => sub {
-                                                        $category = $short_category{$category}
-                                                                if exists $short_category{$category};
-                                                        die "Unknown category: $category\n"
-                                                                if !isin( $category, values %short_category );
-                                                } },
-        "min-age"      => { short => "a", arg => \$list_min_age,
-                                                code => sub {
-                                                        die "Argument of --min-age must be a non-zero number\n"
-                                                                if $list_min_age == 0;
-                                                } },
-        "max-age"      => { arg => \$list_min_age,
-                                                code => sub {
-                                                        die "Argument of --max-age must be a non-zero number\n"
-                                                                if $list_min_age == 0;
-                                                        $list_min_age *= -1;
-                                                } },
-         "format"       => { arg => \$printformat },
-         "own-format"       => { arg => \$ownprintformat },
-         "Pas"          => { arg => \$Pas },
-        "extra-depends"=> { arg => \$extra_depends },
-        "extra-conflicts"=> { arg => \$extra_conflicts },
-        # special actions
-        export         => { arg => \$export_to, mode => "export" },
-        import         => { arg => \$import_from, mode => "import" },
-        "manual-edit"  => { mode => "manual-edit" },
-        "distribution-architectures" => { mode => "distribution-architectures" },
-        "distribution-aliases" => { mode => "distribution-aliases" },
-        );
-
-while( @ARGV && $ARGV[0] =~ /^-/ ) {
-       $_ = shift @ARGV;
-       last if $_ eq "--";
-       my($opt, $optname, $arg);
-       if (/^--([^=]+)(=|$)/) {
-               $optname = $1;
-               $opt = $options{$optname};
-               $arg = $1 if /^--\Q$optname\E=((.|\n)*)$/;
-       }
-       else {
-               $optname = substr( $_, 1, 1 );
-               $opt = (grep { defined($_->{short}) ? $_->{short} eq $optname : 0} values %options)[0];
-               $arg = $1 if /^-$optname(.+)$/;
-       }
-       if (!$opt) {
-               warn "Unknown option: --$1\n";
-               usage();
-       }
-       if ($opt->{arg}) {
-               if (!defined $arg) {
-                       die "$optname option missing argument\n" if !@ARGV;
-                       $arg = shift @ARGV;
-               }
-               ${$opt->{arg}} = $arg;
-       }
-       elsif (defined $arg) {
-               die "Option $optname takes no argument\n";
-       }
-       
-       if ($opt->{mode}) {
-               die "Conflicting operation modes\n" if $op_mode;
-               $op_mode = $opt->{mode};
-       }
-       if ($opt->{flag}) {
-               ${$opt->{flag}}++;
-       }
-       if ($opt->{code}) {
-               &{$opt->{code}};
-       }
-}
+# set mode of operation based on command line switch. Should be used
+# by GetOptions below.
+sub _set_mode_set { $op_mode = "set-$_[0]" }
+sub _set_mode { $op_mode = "$_[0]" }
+
+sub _option_deprecated { warn "Option $_[0] is deprecated" }
+
+my @wannabuildoptions = (
+    # this is not supported by all operations (yet)!
+    'simulate'      => \$simulate,
+    'simulate-edos' => \$simulate_edos,
+    'simulate-all'  => sub { $simulate = 1; $simulate_edos = 1; },
+    'api=i'         => sub {
+        $api = $_[1];
+        die "$api too large" unless $api <= 1;
+    },
+    'verbose|v'       => \$verbose,
+    'override|o'      => \$opt_override,
+    'correct-compare' => \$WannaBuild::opt_correct_version_cmp,
+
+    # TODO: remove after buildds no longer pass to wanna-build
+    'no-propagation|N'      => \&_option_deprecated,
+    'no-down-propagation|D' => \&_option_deprecated,
+
+    # normal actions
+    'building|take'         => \&_set_mode_set,
+    'failed|f'              => \&_set_mode_set,
+    'uploaded|u'            => \&_set_mode_set,
+    'not-for-us|no-build|n' => \&_set_mode_set,
+    'built'                 => \&_set_mode_set,
+    'attempted'             => \&_set_mode_set,
+    'needs-build|give-back' => \&_set_mode_set,
+    'dep-wait'              => \&_set_mode_set,
+    'update'                => \&_set_mode_set,
+    'forget'                => \&_set_mode,
+    'forget-user'           => \&_set_mode,
+    'merge-v3'              => \&_set_mode,
+    'info|i'                => \&_set_mode,
+    'binary-nmu|binNMU=i'   => sub {
+        _set_mode_set(@_);
+        $binNMUver = $_[1];
+    },
+    'permanent-build-priority|perm-build-priority=i' => sub {
+        _set_mode_set(@_);
+        $build_priority = $_[1];
+    },
+    'build-priority=i' => sub {
+        _set_mode_set(@_);
+        $build_priority = $_[1];
+    },
+    'list|l=s' => sub {
+        _set_mode(@_);
+        $list_state = $_[1];
+        die "Unknown state to list: $list_state\n"
+          if not $list_state ~~ [
+              qw( needs-build building uploaded built
+                  build-attempted failed installed
+                  dep-wait not-for-us auto-not-for-us
+                  all failed-removed install-wait
+                  reupload-wait bd-uninstallable ) ];
+    },
+    'dist|d=s' => sub {
+        $distribution = $_[1];
+        given ( $_[1] ) {
+            when ( [qw< a all >] ) {
+                $info_all_dists = 1;
+                $distribution   = '';
+            }
+            when ('o') { $distribution = 'oldstable'; }
+            when ('s') { $distribution = 'stable'; }
+            when ('t') { $distribution = 'testing'; }
+            when ('u') { $distribution = 'unstable'; }
+
+            if ($distribution eq 'any-priv') {
+                $privmode = 1;
+                $distribution = 'any';
+            }
+            if ($distribution eq 'any-unpriv') {
+                $privmode = 0;
+                $distribution = 'any';
+            }
+        }
+    },
+    'order|O=s' => sub {
+        $list_order = $_[1];
+        die "Bad ordering character\n"
+          if $list_order !~ /^[PSpsncbCWT]+$/;
+    },
+    'message|m=s'  => \$fail_reason,
+    'database|b=s' => sub {
+        # If they didn't specify an arch, try to get it from database name which
+        # is in the form of $arch/build-db
+        # This is for backwards compatibity with older versions that didn't
+        # specify the arch yet.
+        warn "database is deprecated, please use 'arch' instead.\n";
+        $_[1] =~ m#^([^/]+)#;
+        $arch ||= $1;
+    },
+    'arch|A=s'     => \$arch,
+    'user|U=s'     => \$user,
+    'min-age|a=i'       => \$list_min_age,
+    'max-age=i'         => sub { $list_min_age = -1 * ($_[1]); },
+    'format=s'          => \$printformat,
+    'own-format=s'      => \$ownprintformat,
+    'Pas=s'             => \$Pas,
+    'extra-depends=s'   => \$extra_depends,
+    'extra-conflicts=s' => \$extra_conflicts,
+
+    # special actions
+    'export=s' => sub { _set_mode(@_); $export_to   = $_[1]; },
+    'import=s' => sub { _set_mode(@_); $import_from = $_[1]; },
+    'manual-edit'                => \&_set_mode,
+    'distribution-architectures' => \&_set_mode,
+    'distribution-aliases'       => \&_set_mode,
+
+    'ssh-wrapper'       => \$sshwrapper,
+    'recorduser'        => \$recorduser,
+    );
+
+GetOptions(@wannabuildoptions) or usage();
 
 my $dbh;
 
@@ -273,18 +220,9 @@ END {
 }
 
 $distribution ||= "sid";
-if ($distribution eq 'any-priv') {
-    $privmode = 1;
-    $distribution = 'any';
-}
-if ($distribution eq 'any-unpriv') {
-    $privmode = 0;
-    $distribution = 'any';
-}
 
 my $schema_suffix = '';
-$recorduser //= (not -t and $user//"" =~ /^buildd_/);
-if ((isin( $op_mode, qw(list info)) && $distribution !~ /security/ && !$recorduser && !($privmode)) || $simulate) {
+if ((isin( $op_mode, qw(list info distribution-architectures distribution-aliases)) && $distribution !~ /security/ && !$recorduser && !($privmode)) || $simulate) {
        $dbh = DBI->connect("DBI:Pg:service=wanna-build") || 
                die "FATAL: Cannot open database: $DBI::errstr\n";
        $schema_suffix = '_public';
@@ -318,8 +256,7 @@ foreach my $name (keys %$rows) {
 }
 $distribution = $distribution_aliases{$distribution} if (isin($distribution, keys %distribution_aliases));
 
-$op_mode = $category ? "set-failed" : "set-building"
-       if !$op_mode; # default operation
+$op_mode ||= "set-building";
 undef $distribution if $distribution eq 'any';
 if ($distribution) {
     my @dists = split(/[, ]+/, $distribution);
@@ -332,13 +269,6 @@ if (!isin ( $op_mode, qw(list) ) && ( !$distribution || $distribution =~ /[ ,]/)
     die "multiple distributions are only allowed for list";
 }
 
-# If they didn't specify an arch, try to get it from database name which
-# is in the form of $arch/build-db
-# This is for backwards compatibity with older versions that didn't
-# specify the arch yet.
-$conf::dbbase =~ m#^([^/]+)#;
-$arch ||= $1;
-
 # TODO: Check that it's an known arch (for that dist), and give
 # a proper error.
 
@@ -362,7 +292,7 @@ die "Can't determine your user name\n"
           !($user = $real_user);
 
 if (!$fail_reason) {
-       if ($op_mode eq "set-failed" && !$category) {
+       if ($op_mode eq "set-failed" ) {
                print "Enter reason for failing (end with '.' alone on ".
                      "its line):\n";
                my $line;
@@ -413,22 +343,12 @@ $list_order ||= $yamlmap->{"list-order"}{'default'};
 $api //= $yamlmap->{"api"};
 $api //= 0;
 
-process();
-
-$dbh->commit;
-$dbh->disconnect;
-
-if ($mail_logs && $conf::log_mail) {
-       send_mail( $conf::log_mail,
-                          "wanna-build $distribution state changes $curr_date",
-                          "State changes at $curr_date for distribution ".
-                          "$distribution:\n\n$mail_logs\n" );
+if (isin($op_mode, qw<forget-user merge-v3 import>) && defined @conf::admin_users && !isin( $real_user, @conf::admin_users) && !$simulate ) {
+    die "This operation is restricted to admin users";
+}
+if (!isin($op_mode, qw<distribution-architectures distribution-aliases>)) {
+    die "need an architecture" unless $arch;
 }
-
-exit 0;
-
-
-sub process {
 
        SWITCH: foreach ($op_mode) {
                /^set-(.+)/ && do {
@@ -444,9 +364,6 @@ sub process {
                        last SWITCH;
                };
                /^forget-user/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
                        forget_users( @ARGV );
                        last SWITCH;
                };
@@ -455,8 +372,6 @@ sub process {
                        last SWITCH;
                };
                /^merge-v3/ && do {
-                        die "This operation is restricted to admin users\n"
-                           if (defined @conf::admin_users and !isin( $real_user, @conf::admin_users) and !$simulate);
                         # call with installed-packages+ . installed-sources+ [ . available-for-build-packages* [ . consider-as-installed-source* ]  ]
                         # in case available-for-build-packages is not specified, installed-packages are used
                         lock_table() unless $simulate;
@@ -483,12 +398,7 @@ sub process {
                         last SWITCH;
                };
                /^import/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
-                       $dbh->do("DELETE from " . table_name() . 
-                               " WHERE distribution = ?", undef,
-                               $distribution)
+                       $dbh->do("DELETE from ".table_name()." WHERE distribution = ?", undef, $distribution)
                                or die $dbh->errstr;
                        forget_users();
                        read_db( $import_from );
@@ -520,62 +430,166 @@ sub process {
                        update_user_info($user);
                }
        }
+
+
+$dbh->commit unless $simulate;
+$dbh->disconnect;
+
+if ($mail_logs && $conf::log_mail) {
+       send_mail( $conf::log_mail,
+                          "wanna-build $distribution state changes $curr_date",
+                          "State changes at $curr_date for distribution ".
+                          "$distribution:\n\n$mail_logs\n" );
 }
 
-sub add_packages {
-       my $newstate = shift;
-       my( $package, $name, $version, $ok, $reason );
-       
-       foreach $package (@_) {
-               $package =~ s,^.*/,,; # strip path
-               $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
-               $package =~ s/_[a-zA-Z\d-]+\.changes$//; # strip extension
-               if ($package =~ /^([\w\d.+-]+)_([\w\d:.+~-]+)/) {
-                       ($name,$version) = ($1,$2);
-               }
-               else {
-                       warn "$package: can't extract package name and version ".
-                                "(bad format)\n";
-                       next;
-               }
+exit 0;
 
-               if ($op_mode eq "set-building") {
-                       add_one_building( $name, $version );
-               }
-               elsif ($op_mode eq "set-built") {
-                       add_one_built( $name, $version );
-               }
-               elsif ($op_mode eq "set-attempted") {
-                       add_one_attempted( $name, $version );
-               }
-               elsif ($op_mode eq "set-uploaded") {
-                       add_one_uploaded( $name, $version );
-               }
-               elsif ($op_mode eq "set-failed") {
-                       add_one_failed( $name, $version );
-               }
-               elsif ($op_mode eq "set-not-for-us") {
-                       add_one_notforus( $name, $version );
-               }
-               elsif ($op_mode eq "set-needs-build") {
-                       add_one_needsbuild( $name, $version );
-               }
-               elsif ($op_mode eq "set-dep-wait") {
-                       add_one_depwait( $name, $version );
-               }
-               elsif ($op_mode eq "set-build-priority") {
-                       set_one_buildpri( $name, $version, 'buildpri' );
-               }
-               elsif ($op_mode eq "set-permanent-build-priority") {
-                       set_one_buildpri( $name, $version, 'permbuildpri' );
-               }
-               elsif ($op_mode eq "set-binary-nmu") {
-                       set_one_binnmu( $name, $version );
-               }
-               elsif ($op_mode eq "set-update") {
-                       set_one_update( $name, $version );
-               }
+
+BEGIN {
+    $actions = {
+        'set-building'  => { 'noversion' => 1, 'nopkgdef' => 1, },
+        'set-built'     => { 'builder' => 1, to => 'Built', action => 'built', 'from' => [qw<Building Build-Attempted>]},
+        'set-attempted' => { 'builder' => 1, to => 'Build-Attempted', action => 'attempted', 'from' => [qw<Building Build-Attempted>]},
+        'set-uploaded'  => { 'builder' => 1, to => 'Uploaded', action => 'uploaded', 'from' => [qw<Building Built Build-Attempted>], binversion => 1, },
+        'set-failed'    => { 'builder' => 1, to => 'Failed', action => 'failed', from => [qw<Building Built Build-Attempted Dep-Wait Failed>], warnfrom => [qw<Needs-Build Uploaded Dep-Wait BD-Uninstallable>], },
+        'set-dep-wait'  => { 'builder' => 1, warnfrom => [qw<Needs-Build Failed BD-Uninstallable>], },
+        'set-update'    => { 'noversion' => 1, },
+        'set-needs-build' => { builder => 1, to => 'BD-Uninstallable', action => 'give-back'},
+    };
+}
+
+sub add_packages {
+    my $newstate = shift;
+    my( $package, $name, $version, $ok, $reason );
+
+    foreach $package (@_) {
+        $package =~ s,^.*/,,; # strip path
+        $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
+        $package =~ s/_[a-zA-Z\d-]+\.changes$//; # strip extension
+        if ($package =~ /^([\w\d.+-]+)_([\w\d:.+~-]+)/) {
+            ($name,$version) = ($1,$2);
+        } else {
+           warn "$package: can't extract package name and version (bad format)\n";
+           next;
        }
+
+       my $pkg = get_source_info($name);
+        if (!($actions->{$op_mode}) || !($actions->{$op_mode}->{'nopkgdef'})) {
+            if (!defined($pkg)) {
+                print "$name: not registered yet.\n";
+                next;
+            }
+        }
+        if ($actions->{$op_mode} && $actions->{$op_mode}->{'builder'}) {
+            if (($pkg->{'builder'} && $user ne $pkg->{'builder'}) &&
+               !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user) &&
+               !$opt_override) {
+                print "$pkg->{'package'}: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
+                next;
+            }
+        }
+        if (!($actions->{$op_mode}) || !($actions->{$op_mode}->{'noversion'})) {
+            my $nmuver = binNMU_version($pkg->{version}, $pkg->{'binary_nmu_version'});
+            if ((!pkg_version_eq($pkg,$version) || $actions->{$op_mode}->{'binversion'}) && !version_eq( $nmuver, $version )) {
+                print "$pkg->{package}: version mismatch ($nmuver";
+                print " by $pkg->{'builder'}" if $pkg->{'builder'};
+                print ")\n";
+                next;
+            }
+        }
+
+        if ($actions->{$op_mode} && $actions->{$op_mode}->{'from'}) {
+            if (!isin($pkg->{'state'}, @{$actions->{$op_mode}->{'from'}}, @{$actions->{$op_mode}->{'warnfrom'}})) {
+                print "$name: skiping: state is $pkg->{'state'}, not in ".join(", ",@{$actions->{$op_mode}->{'from'}}, @{$actions->{$op_mode}->{'warnfrom'}})."\n";
+                next;
+            }
+        }
+        if ($actions->{$op_mode} && $actions->{$op_mode}->{'warnfrom'}) {
+            if (isin($pkg->{'state'}, @{$actions->{$op_mode}->{'warnfrom'}})) {
+                print "$name: warning: state is $pkg->{'state'}, processing anyways.\n";
+            }
+        }
+
+        if ($op_mode eq "set-building") {
+            add_one_building( $name, $version, $pkg );
+        }
+        elsif ($op_mode eq "set-failed") {
+           print "$pkg->{'package'}: already registered as failed; will append new message\n" if $pkg->{'state'} eq "Failed";
+           $pkg->{'builder'} = $user;
+           $pkg->{'failed'} .= "\n" if $pkg->{'failed'};
+           $pkg->{'failed'} .= $fail_reason;
+        }
+        elsif ($op_mode eq "set-not-for-us") {
+            add_one_notforus( $pkg );
+        }
+        elsif ($op_mode eq "set-needs-build") {
+           my $state = $pkg->{'state'};
+
+           if ($state eq "BD-Uninstallable") {
+               if ($opt_override) {
+                       print "$name: Forcing uninstallability mark to be removed. This is not permanent and might be reset with the next trigger run\n";
+
+                       change_state( \$pkg, 'Needs-Build' );
+                       delete $pkg->{'builder'};
+                       delete $pkg->{'depends'};
+                       log_ta( $pkg, "--give-back" );
+                       update_source_info($pkg);
+                       print "$name: given back\n" if $verbose;
+                       next;
+               }
+               else {
+                       print "$name: has uninstallable build-dependencies. Skipping\n  (use --override to clear dependency list and give back anyway)\n";
+                       next;
+               }
+            }
+            elsif ($state eq "Dep-Wait") {
+               if ($opt_override) {
+                       print "$name: Forcing source dependency list to be cleared\n";
+               }
+               else {
+                       print "$name: waiting for source dependencies. Skipping\n  (use --override to clear dependency list and give back anyway)\n";
+                       next;
+               }
+            }
+            elsif (!isin( $state, qw(Building Built Build-Attempted))) {
+               print "$name: not taken for building (state is $state).";
+               if ($opt_override) {
+                       print "\n$name: Forcing give-back\n";
+               }
+               else {
+                       print " Skipping.\n";
+                       next;
+               }
+            }
+            $pkg->{'builder'} = undef;
+            $pkg->{'depends'} = undef;
+        }
+        elsif ($op_mode eq "set-dep-wait") {
+            add_one_depwait( $pkg );
+        }
+        elsif ($op_mode eq "set-build-priority") {
+            set_one_buildpri( 'buildpri', $pkg );
+        }
+        elsif ($op_mode eq "set-permanent-build-priority") {
+            set_one_buildpri( 'permbuildpri', $pkg );
+        }
+        elsif ($op_mode eq "set-binary-nmu") {
+            set_one_binnmu( $name, $version, $pkg );
+        }
+        elsif ($op_mode eq "set-update") {
+            $pkg->{'version'} =~ s/\+b[0-9]+$//;
+
+           log_ta( $pkg, "--update" );
+           update_source_info($pkg);
+        }
+
+        if ($actions->{$op_mode} && $actions->{$op_mode}->{'action'} && $actions->{$op_mode}->{'to'}) {
+           change_state( \$pkg, $actions->{$op_mode}->{'to'} );
+           log_ta( $pkg, "--".$actions->{$op_mode}->{'action'} );
+           update_source_info($pkg);
+           print "$name: registered as ".$actions->{$op_mode}->{'action'}."\n" if $verbose;
+        }
+    }
 }
 
 sub add_one_building {
@@ -584,19 +598,16 @@ sub add_one_building {
        my( $ok, $reason );
 
        $ok = 1;
-       my $pkg = get_source_info($name);
+       my $pkg = shift;
        if (defined($pkg)) {
-               if ($pkg->{'state'} eq "Not-For-Us") {
-                       $ok = 0;
-                       $reason = "not suitable for this architecture";
-               }
-               elsif ($pkg->{'state'} =~ /^Dep-Wait/) {
-                       $ok = 0;
-                       $reason = "not all source dependencies available yet";
-               }
-               elsif ($pkg->{'state'} =~ /^BD-Uninstallable/) {
+            my $pkgnack = {
+                'Not-For-Us' => 'not suitable for this architecture',
+                'Dep-Wait' => 'not all source dependencies available yet',
+                'BD-Uninstallable' => 'source dependencies are not installable',
+            };
+               if ($pkgnack->{$pkg->{'state'}}) {
                        $ok = 0;
-                       $reason = "source dependencies are not installable";
+                       $reason = $pkgnack->{$pkg->{'state'}};
                }
                elsif ($pkg->{'state'} eq "Uploaded" &&
                           (version_lesseq($version, $pkg->{'version'}))) {
@@ -738,215 +749,11 @@ sub add_one_building {
        }
 }
 
-sub add_one_attempted {
-       my $name = shift;
-       my $version = shift;
-       my $pkg = get_source_info($name);
-
-       if (!defined($pkg)) {
-               print "$name: not registered yet.\n";
-               return;
-       }
-
-       if (($pkg->{'state'} ne "Building") && ($pkg->{'state'} ne "Build-Attempted")) {
-               print "$name: not taken for building (state is $pkg->{'state'}). ",
-                         "Skipping.\n";
-               return;
-       }
-       if ($pkg->{'builder'} ne $user) {
-               print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
-               return;
-       }
-       elsif ( !pkg_version_eq($pkg, $version) ) {
-               print "$name: version mismatch ".
-                         "$(pkg->{'version'} ".
-                         "by $pkg->{'builder'})\n";
-               return;
-       }
-
-       change_state( \$pkg, 'Build-Attempted' );
-       log_ta( $pkg, "--attempted" );
-       update_source_info($pkg);
-       print "$name: registered as uploaded\n" if $verbose;
-}
-
-sub add_one_built {
-       my $name = shift;
-       my $version = shift;
-       my $pkg = get_source_info($name);
-
-       if (!defined($pkg)) {
-               print "$name: not registered yet.\n";
-               return;
-       }
-
-        if (($pkg->{'state'} ne "Building") && ($pkg->{'state'} ne "Build-Attempted")) {
-               print "$name: not taken for building (state is $pkg->{'state'}). ",
-                         "Skipping.\n";
-               return;
-       }
-       if ($pkg->{'builder'} ne $user) {
-               print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
-               return;
-       }
-       elsif ( !pkg_version_eq($pkg, $version) ) {
-               print "$name: version mismatch ".
-                         "$(pkg->{'version'} ".
-                         "by $pkg->{'builder'})\n";
-               return;
-       }
-       change_state( \$pkg, 'Built' );
-       log_ta( $pkg, "--built" );
-       update_source_info($pkg);
-       print "$name: registered as built\n" if $verbose;
-}
-
-sub add_one_uploaded {
-       my $name = shift;
-       my $version = shift;
-       my $pkg = get_source_info($name);
-
-       if (!defined($pkg)) {
-               print "$name: not registered yet.\n";
-               return;
-       }
-
-       if ($pkg->{'state'} eq "Uploaded" &&
-               pkg_version_eq($pkg,$version)) {
-               print "$name: already uploaded\n";
-               return;
-       }
-       if (!isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
-               print "$name: not taken for building (state is $pkg->{'state'}). ",
-                         "Skipping.\n";
-               return;
-       }
-       if ($pkg->{'builder'} ne $user) {
-               print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
-               return;
-       }
-       # strip epoch -- buildd-uploader used to go based on the filename.
-       # (to remove at some point)
-       my $pkgver;
-       ($pkgver = $pkg->{'version'}) =~ s/^\d+://;
-       $version =~ s/^\d+://; # for command line use
-       if ($pkg->{'binary_nmu_version'} ) {
-               my $nmuver = binNMU_version($pkgver, $pkg->{'binary_nmu_version'});
-               if (!version_eq( $nmuver, $version )) {
-                       print "$name: version mismatch ($nmuver registered). ",
-                                 "Skipping.\n";
-                       return;
-               }
-       } elsif (!version_eq($pkgver, $version)) {
-               print "$name: version mismatch ($pkg->{'version'} registered). ",
-                         "Skipping.\n";
-               return;
-       }
-
-       change_state( \$pkg, 'Uploaded' );
-       log_ta( $pkg, "--uploaded" );
-       update_source_info($pkg);
-       print "$name: registered as uploaded\n" if $verbose;
-}
-
-sub add_one_failed {
-       my $name = shift;
-       my $version = shift;
-       my ($state, $cat);
-       my $pkg = get_source_info($name);
-
-       if (!defined($pkg)) {
-               print "$name: not registered yet.\n";
-               return;
-       }
-       $state = $pkg->{'state'};
-
-       if ($state eq "Not-For-Us") {
-               print "$name: not suitable for this architecture anyway. Skipping.\n";
-               return;
-       }
-       elsif ($state eq "Failed-Removed") {
-               print "$name: failed previously and doesn't need building. Skipping.\n";
-               return;
-       }
-       elsif ($state eq "Installed") {
-               print "$name: Is already installed in archive. Skipping.\n";
-               return;
-       }
-       elsif ($pkg->{'builder'} &&
-                  (($user ne $pkg->{'builder'}) &&
-                   !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user))) {
-               print "$name: not taken by you, but by ".
-                         "$pkg->{'builder'}. Skipping.\n";
-               return;
-       }
-       elsif ( !pkg_version_eq($pkg, $version) ) {
-               print "$name: version mismatch ".
-                         "$(pkg->{'version'} ".
-                         "by $pkg->{'builder'})\n";
-               return;
-       }
-
-       $cat = $category;
-       if (!$cat && $fail_reason =~ /^\[([^\]]+)\]/) {
-               $cat = $1;
-               $cat = $short_category{$cat} if exists $short_category{$cat};
-               if (!isin( $cat, values %short_category )) {
-                       print "$name: Warning: unknown category $cat; discarded\n";
-                       $cat = "";
-               }
-               $fail_reason =~ s/^\[[^\]]+\][ \t]*\n*//;
-       }
-
-       if ($state eq "Needs-Build") {
-               print "$name: Warning: not registered for building previously, ".
-                         "but processing anyway.\n";
-       }
-       elsif ($state eq "Uploaded") {
-               print "$name: Warning: marked as uploaded previously, ".
-                         "but processing anyway.\n";
-       }
-       elsif ($state eq "Dep-Wait") {
-               print "$name: Warning: marked as waiting for dependencies, ".
-                         "but processing anyway.\n";
-       }
-       elsif ($state eq "BD-Uninstallable") {
-               print "$name: Warning: marked as having uninstallable build-dependencies, ".
-                         "but processing anyway.\n";
-       }
-       elsif ($state eq "Failed") {
-               print "$name: already registered as failed; will append new message\n"
-                       if $fail_reason;
-               print "$name: already registered as failed; changing category\n"
-                       if $cat;
-       }
-
-       if (($cat eq "reminder-sent" || $cat eq "nmu-offered") &&
-               defined $pkg->{'failed_category'} &&
-               $pkg->{'failed_category'} ne $cat) {
-               (my $action = $cat) =~ s/-/ /;
-               $fail_reason .= "\n$short_date: $action";
-       }
-
-       change_state( \$pkg, 'Failed' );
-       $pkg->{'builder'} = $user;
-       $pkg->{'failed'} .= "\n" if $pkg->{'failed'};
-       $pkg->{'failed'} .= $fail_reason;
-       $pkg->{'failed_category'} = $cat if $cat;
-       if (defined $pkg->{'permbuildpri'}) {
-               $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
-       } else {
-               delete $pkg->{'buildpri'};
-       }
-       log_ta( $pkg, "--failed" );
-       update_source_info($pkg);
-       print "$name: registered as failed\n" if $verbose;
-}
 
 sub add_one_notforus {
-       my $name = shift;
-       my $version = shift;
-       my $pkg = get_source_info($name);
+       my $pkg = shift;
+       my $state = $pkg->{'state'};
+        my $name = $pkg->{'package'};
 
        if ($pkg->{'state'} eq 'Not-For-Us') {
                # reset Not-For-Us state in case it's called twice; this is
@@ -975,7 +782,6 @@ sub add_one_notforus {
                $pkg->{'package'} = $name;
                delete $pkg->{'builder'};
                delete $pkg->{'depends'};
-               delete $pkg->{'buildpri'};
                delete $pkg->{'binary_nmu_version'};
                delete $pkg->{'binary_nmu_changelog'};
                log_ta( $pkg, "--no-build" );
@@ -993,101 +799,11 @@ sub add_one_notforus {
        update_source_info($pkg);
 }
 
-sub add_one_needsbuild {
-       my $name = shift;
-       my $version = shift;
-       my $state;
-       my $pkg = get_source_info($name);
-
-       if (!defined($pkg)) {
-               print "$name: not registered; can't give back.\n";
-               return;
-       }
-       $state = $pkg->{'state'};
-
-       if ($state eq "BD-Uninstallable") {
-               if ($opt_override) {
-                       print "$name: Forcing uninstallability mark to be removed. This is not permanent and might be reset with the next trigger run\n";
-
-                       change_state( \$pkg, 'Needs-Build' );
-                       delete $pkg->{'builder'};
-                       delete $pkg->{'depends'};
-                       log_ta( $pkg, "--give-back" );
-                       update_source_info($pkg);
-                       print "$name: given back\n" if $verbose;
-                       return;
-               }
-               else {
-                       print "$name: has uninstallable build-dependencies. Skipping\n",
-                                 "  (use --override to clear dependency list and ",
-                                 "give back anyway)\n";
-                       return;
-               }
-       }
-       elsif ($state eq "Dep-Wait") {
-               if ($opt_override) {
-                       print "$name: Forcing source dependency list to be cleared\n";
-               }
-               else {
-                       print "$name: waiting for source dependencies. Skipping\n",
-                                 "  (use --override to clear dependency list and ",
-                                 "give back anyway)\n";
-                       return;
-               }
-       }
-       elsif (!isin( $state, qw(Building Built Build-Attempted))) {
-               print "$name: not taken for building (state is $state).";
-               if ($opt_override) {
-                       print "\n$name: Forcing give-back\n";
-               }
-               else {
-                       print " Skipping.\n";
-                       return;
-               }
-       }
-       if (defined ($pkg->{'builder'}) && $user ne $pkg->{'builder'} &&
-               !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user) &&
-               !$opt_override) {
-               print "$name: not taken by you, but by ".
-                         "$pkg->{'builder'}. Skipping.\n";
-               return;
-       }
-       if (!pkg_version_eq($pkg, $version)) {
-               print "$name: version mismatch ($pkg->{'version'} registered). ",
-                         "Skipping.\n";
-               return;
-       }
-       if (!defined $distributions{$distribution}{noadw}) {
-               change_state( \$pkg, 'BD-Uninstallable' );
-               $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
-       } else {
-               change_state( \$pkg, 'Needs-Build' );
-       }
-       $pkg->{'builder'} = undef;
-       $pkg->{'depends'} = undef;
-       log_ta( $pkg, "--give-back" );
-       update_source_info($pkg);
-       print "$name: given back\n" if $verbose;
-}
-
 sub set_one_binnmu {
        my $name = shift;
        my $version = shift;
-       my $pkg = get_source_info($name);
-       my $state;
-
-       if (!defined($pkg)) {
-               print "$name: not registered; can't register for binNMU.\n";
-               return;
-       }
-       my $db_ver = $pkg->{'version'};
-
-       if (!version_eq($db_ver, $version)) {
-               print "$name: version mismatch ($db_ver registered). ",
-                         "Skipping.\n";
-               return;
-       }
-       $state = $pkg->{'state'};
+       my $pkg = shift;
+       my $state = $pkg->{'state'};
 
        if (defined $pkg->{'binary_nmu_version'}) {
                if ($binNMUver == 0) {
@@ -1096,6 +812,7 @@ sub set_one_binnmu {
                        delete $pkg->{'depends'};
                        delete $pkg->{'binary_nmu_version'};
                        delete $pkg->{'binary_nmu_changelog'};
+                        delete $pkg->{'buildpri'};
                } elsif ($binNMUver <= $pkg->{'binary_nmu_version'}) {
                        print "$name: already building binNMU $pkg->{'binary_nmu_version'}\n";
                        return;
@@ -1103,13 +820,8 @@ sub set_one_binnmu {
                        $pkg->{'binary_nmu_version'} = $binNMUver;
                        $pkg->{'binary_nmu_changelog'} = $fail_reason;
                        $pkg->{'notes'} = 'out-of-date';
-                       $pkg->{'buildpri'} = $pkg->{'permbuildpri'}
-                               if (defined $pkg->{'permbuildpri'});
-                       if (defined $distributions{$distribution}{noadw}) {
-                               change_state( \$pkg, 'Installed' );
-                       } else {
-                               change_state( \$pkg, 'BD-Uninstallable' );
-                       }
+                        delete $pkg->{'buildpri'};
+                       change_state( \$pkg, 'BD-Uninstallable' );
                }
                log_ta( $pkg, "--binNMU" );
                update_source_info($pkg);
@@ -1131,123 +843,53 @@ sub set_one_binnmu {
                return;
        }
 
-       if (!defined $distributions{$distribution}{noadw}) {
-               change_state( \$pkg, 'BD-Uninstallable' );
-               $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
-       }
-       else
-       {
-               change_state( \$pkg, 'Needs-Build' );
-       }
+       change_state( \$pkg, 'BD-Uninstallable' );
        delete $pkg->{'builder'};
        delete $pkg->{'depends'};
        $pkg->{'binary_nmu_version'} = $binNMUver;
        $pkg->{'binary_nmu_changelog'} = $fail_reason;
        $pkg->{'notes'} = 'out-of-date';
+        delete $pkg->{'buildpri'};
        log_ta( $pkg, "--binNMU" );
        update_source_info($pkg);
        print "${name}: registered for binNMU $fullver\n" if $verbose;
 }
 
 sub set_one_buildpri {
-       my $name = shift;
-       my $version = shift;
        my $key = shift;
-       my $pkg = get_source_info($name);
-       my $state;
-
-       if (!defined($pkg)) {
-               print "$name: not registered; can't set priority.\n";
-               return;
-       }
-       $state = $pkg->{'state'};
+       my $pkg = shift;
+        my $name = $pkg->{'package'};
 
-       if ($state eq "Not-For-Us") {
-               print "$name: not suitable for this architecture. Skipping.\n";
-               return;
-       } elsif ($state eq "Failed-Removed") {
-               print "$name: failed previously and doesn't need building. Skipping.\n";
-               return;
-        }
-       if (!pkg_version_eq($pkg, $version)) {
-               print "$name: version mismatch ($pkg->{'version'} registered). ",
-                         "Skipping.\n";
-               return;
-       }
-       if ( $build_priority == 0 ) {
-               delete $pkg->{'buildpri'}
-                       if $key eq 'permbuildpri' and defined $pkg->{'buildpri'}
-                       and $pkg->{'buildpri'} == $pkg->{$key};
-               delete $pkg->{$key};
-       } else {
-               $pkg->{'buildpri'} = $build_priority
-                       if $key eq 'permbuildpri';
+       if ( $build_priority ) {
                $pkg->{$key} = $build_priority;
+       } else {
+               delete $pkg->{$key};
        }
        update_source_info($pkg);
        print "$name: set to build priority $build_priority\n" if $verbose;
 }
 
 sub add_one_depwait {
-       my $name = shift;
-       my $version = shift;
-       my $state;
-       my $pkg = get_source_info($name);
-
-       if (!defined($pkg)) {
-               print "$name: not registered yet.\n";
-               return;
-       }
-       $state = $pkg->{'state'};
+       my $pkg = shift;
+       my $state = $pkg->{'state'};
+        my $name = $pkg->{'package'};
 
        if ($state eq "Dep-Wait") {
                print "$name: merging with previously registered dependencies\n";
        }
        
-       if (isin( $state, qw(Needs-Build Failed BD-Uninstallable))) {
-               print "$name: Warning: not registered for building previously, ".
-                         "but processing anyway.\n";
-       }
-       elsif ($state eq "Not-For-Us") {
-               print "$name: not suitable for this architecture anyway. Skipping.\n";
-               return;
-       }
-       elsif ($state eq "Failed-Removed") {
-               print "$name: failed previously and doesn't need building. Skipping.\n";
-               return;
-       }
-       elsif ($state eq "Installed") {
-               print "$name: Is already installed in archive. Skipping.\n";
-               return;
-       }
-       elsif ($state eq "Uploaded") {
-               print "$name: Is already uploaded. Skipping.\n";
-               return;
-       }
-       elsif ($pkg->{'builder'} &&
-                  $user ne $pkg->{'builder'}) {
-               print "$name: not taken by you, but by ".
-                         "$pkg->{'builder'}. Skipping.\n";
-               return;
-       }
-       elsif ( !pkg_version_eq($pkg,$version)) {
-               print "$name: version mismatch ".
-                         "($pkg->{'version'} ".
-                         "by $pkg->{'builder'})\n";
-               return;
-       }
-       elsif ($fail_reason =~ /^\s*$/ ||
+       if (isin( $state, qw<Installed Not-For-Us>)) {
+            print "add_one_depwait: $name: skiping in state $state\n";
+            return;
+        }
+        
+       if ($fail_reason =~ /^\s*$/ ||
                   !parse_deplist( $fail_reason, 1 )) {
                print "$name: Bad dependency list\n";
                return;
        }
        change_state( \$pkg, 'Dep-Wait' );
        $pkg->{'builder'} = $user;
-       if (defined $pkg->{'permbuildpri'}) {
-               $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
-       } else {
-               delete $pkg->{'buildpri'};
-       }
        my $deplist = parse_deplist( $pkg->{'depends'} );
        my $new_deplist = parse_deplist( $fail_reason );
        # add new dependencies, maybe overwriting old entries
@@ -1255,24 +897,9 @@ sub add_one_depwait {
                $deplist->{$_} = $new_deplist->{$_};
        }
        $pkg->{'depends'} = build_deplist($deplist);
-       log_ta( $pkg, "--dep-wait" );
-       update_source_info($pkg);
-       print "$name: registered as waiting for dependencies\n" if $verbose;
-}
-
-sub set_one_update {
-       my $name = shift;
-       my $version = shift;
-       my $pkg = get_source_info($name);
-
-       if (!defined($pkg)) {
-               print "$name: not registered yet.\n";
-               return;
-       }
-        $pkg->{'version'} =~ s/\+b[0-9]+$//;
-
-       log_ta( $pkg, "--update" );
-       update_source_info($pkg);
+       log_ta( $pkg, "--dep-wait" ) unless $simulate;
+       update_source_info($pkg) unless $simulate;
+       print "$name: registered as waiting for dependencies\n" if $verbose || $simulate;
 }
 
 
@@ -1345,28 +972,18 @@ BEGIN {
        }
        $sectval{'unknown'}     = -165;
 
-       %catval =  ( "none"                           => -20,
-                                "uploaded-fixed-pkg" => -19,
-                                "fix-expected"       => -18,
-                                "reminder-sent"      => -17,
-                                "nmu-offered"        => -16,
-                                "easy"               => -15,
-                                "medium"                     => -14,
-                                "hard"                   => -13,
-                                "compiler-error"     => -12 );
 }
 
 sub sort_list_func {
     my $map_funcs = {
         'C' => ['<->', sub { return $_[0]->{'calprio'}; }],
         'W' => ['<->', sub { return $_[0]->{'state_days'}; }],
-        'P' => ['<->', sub { return $_[0]->{'buildpri'}; }],
+        'P' => ['<->', sub { return ($_[0]->{'buildpri'}//0) + ($_[0]->{'permbuildpri'}//0); }],
         'p' => ['<=>', sub { return $prioval{$_[0]->{'priority'}//""}//0; }],
         's' => ['<=>', sub { return $sectval{$_[0]->{'section'}//""}//0; }],
         'n' => ['cmp', sub { return $_[0]->{'package'}; }],
         'b' => ['cmp', sub { return $_[0]->{'builder'}; }],
         'c' => ['<=>', sub { return ($_[0]->{'notes'}//"" =~ /^(out-of-date|partial)/) ? 0: ($_[0]->{'notes'}//"" =~ /^uncompiled/) ? 2 : 1; }],
-        'f' => ['<=>', sub { return $catval{ $_[0]->{'failed_category'} ? $_[0]->{'failed_category'}: "none" }; }],
         'S' => ['<->', sub { return isin($_[0]->{'priority'}, qw(required important standard)); }],
         'T' => ['<->', sub { return $_[0]->{'state_time'} % 86400;} ], # Fractions of a day
     };
@@ -1384,7 +1001,7 @@ sub sort_list_func {
 sub calculate_prio {
        my $priomap = $yamlmap->{priority};
        my $pkg = shift;
-        my @s=split("/", $pkg->{'section'});
+        my @s=split("/", $pkg->{'section'}//"");
         $pkg->{'component'} = $s[0] if $s[1];
         $pkg->{'component'} ||= 'main';
        $pkg->{'calprio'} = 0;
@@ -1506,7 +1123,7 @@ Text could contain further %. To start with !, use %!
             no warnings;
             my $c = "$pkg->{'priority'}:$pkg->{'notes'}";
             $c .= ":PREV-FAILED" if $pkg->{'previous_state'} && $pkg->{'previous_state'} =~ /^Failed/;
-            $c .= ":bp{" . $pkg->{'buildpri'} . "}" if defined $pkg->{'buildpri'};
+            $c .= ":bp{" . (($pkg->{'buildpri'}//0)+($pkg->{'permbuildpri'}//0)) . "}" if (($pkg->{'buildpri'}//0)+($pkg->{'permbuildpri'}//0));
             $c .= ":binNMU{" . $pkg->{'binary_nmu_version'} . "}" if defined $pkg->{'binary_nmu_version'};
             $c .= ":calprio{". $pkg->{'calprio'}."}";
             $c .= ":days{". $pkg->{'state_days'}."}";
@@ -1540,15 +1157,15 @@ Text could contain further %. To start with !, use %!
 
 sub list_packages {
        my $state = shift;
-       my( $name, $pkg, @list );
+       my @list;
        my $cnt = 0;
        my %scnt;
        my $ctime = time;
 
-       my $db = get_all_source_info(state => $state, user => $user, category => $category, list_min_age => $list_min_age);
-       foreach $name (keys %$db) {
-               next if $name =~ /^_/;
-               push @list, calculate_prio($db->{$name});
+       my $db = get_all_source_info(state => $state, user => $user, list_min_age => $list_min_age, multisuite => 1);
+       foreach my $key (keys %$db) {
+               next if $key =~ /^_/;
+               push @list, calculate_prio($db->{$key});
        }
 
         # filter components
@@ -1562,7 +1179,8 @@ sub list_packages {
         $printformat ||= $yamlmap->{"format"}{"default"}{"default"};
         undef $printformat if ($ownprintformat && $ownprintformat eq 'none');
 
-       foreach $pkg (sort sort_list_func @list) {
+       foreach my $pkg (sort sort_list_func @list) {
+                no warnings;
                 if ($printformat) {
                     print print_format($printformat, $pkg, {'cnt' => $cnt, 'scnt' => \%scnt})."\n";
                    ++$cnt;
@@ -1576,9 +1194,6 @@ sub list_packages {
                        if $pkg->{'state'} ne "Needs-Build";
                print print_format(" [%X]\n", $pkg, {});
                print "  Reasons for failing:\n",
-                         "    [Category: ",
-                         defined $pkg->{'failed_category'} ? $pkg->{'failed_category'} : "none",
-                         "]\n    ",
                          join("\n    ",split("\n",$pkg->{'failed'})), "\n"
                        if $pkg->{'state'} =~ /^Failed/;
                print "  Dependencies: $pkg->{'depends'}\n"
@@ -1621,7 +1236,7 @@ sub info_packages {
                'bd_problem' => 'BD-Problem', 
                'binary_nmu_changelog' => 'Binary-NMU-Changelog', 'binary_nmu_version' => 'Binary-NMU-Version',
                'buildpri' => 'BuildPri', 'depends' => 'Depends', 'failed' => 'Failed',
-               'failed_category' => 'Failed-Category', 'notes' => 'Notes',
+               'notes' => 'Notes',
                'distribution' => 'Distribution', 'old_failed' => 'Old-Failed',
                'permbuildpri' => 'PermBuildPri', 'rel' => 'Rel',
                'calprio' => 'CalculatedPri', 'state_days' => 'State-Days', 'state_time' => 'State-Time',
@@ -1671,6 +1286,7 @@ sub info_packages {
 }
 
 sub forget_packages {
+        no warnings;
        my( $name, $pkg, $key, $data );
        
        foreach $name (@_) {
@@ -1809,6 +1425,7 @@ sub change_state {
        my $newstate = shift;
        my $state = \$pkg->{'state'};
        
+        $newstate = 'Needs-Build' if $newstate eq 'BD-Uninstallable' && $distributions{$distribution}{noadw};
        return if defined($$state) and $$state eq $newstate;
         $pkg->{'previous_state'} = $$state if defined($$state);
        $pkg->{'state_change'} = $curr_date;
@@ -1820,11 +1437,9 @@ sub change_state {
                        ($pkg->{'failed'} // ""). "\n" .
                        ($pkg->{'old_failed'} // "");
                delete $pkg->{'failed'};
-               delete $pkg->{'failed_category'};
-       }
-       if (defined($$state) and $$state eq 'BD-Uninstallable') {
-               delete $pkg->{'bd_problem'};
        }
+       delete $pkg->{'bd_problem'} if ($$state//"") eq 'BD-Uninstallable';
+        $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet" if $newstate eq 'BD-Uninstallable';
        $$state = $newstate;
 }
 
@@ -1891,6 +1506,7 @@ sub parse_deplist {
     my $verify = shift;
     my %result;
     
+    return $verify ? 0 : \%result unless $deps;
     foreach (split( /\s*,\s*/, $deps )) {
         if ($verify) {
             # verification requires > starting prompts, no | crap
@@ -2068,12 +1684,12 @@ sub call_edos_depcheck {
         # If such a "binary" package is installable, the corresponding source package is buildable.
        print $SOURCES "Package: source---$key\n";
        print $SOURCES "Version: $pkg->{'version'}\n";
-        my $t = &filterarch($srcs->{$key}{'dep'} || $srcs->{$key}{'depends'}, $arch);
-        my $tt = &filterarch($pkg->{'extra_depends'}, $arch);
+        my $t = &filterarch($srcs->{$key}{'dep'} || $srcs->{$key}{'depends'}, $args->{'arch'});
+        my $tt = &filterarch($pkg->{'extra_depends'}, $args->{'arch'});
         $t = $t ? ($tt ? "$t, $tt" : $t) : $tt;
        print $SOURCES "Depends: $t\n" if $t;
-        my $u = &filterarch($srcs->{$key}{'conf'} || $srcs->{$key}{'conflicts'}, $arch);
-        my $uu = &filterarch($pkg->{'extra_conflicts'}, $arch);
+        my $u = &filterarch($srcs->{$key}{'conf'} || $srcs->{$key}{'conflicts'}, $args->{'arch'});
+        my $uu = &filterarch($pkg->{'extra_conflicts'}, $args->{'arch'});
         $u = $u ? ($uu ? "$u, $uu" : $u) : $uu;
        print $SOURCES "Conflicts: $u\n" if $u;
        print $SOURCES "Architecture: all\n";
@@ -2247,7 +1863,7 @@ sub pkg_history_table_name {
 sub get_readonly_source_info {
        my $name = shift;
        # SELECT FLOOR(EXTRACT('epoch' FROM age(localtimestamp, '2010-01-22  23:45')) / 86400) -- change to that?
-        my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, failed_category, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days, floor(extract(epoch from now()) - extract(epoch from state_change)) as state_time"
+        my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days, floor(extract(epoch from now()) - extract(epoch from state_change)) as state_time"
             . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution AND result = 'successful') AS successtime"
             . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution ) AS anytime"
             . ", extra_depends, extra_conflicts, build_arch_all"
@@ -2271,7 +1887,7 @@ sub get_source_info {
 sub get_all_source_info {
        my %options = @_;
 
-        my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, failed_category, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days, floor(extract(epoch from now()) - extract(epoch from state_change)) as state_time"
+        my $q = "SELECT rel, priority, state_change, permbuildpri, section, buildpri, failed, state, binary_nmu_changelog, bd_problem, version, package, distribution, installed_version, notes, builder, old_failed, previous_state, binary_nmu_version, depends, extract(days from date_trunc('days', now() - state_change)) as state_days, floor(extract(epoch from now()) - extract(epoch from state_change)) as state_time"
 #            . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution AND result = 'successful') AS successtime"
 #            . ", (SELECT max(build_time) FROM ".pkg_history_table_name()." WHERE pkg_history.package = packages.package AND pkg_history.distribution = packages.distribution ) AS anytime"
             . ", successtime.build_time as successtime, anytime.build_time as anytime, extra_depends, extra_conflicts"
@@ -2302,12 +1918,6 @@ sub get_all_source_info {
                push @args, $options{user};
        }
 
-       if ($options{category}) {
-               $q .= ' AND failed_category <> ? AND upper(state) = ? ';
-               push @args, $options{category};
-               push @args, "FAILED";
-       }
-
        if ($options{list_min_age} && $options{list_min_age} > 0) {
                $q .= ' AND age(state_change) > ? ';
                push @args, $options{list_min_age} . " days";
@@ -2318,7 +1928,20 @@ sub get_all_source_info {
                push @args, -$options{list_min_age} . " days";
        }
 
-       my $db = $dbh->selectall_hashref($q, 'package', undef, @args);
+       my $db;
+        if (($options{multisuite}) && (!$distribution || $distribution =~ / /)) {
+            # return packages in multiple suites - only for those functions marked as clean for that api change
+            $db = $dbh->selectall_hashref($q, [qw<package distribution>], undef, @args);
+            my $dbk = {};
+            foreach my $p ( keys %$db ) {
+                foreach my $d (keys %{$db->{$p}}) {
+                    $dbk->{"$p/$d"} = $db->{$p}->{$d};
+                }
+            }
+            $db = $dbk;
+        } else {
+            $db = $dbh->selectall_hashref($q, [qw<package>], undef, @args);
+        }
        return $db;
 }
 
@@ -2367,7 +1990,6 @@ sub update_source_info {
                        'old_failed = ?, ' .
                        'binary_nmu_version = ?, ' .
                        'binary_nmu_changelog = ?, ' .
-                       'failed_category = ?, ' .
                        'permbuildpri = ?, ' .
                        'buildpri = ?, ' .
                        'depends = ?, ' .
@@ -2389,7 +2011,6 @@ sub update_source_info {
                $pkg->{'old_failed'},
                $pkg->{'binary_nmu_version'},
                $pkg->{'binary_nmu_changelog'},
-               $pkg->{'failed_category'},
                $pkg->{'permbuildpri'},
                $pkg->{'buildpri'},
                $pkg->{'depends'},
@@ -2474,12 +2095,13 @@ sub parse_all_v3 {
     foreach my $name (keys %$srcs) {
         next if $name eq '_binary';
 
-        # state = installed, out-of-date, uncompiled, not-for-us, auto-not-for-us
+        # state = installed, out-of-date, uncompiled, packages-arch-specific, overwritten-by-arch-all, arch-not-in-arch-list, arch-all-only
         my $pkgs = $srcs->{$name};
+        next if isin($pkgs->{'status'}, qw <arch-all-only>);
         my $pkg = $db->{$name};
 
         unless ($pkg) {
-            next SRCS if $pkgs->{'status'} eq 'not-for-us';
+            next SRCS if $pkgs->{'status'} eq 'packages-arch-specific';
             my $logstr = sprintf("merge-v3 %s %s_%s (%s, %s):", $vars->{'time'}, $name, $pkgs->{'version'}, $vars->{'arch'}, $vars->{'suite'});
 
             # does at least one binary exist in the database and is more recent - if so, we're probably just outdated, ignore the source package
@@ -2491,18 +2113,22 @@ sub parse_all_v3 {
             }
             $pkg->{'package'}  = $name;
         }
+        $pkg->{'version'} ||= "";
+        $pkg->{'state'} ||= "";
         my $logstr = sprintf("merge-v3 %s %s_%s", $vars->{'time'}, $name, $pkgs->{'version'}).
             ($pkgs->{'binnmu'} ? ";b".$pkgs->{'binnmu'} : "").
             sprintf(" (%s, %s, previous: %s", $vars->{'arch'}, $vars->{'suite'}, $pkg->{'version'}//"").
             ($pkg->{'binary_nmu_version'} ? ";b".$pkg->{'binary_nmu_version'} : "").
-            ", $pkg->{'state'}):";
+            ", $pkg->{'state'}".($pkg->{'notes'} ? "/".$pkg->{'notes'} : "")."):";
 
-        if (isin($pkgs->{'status'}, qw (installed related)) && $pkgs->{'version'} eq $pkg->{'version'} && $pkgs->{'binnmu'}//0 < int($pkg->{'binary_nmu_version'}//0)) {
+        if (isin($pkgs->{'status'}, qw (installed related)) && $pkgs->{'version'} eq $pkg->{'version'} && ($pkgs->{'binnmu'}//0) < int($pkg->{'binary_nmu_version'}//0)) {
                 $pkgs->{'status'} = 'out-of-date';
         }
-        if (isin($pkgs->{'status'}, qw (installed related auto-not-for-us))) {
+        if (isin($pkgs->{'status'}, qw <installed related arch-not-in-arch-list packages-arch-specific overwritten-by-arch-all arch-all-only>)) {
             my $change = 0;
-            my $tstate = {'installed' => 'Installed', 'related' => 'Installed', 'auto-not-for-us' => 'Auto-Not-For-Us'}->{$pkgs->{'status'}};
+            my $tstate = {'installed' => 'Installed', 'related' => 'Installed', 
+                'arch-not-in-arch-list' => 'Auto-Not-For-Us', 'packages-arch-specific' => 'Auto-Not-For-Us', 'overwritten-by-arch-all' => 'Auto-Not-For-Us', 'arch-all-only' => 'Auto-Not-For-Us',
+                }->{$pkgs->{'status'}};
             next if isin( $pkg->{'state'}, qw<Not-For-Us Failed Failed-Removed Dep-Wait Dep-Wait-Removed>) && isin( $tstate, qw<Auto-Not-For-Us>);
             # if the package is currently current, the status is Installed, not not-for-us
             if ($pkg->{'state'} ne $tstate) {
@@ -2522,9 +2148,12 @@ sub parse_all_v3 {
                     $change++;
                 }
             }
-            if (isin($pkgs->{'status'}, qw (related)) and $pkg->{'notes'} ne "related") {
-                $pkg->{'notes'} = "related";
-                $change++;
+            if (isin($pkgs->{'status'}, qw <related packages-arch-specific overwritten-by-arch-all arch-not-in-arch-list arch-all-only>)) {
+                my $tnotes = $pkgs->{'status'};
+                if (($pkg->{'notes'}//"") ne $tnotes) {
+                    $pkg->{'notes'} = $tnotes;
+                    $change++;
+                }
             }
             if ($change) {
                 print "$logstr set to $tstate/".($pkg->{'notes'}//"")."\n" if $verbose || $simulate;
@@ -2534,21 +2163,6 @@ sub parse_all_v3 {
             next;
         }
 
-        if ($pkgs->{'status'} eq 'not-for-us') {
-            next if isin( $pkg->{'state'}, qw(Not-For-Us Installed Failed-Removed));
-
-            if (isin( $pkg->{'state'}, qw(Failed Build-Attempted Built))) {
-                change_state( \$pkg, "Failed-Removed" );
-               log_ta( $pkg, "--merge-v3: Failed-Removed" ) unless $simulate;
-               update_source_info($pkg) unless $simulate;
-                print "$logstr (virtually) deleted from database\n" if $verbose || $simulate;
-                next;
-            }
-
-            print "$logstr should delete (not-for-us according to P-a-s)\n" if $verbose || $simulate || 1; # not implemented yet on purpose
-            next;
-        }
-
         # only uncompiled / out-of-date are left, so check if anything new
         if (!(isin($pkgs->{'status'}, qw (uncompiled out-of-date)))) {
             print "$logstr package in unknown state: $pkgs->{'status'}\n";
@@ -2580,6 +2194,7 @@ sub parse_all_v3 {
         delete $pkg->{'builder'};
         delete $pkg->{'binary_nmu_version'} unless $pkgs->{'binnmu'};
         delete $pkg->{'binary_nmu_changelog'} unless $pkgs->{'binnmu'};
+        delete $pkg->{'buildpri'};
         log_ta( $pkg, "--merge-v3: needs-build" ) unless $simulate;
         update_source_info($pkg) unless $simulate;
         print "$logstr set to needs-builds\n" if $simulate || $verbose;