]> git.donarmstrong.com Git - wannabuild.git/blobdiff - bin/wanna-build
wanna-build: ignore partial package overwrite with arch:all
[wannabuild.git] / bin / wanna-build
index 1de5e920a53155502dbafa70598a1458f5105721..6845901f17ad360e79e793d4df690732b9a20cc7 100755 (executable)
@@ -3,6 +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>
 # 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,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
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License as
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 #
+use strict;
+use warnings;
+use 5.010;
+
+die "wanna-build disabled" if -f "/org/wanna-build/NO-WANNA-BUILD";
 
 package conf;
 
 package conf;
+
+use vars qw< $basedir $dbbase $transactlog $mailprog $buildd_domain >;
 # defaults
 $basedir ||= "/var/lib/debbuild";
 $dbbase ||= "build-db";
 # defaults
 $basedir ||= "/var/lib/debbuild";
 $dbbase ||= "build-db";
@@ -31,28 +39,47 @@ die "dbbase is empty\n" if ! $dbbase;
 die "transactlog is empty\n" if ! $transactlog;
 die "mailprog binary $conf::mailprog does not exist or isn't executable\n"
        if !-x $conf::mailprog;
 die "transactlog is empty\n" if ! $transactlog;
 die "mailprog binary $conf::mailprog does not exist or isn't executable\n"
        if !-x $conf::mailprog;
-die "no distributions defined\n" if ! %distributions;
 package main;
 
 package main;
 
-use strict;
 use POSIX;
 use FileHandle;
 use File::Copy;
 use DBI;
 use POSIX;
 use FileHandle;
 use File::Copy;
 use DBI;
-use lib '/org/wanna-build/bin';
+use Getopt::Long qw ( :config gnu_getopt );
+use lib '/org/wanna-build/lib';
+#use lib 'lib';
 use WannaBuild;
 use YAML::Tiny;
 use Data::Dumper;
 use Hash::Merge qw ( merge );
 use WannaBuild;
 use YAML::Tiny;
 use Data::Dumper;
 use Hash::Merge qw ( merge );
+use String::Format;
+use Date::Parse;
+use List::Util qw[max];
+use Dpkg::Version (); # import nothing
+if ( defined $Dpkg::Version::VERSION ) {
+    *vercmp = \&Dpkg::Version::version_compare;
+} else {
+    *vercmp = \&Dpkg::Version::vercmp;
+}
+
+use Dpkg::Deps; # TODO: same
 
 our ($verbose, $mail_logs, $list_order, $list_state,
     $curr_date, $op_mode, $user, $real_user, $distribution,
 
 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,
     %prioval, %sectval,
     $info_all_dists, $arch,
-    $category, %catval, %short_category,
-    $short_date, $list_min_age, $dbbase, @curr_time,
-    $build_priority, %new_vers, $binNMUver, %merge_srcvers, %merge_binsrc);
+    $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, $actions,
+    $sshwrapper,
+    );
+our $Pas = '/org/buildd.debian.org/etc/packages-arch-specific/Packages-arch-specific';
+our $simulate = 0;
+our $simulate_edos = 0;
+our $api = undef; # allow buildds to specify an different api
+our $recorduser = undef;
 
 # global vars
 $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:/org/wanna-build/bin/";
 
 # global vars
 $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:/org/wanna-build/bin/";
@@ -64,201 +91,197 @@ $curr_date = strftime("%Y %b %d %H:%M:%S",@curr_time);
 $short_date = strftime("%m/%d/%y",@curr_time);
 $| = 1;
 
 $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",
-                                 "merge-quinn"         => "merge-quinn",
-                                 "merge-packages"  => "merge-packages",
-                                 "merge-sources"   => "merge-sources",
-                                 "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
-        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' },
-        "merge-quinn"  => { mode => "merge-quinn" },
-        "merge-partial-quinn" => { mode => "merge-partial-quinn" },
-        "merge-packages" => { mode => "merge-packages" },
-        "merge-sources" => { mode => "merge-sources" },
-        "pretend-avail" => { short => "p", mode => "pretend-avail" },
-        "merge-all"     => { mode => "merge-all" },
-        "merge-all-secondary" => { mode => "merge-all-secondary" },
-        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 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 !~ /^[PSpsncbCW]+$/;
-          } },
-        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;
-                                                } },
-        # special actions
-        export         => { arg => \$export_to, mode => "export" },
-        import         => { arg => \$import_from, mode => "import" },
-        "manual-edit"  => { mode => "manual-edit" },
-        );
-
-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';
+            }
+            $privmode = 1 if $distribution =~ /security/;
+        }
+    },
+    '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;
+
+END {
+       if (defined $dbh)
+       {
+               $dbh->disconnect or warn $dbh->errstr;
        }
 }
 
        }
 }
 
-$op_mode = $category ? "set-failed" : "set-building"
-       if !$op_mode; # default operation
-$list_order = $list_state eq "failed" ? 'fPcpsn' : 'PScpsn'
-       if !$list_order and $list_state;
-$distribution ||= "unstable";
-die "Bad distribution '$distribution'\n"
-       if !isin($distribution, keys %conf::distributions);
+my $schema_suffix = '';
+if ((isin( $op_mode, qw(list info distribution-architectures distribution-aliases)) && !$recorduser && !$privmode) || $simulate) {
+       $dbh = DBI->connect("DBI:Pg:service=wanna-build") || 
+               die "FATAL: Cannot open database: $DBI::errstr\n";
+       $schema_suffix = '_public';
+}
+else
+{
+       $dbh = DBI->connect("DBI:Pg:service=wanna-build-privileged") || 
+               die "FATAL: Cannot open database: $DBI::errstr\n";
+}
+
+# TODO: This shouldn't be needed, file a bug.
+$dbh->{pg_server_prepare} = 0;
+
+$dbh->begin_work or die $dbh->errstr;
 
 
-# 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;
+my $q = 'SELECT distribution, public, auto_dep_wait, build_dep_resolver, suppress_successful_logs, archive FROM distributions';
+my $rows = $dbh->selectall_hashref($q, 'distribution');
+foreach my $name (keys %$rows) {
+       $distributions{$name} = {};
+       $distributions{$name}->{'noadw'} = 1 if !($rows->{$name}->{'auto_dep_wait'});
+       $distributions{$name}->{'hidden'} = 1 if !($rows->{$name}->{'public'});
+       $distributions{$name}->{'build_dep_resolver'} = $rows->{$name}->{'build_dep_resolver'} if $rows->{$name}->{'build_dep_resolver'};
+       $distributions{$name}->{'suppress_successful_logs'} = $rows->{$name}->{'suppress_successful_logs'} if $rows->{$name}->{'suppress_successful_logs'};
+       $distributions{$name}->{'archive'} = $rows->{$name}->{'archive'} if $rows->{$name}->{'archive'};
+}
+
+$q = 'SELECT alias, distribution FROM distribution_aliases';
+$rows = $dbh->selectall_hashref($q, 'alias');
+foreach my $name (keys %$rows) {
+       $distribution_aliases{$name} = $rows->{$name}->{'distribution'};
+}
+$distribution = $distribution_aliases{$distribution} if (isin($distribution, keys %distribution_aliases));
+
+$op_mode ||= "set-building";
+if ($distribution) {
+    my @dists = split(/[, ]+/, $distribution);
+    foreach my $dist (@dists) {
+        die "Bad distribution '$distribution'\n"
+           if !isin($dist, keys %distributions, "any");
+    }
+}
+if (!isin ( $op_mode, qw(list) ) && ( ($distribution//"") =~ /[ ,]/)) {
+    die "multiple distributions are only allowed for list";
+}
 
 # TODO: Check that it's an known arch (for that dist), and give
 # a proper error.
 
 if ($verbose) {
 
 # TODO: Check that it's an known arch (for that dist), and give
 # a proper error.
 
 if ($verbose) {
-       my $version = '$Revision: db181a534e9d $ $Date: 2008/03/26 06:20:22 $ $Author: rmurray $';
-       $version =~ s/(^\$| \$ .*$)//g;
-       print "wanna-build $version for $distribution on $arch\n";
+       my $version = '$Id$';
+       $version =~ s/^.* ([a-f0-9]+) .*$/$1/g;
+       print "wanna-build $version for ".($distribution//"sid")." on $arch\n";
 }
 
 if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import export
                                  merge-packages manual-edit
 }
 
 if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import export
                                  merge-packages manual-edit
-                                 merge-sources))) {
+                                 merge-sources distribution-architectures
+                                 distribution-aliases))) {
        warn "No packages given.\n";
        usage();
 }
        warn "No packages given.\n";
        usage();
 }
@@ -269,7 +292,7 @@ die "Can't determine your user name\n"
           !($user = $real_user);
 
 if (!$fail_reason) {
           !($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;
                print "Enter reason for failing (end with '.' alone on ".
                      "its line):\n";
                my $line;
@@ -301,61 +324,39 @@ if (!$fail_reason) {
 my $yamlmap = ();
 my $yamldir = "/org/wanna-build/etc/yaml";
 my @files = ('wanna-build.yaml');
 my $yamlmap = ();
 my $yamldir = "/org/wanna-build/etc/yaml";
 my @files = ('wanna-build.yaml');
-if ($user =~ /(buildd.*)-/) { push (@files, "$1.yaml") };
-push ( @files, "$user.yaml");
+if ((getpwuid($>))[7]) { push (@files, ((getpwuid($>))[7])."/.wanna-build.yaml"); }
+if ($user && $user =~ /(buildd.*)-/) { push (@files, "$1.yaml") };
+if ($user) { push ( @files, "$user.yaml"); }
 foreach my $file (@files) {
 foreach my $file (@files) {
-       if ($verbose >= 2) { print "Trying to read $file ...\n"; }
-       next unless -f $yamldir."/".$file;
-       if ($verbose >= 2) { print "Read $file ...\n"; }
-       my $m = YAML::Tiny->read( $yamldir."/".$file )->[0];
+        my $cfile = File::Spec->rel2abs( $file, $yamldir );
+       if ($verbose >= 2) { print "Trying to read $file ($cfile) ...\n"; }
+       next unless -f $cfile;
+       if ($verbose >= 2) { print "Read $file ($cfile) ...\n"; }
+       my $m = YAML::Tiny->read( $cfile )->[0];
        $yamlmap = merge($m, $yamlmap);
 }
 if (not $yamlmap) {
        die "FATAL: no configuration found\n";
 }
        $yamlmap = merge($m, $yamlmap);
 }
 if (not $yamlmap) {
        die "FATAL: no configuration found\n";
 }
+$list_order = $yamlmap->{"list-order"}{$list_state} if !$list_order and $list_state;
+$list_order ||= $yamlmap->{"list-order"}{'default'};
+$api //= $yamlmap->{"api"};
+$api //= 0;
 
 
-my $dbh;
-
-END {
-       if (defined $dbh)
-       {
-               $dbh->disconnect or warn $dbh->errstr;
-       }
-}
-
-my $schema_suffix = '';
-if (isin( $op_mode, qw(list info)) && $distribution !~ /security/ && !(not -t and $user =~ /-/)) {
-       $dbh = DBI->connect("DBI:Pg:service=wanna-build") || 
-               die "FATAL: Cannot open database: $DBI::errstr\n";
-       $schema_suffix = '_public';
-}
-else
-{
-       $dbh = DBI->connect("DBI:Pg:service=wanna-build-privileged") || 
-               die "FATAL: Cannot open database: $DBI::errstr\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";
 }
 }
-
-# TODO: This shouldn't be needed, file a bug.
-$dbh->{pg_server_prepare} = 0;
-
-$dbh->begin_work or die $dbh->errstr;
-
-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<distribution-architectures distribution-aliases>)) {
+    die "need an architecture" unless $arch;
+    my $rows = $dbh->selectall_hashref('SELECT distribution as d from distribution_architectures where architecture=? and distribution=?', [qw<d>], undef, ($arch, $distribution//"sid")) if ($distribution//"") ne 'any';
+    $rows = $dbh->selectall_hashref('SELECT distribution as d from distribution_architectures where architecture=?', [qw<d>], undef, ($arch,)) unless $rows;
+    die "architecture ($arch) does not exist (at least not for ".($distribution//"sid").")" if !keys %$rows and $distribution//"sid" ne 'any';
+    die "architecture ($arch) does not exist" if !keys %$rows;
 }
 
 }
 
-exit 0;
-
-
-sub process {
+my $suite = $distribution;
+$distribution ||='sid';
+undef $distribution if $distribution eq 'any';
 
        SWITCH: foreach ($op_mode) {
                /^set-(.+)/ && do {
 
        SWITCH: foreach ($op_mode) {
                /^set-(.+)/ && do {
@@ -371,9 +372,6 @@ sub process {
                        last SWITCH;
                };
                /^forget-user/ && do {
                        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;
                };
                        forget_users( @ARGV );
                        last SWITCH;
                };
@@ -381,87 +379,39 @@ sub process {
                        forget_packages( @ARGV );
                        last SWITCH;
                };
                        forget_packages( @ARGV );
                        last SWITCH;
                };
-               /^merge-partial-quinn/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
-                       lock_table();
-                       parse_quinn_diff(1);
-                       last SWITCH;
-               };
-               /^merge-quinn/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
-                       lock_table();
-                       parse_quinn_diff(0);
-                       last SWITCH;
-               };
-               /^merge-packages/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
-                       lock_table();
-                       parse_packages(0);
-                       last SWITCH;
-               };
-               /^merge-sources/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
-                       lock_table();
-                       parse_sources(0);
-                       last SWITCH;
-               };
-               /^pretend-avail/ && do {
-                       pretend_avail( @ARGV );
-                       last SWITCH;
-               };
-               /^merge-all$/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
-                       lock_table();
-                       my @ARGS = @ARGV;
-                       @ARGV = ( $ARGS[0] );
-                       my $pkgs = parse_packages(0);
-                       @ARGV = ( $ARGS[1] );
-                       parse_quinn_diff(0);
-                       @ARGV = ( $ARGS[2] );
-                       my $srcs = parse_sources(1);
-                       call_edos_depcheck( $ARGS[0], $srcs );
-                       last SWITCH;
-               };
-               /^merge-all-secondary/ && do {
-                       die "This operation is restricted to admin users\n"
-                               if (defined @conf::admin_users and
-                                   !isin( $real_user, @conf::admin_users));
-                       # This is in case the chroot has multiple unrelated
-                       # dist, for instance unstable and experimental.
-                       # This is not for stable and proposed-updates.
-                       # The second packages file contains a combination
-                       # of all Packages files known to the buildd, the
-                       # first only for the current dist.
-                       lock_table();
-                       my @ARGS = @ARGV;
-                       @ARGV = ( $ARGS[0] );
-                       my $pkgs = parse_packages(0);
-                       @ARGV = ( $ARGS[3] );
-                       my $pkgs = parse_packages(1);
-                       @ARGV = ( $ARGS[1] );
-                       parse_quinn_diff(0);
-                       @ARGV = ( $ARGS[2] );
-                       my $srcs = parse_sources(1);
-                       call_edos_depcheck( $ARGS[3], $srcs );
-                       last SWITCH;
+               /^merge-v3/ && do {
+                        # 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;
+                        my $replacemap = { '%ARCH%' => $arch, '%SUITE%' => $distribution };
+                        map { my $k = $_; grep { $k =~ s,$_,$replacemap->{$_}, } keys %{$replacemap}; $_ = $k; } @ARGV;
+                        my @ipkgs = &parse_argv( \@ARGV, '.'); # installed packages
+                        my @isrcs = &parse_argv( \@ARGV, '.'); # installed sources
+                        my @bpkgs = &parse_argv( \@ARGV, '.'); # packages available for building (edos-debcheck)
+                        my @psrcs = &parse_argv( \@ARGV, '.'); # consider as installed sources
+                        use WB::QD;
+                        my $srcs = WB::QD::readsourcebins($arch, $Pas, \@isrcs, \@ipkgs);
+                        if (@psrcs) {
+                            # Installed sources of the base suite: only add them as related, not
+                            # installed; skip the entries if we got something in installed
+                            # sources already.
+                            my $psrcs = WB::QD::readsourcebins($arch, $Pas, \@psrcs, []);
+                            foreach my $k (keys %$$psrcs) {
+                                next if $$srcs->{$k};
+                                my $pkg = $$psrcs->{$k};
+                                $pkg->{'status'} = 'related';
+                                $$srcs->{$k} = $pkg;
+                            }
+                        }
+                        parse_all_v3($$srcs, {'arch' => $arch, 'suite' => $distribution, 'time' => $curr_date});
+                        # The packages passed to edos-debcheck are normally the binaries available,
+                        # unless you've also a base suite the builder will take packages from.
+                        @bpkgs = @ipkgs unless @bpkgs;
+                        call_edos_depcheck( {'arch' => $arch, 'pkgs' => \@bpkgs, 'srcs' => $$srcs, 'depwait' => 1 });
+                        last SWITCH;
                };
                /^import/ && do {
                };
                /^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 );
                                or die $dbh->errstr;
                        forget_users();
                        read_db( $import_from );
@@ -471,10 +421,18 @@ sub process {
                        export_db( $export_to );
                        last SWITCH;
                };
                        export_db( $export_to );
                        last SWITCH;
                };
+               /^distribution-architectures/ && do {
+                       show_distribution_architectures({'suite' => $suite});
+                       last SWITCH;
+               };
+               /^distribution-aliases/ && do {
+                       show_distribution_aliases();
+                       last SWITCH;
+               };
 
                die "Unexpected operation mode $op_mode\n";
        }
 
                die "Unexpected operation mode $op_mode\n";
        }
-       if (not -t and $user =~ /-/) {
+       if ($recorduser) {
                my $userinfo = get_user_info($user);
                if (!defined $userinfo)
                {
                my $userinfo = get_user_info($user);
                if (!defined $userinfo)
                {
@@ -485,59 +443,166 @@ sub process {
                        update_user_info($user);
                }
        }
                        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" );
+}
+
+exit 0;
+
+
+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 {
 }
 
 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 $newstate = shift;
+    my( $package, $name, $version, $ok, $reason );
 
 
-               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 );
-               }
+    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 {
 }
 
 sub add_one_building {
@@ -546,19 +611,16 @@ sub add_one_building {
        my( $ok, $reason );
 
        $ok = 1;
        my( $ok, $reason );
 
        $ok = 1;
-       my $pkg = get_source_info($name);
+       my $pkg = shift;
        if (defined($pkg)) {
        if (defined($pkg)) {
-               if ($pkg->{'state'} eq "Not-For-Us") {
-                       $ok = 0;
-                       $reason = "not suitable for this architecture";
-               }
-               elsif ($pkg->{'state'} =~ /^Dep-Wait/) {
+            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;
                        $ok = 0;
-                       $reason = "not all source dependencies available yet";
-               }
-               elsif ($pkg->{'state'} =~ /^BD-Uninstallable/) {
-                       $ok = 0;
-                       $reason = "source dependencies are not installable";
+                       $reason = $pkgnack->{$pkg->{'state'}};
                }
                elsif ($pkg->{'state'} eq "Uploaded" &&
                           (version_lesseq($version, $pkg->{'version'}))) {
                }
                elsif ($pkg->{'state'} eq "Uploaded" &&
                           (version_lesseq($version, $pkg->{'version'}))) {
@@ -659,6 +721,7 @@ sub add_one_building {
                }
        }
        if ($ok) {
                }
        }
        if ($ok) {
+            if ($api < 1) {
                my $ok = 'ok';
                if ($pkg->{'binary_nmu_version'}) {
                        print "$name: Warning: needs binary NMU $pkg->{'binary_nmu_version'}\n" .
                my $ok = 'ok';
                if ($pkg->{'binary_nmu_version'}) {
                        print "$name: Warning: needs binary NMU $pkg->{'binary_nmu_version'}\n" .
@@ -669,228 +732,41 @@ sub add_one_building {
                                if $pkg->{'previous_state'} =~ /^Failed/ ||
                                   $pkg->{'state'} =~ /^Failed/;
                }
                                if $pkg->{'previous_state'} =~ /^Failed/ ||
                                   $pkg->{'state'} =~ /^Failed/;
                }
+               print "$name: $ok\n" if $verbose;
+            } else {
+                print  "- $name:\n";
+                print  "    - status: ok\n";
+                printf "    - pkg-ver: %s_%s\n", $name, $version;
+                print  "    - binNMU: $pkg->{'binary_nmu_version'}\n" if $pkg->{'binary_nmu_version'};
+                print  "    - extra-changelog: $pkg->{'binary_nmu_changelog'}\n" if $pkg->{'binary_nmu_changelog'} && $pkg->{'binary_nmu_version'};
+                print  "    - extra-depends: $pkg->{'extra_depends'}\n" if $pkg->{'extra_depends'};
+                print  "    - extra-conflicts: $pkg->{'extra_conflicts'}\n" if $pkg->{'extra_conflicts'};
+                print  "    - archive: $distributions{$distribution}->{'archive'}\n" if $distributions{$distribution}->{'archive'};
+                print  "    - build_dep_resolver: $distributions{$distribution}->{'build_dep_resolver'}\n" if $distributions{$distribution}->{'build_dep_resolver'};
+                print  "    - arch_all: $pkg->{'build_arch_all'}\n" if $pkg->{'build_arch_all'};
+                print  "    - suppress_successful_logs: $distributions{$distribution}->{'suppress_successful_logs'}\n" if $distributions{$distribution}->{'suppress_successful_logs'};
+            }
                change_state( \$pkg, 'Building' );
                $pkg->{'package'} = $name;
                $pkg->{'version'} = $version;
                $pkg->{'builder'} = $user;
                log_ta( $pkg, "--take" );
                update_source_info($pkg);
                change_state( \$pkg, 'Building' );
                $pkg->{'package'} = $name;
                $pkg->{'version'} = $version;
                $pkg->{'builder'} = $user;
                log_ta( $pkg, "--take" );
                update_source_info($pkg);
-               print "$name: $ok\n" if $verbose;
        }
        else {
        }
        else {
+            if ($api < 1) {
                print "$name: NOT OK!\n  $reason\n";
                print "$name: NOT OK!\n  $reason\n";
+            } else {
+               print "- $name:\n    - status: not ok\n    - reason: \"$reason\"\n";
+            }
        }
 }
 
        }
 }
 
-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 {
 
 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
 
        if ($pkg->{'state'} eq 'Not-For-Us') {
                # reset Not-For-Us state in case it's called twice; this is
@@ -919,7 +795,6 @@ sub add_one_notforus {
                $pkg->{'package'} = $name;
                delete $pkg->{'builder'};
                delete $pkg->{'depends'};
                $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" );
                delete $pkg->{'binary_nmu_version'};
                delete $pkg->{'binary_nmu_changelog'};
                log_ta( $pkg, "--no-build" );
@@ -937,101 +812,11 @@ sub add_one_notforus {
        update_source_info($pkg);
 }
 
        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 ($distribution eq "unstable") {
-               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;
 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) {
 
        if (defined $pkg->{'binary_nmu_version'}) {
                if ($binNMUver == 0) {
@@ -1040,6 +825,7 @@ sub set_one_binnmu {
                        delete $pkg->{'depends'};
                        delete $pkg->{'binary_nmu_version'};
                        delete $pkg->{'binary_nmu_changelog'};
                        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;
                } elsif ($binNMUver <= $pkg->{'binary_nmu_version'}) {
                        print "$name: already building binNMU $pkg->{'binary_nmu_version'}\n";
                        return;
@@ -1047,8 +833,8 @@ sub set_one_binnmu {
                        $pkg->{'binary_nmu_version'} = $binNMUver;
                        $pkg->{'binary_nmu_changelog'} = $fail_reason;
                        $pkg->{'notes'} = 'out-of-date';
                        $pkg->{'binary_nmu_version'} = $binNMUver;
                        $pkg->{'binary_nmu_changelog'} = $fail_reason;
                        $pkg->{'notes'} = 'out-of-date';
-                       $pkg->{'buildpri'} = $pkg->{'permbuildpri'}
-                               if (defined $pkg->{'permbuildpri'});
+                        delete $pkg->{'buildpri'};
+                       change_state( \$pkg, 'BD-Uninstallable' );
                }
                log_ta( $pkg, "--binNMU" );
                update_source_info($pkg);
                }
                log_ta( $pkg, "--binNMU" );
                update_source_info($pkg);
@@ -1070,123 +856,53 @@ sub set_one_binnmu {
                return;
        }
 
                return;
        }
 
-       if ($distribution eq "unstable") {
-               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->{'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 {
        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 $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;
                $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 {
        }
        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 ($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;
                   !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
        my $deplist = parse_deplist( $pkg->{'depends'} );
        my $new_deplist = parse_deplist( $fail_reason );
        # add new dependencies, maybe overwriting old entries
@@ -1194,488 +910,9 @@ sub add_one_depwait {
                $deplist->{$_} = $new_deplist->{$_};
        }
        $pkg->{'depends'} = build_deplist($deplist);
                $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 parse_sources {
-       my %pkgs;
-       my %srcver;
-       my $name;
-       my $full = shift;
-
-       my $db = get_all_source_info();
-
-       local($/) = ""; # read in paragraph mode
-       while( <> ) {
-               my( $version, $arch, $section, $priority, $builddep, $buildconf, $binaries );
-               s/\s*$//m;
-               /^Package:\s*(\S+)$/mi and $name = $1;
-               /^Version:\s*(\S+)$/mi and $version = $1;
-               /^Architecture:\s*(\S+)$/mi and $arch = $1;
-               /^Section:\s*(\S+)$/mi and $section = $1;
-               /^Priority:\s*(\S+)$/mi and $priority = $1;
-               /^Build-Depends:\s*(.*)$/mi and $builddep = $1;
-               /^Build-Conflicts:\s*(.*)$/mi and $buildconf = $1;
-                /^Binary:\s*(.*)$/mi and $binaries = $1;
-
-               next if (defined $srcver{$name} and version_less( $version, $srcver{$name} ));
-               $srcver{$name} = $version;
-
-               $pkgs{$name}{'ver'} = $version;
-               $pkgs{$name}{'bin'} = $binaries;
-               $pkgs{$name}{'dep'} = $builddep;
-               $pkgs{$name}{'conf'} = $buildconf;
-               my $pkg = $db->{$name};
-
-               if (defined $pkg) {
-                       my $change = 0;
-
-                       if ($arch eq "all" && !version_less( $version, $pkg->{'version'} )) {
-                               # package is now Arch: all, delete it from db
-                               change_state( \$pkg, 'deleted' );
-                               log_ta( $pkg, "--merge-sources" );
-                               print "$name ($pkg->{'version'}): deleted ".
-                                         "from database, because now Arch: all\n"
-                                                 if $verbose;
-                               del_source_info($name);
-                               delete $db->{$name};
-                               next;
-                       }
-
-                       # The "Version" should always be the source version --
-                       # not a possible binNMU version number.
-                       $pkg->{'version'} = $version, $change++
-                               if ($pkg->{'state'} eq 'Installed' and
-                               !version_eq( $pkg->{'version'}, $version));
-                       # Always update priority and section, if available
-                       $pkg->{'priority'} = $priority, $change++
-                               if defined $priority and (not defined($pkg->{'priority'}) or $pkg->{'priority'} ne $priority);
-
-                       $pkg->{'section'} = $section, $change++
-                               if defined $section and (not defined($pkg->{'section'}) or $pkg->{'section'} ne $section);
-
-                       update_source_info($pkg) if $change;
-               }
-       }
-        # Now that we only have the latest source version, build the list
-        # of binary packages from the Sources point of view
-        foreach $name (keys %pkgs) {
-            foreach my $bin (split( /\s*,\s*/, $pkgs{$name}{'bin'} ) ) {
-                $merge_binsrc{$bin} = $name;
-            }
-        }
-       # remove installed packages that no longer have source available
-       # or binaries installed
-        foreach $name (keys %$db) {
-               next if $name =~ /^_/;
-               my $pkg = $db->{$name};
-               if (not defined($pkgs{$name})) {
-                       change_state( \$pkg, 'deleted' );
-                       log_ta( $pkg, "--merge-sources" );
-                       print "$name ($pkg->{'version'}): ".
-                                 "deleted from database, because ".
-                                 "not in Sources anymore\n"
-                                         if $verbose;
-                       del_source_info($name);
-                       delete $db->{$name};
-               } else {
-                       next if !isin( $pkg->{'state'}, qw(Installed) );
-                       if ($full && not defined $merge_srcvers{$name}) {
-                            change_state( \$pkg, 'deleted' );
-                            log_ta( $pkg, "--merge-sources" );
-                            print "$name ($pkg->{'version'}): ".
-                                      "deleted from database, because ".
-                                      "binaries don't exist anymore\n"
-                                              if $verbose;
-                           del_source_info($name);
-                           delete $db->{$name};
-                        } elsif ($full && version_less( $merge_srcvers{$name}, $pkg->{'version'})) {
-                            print "$name ($pkg->{'version'}): ".
-                                      "package is Installed but binaries are from ".
-                                      $merge_srcvers{$name}. "\n"
-                                              if $verbose;
-                        }
-                }
-       }
-       return \%pkgs;
-}
-
-# This function looks through a Packages file and sets the state of
-# packages to 'Installed'
-sub parse_packages {
-       my $depwait_only = shift;
-       my $installed;
-
-       my $pkgs = get_all_source_info();
-       local($/) = ""; # read in paragraph mode
-       while( <> ) {
-               my( $name, $version, $depends, $source, $sourcev, $architecture, $provides, $binaryv, $binnmu );
-               s/\s*$//m;
-               /^Package:\s*(\S+)$/mi and $name = $1;
-               /^Version:\s*(\S+)$/mi and $version = $1;
-               /^Depends:\s*(.*)$/mi and $depends = $1;
-               /^Source:\s*(\S+)(\s*\((\S+)\))?$/mi and ($source,$sourcev) = ($1, $3);
-               /^Architecture:\s*(\S+)$/mi and $architecture = $1;
-               /^Provides:\s*(.*)$/mi and $provides = $1;
-               next if !$name || !$version;
-               next if ($arch ne $architecture and $architecture ne "all");
-               next if (defined ($installed->{$name}) and $installed->{$name}{'version'} ne "" and
-                       version_lesseq( $version, $installed->{$name}{'version'} ));
-               $installed->{$name}{'version'} = $version;
-               next if $depwait_only;
-               $installed->{$name}{'depends'} = $depends;
-               $installed->{$name}{'all'} = 1 if $architecture eq "all";
-               undef $installed->{$name}{'Provider'};
-               $installed->{$name}{'Source'} = $source ? $source : $name;
-               if ($provides) {
-                    foreach (split( /\s*,\s*/, $provides )) {
-                        if (not defined ($installed->{$_})) {
-                            $installed->{$_}{'version'} = "";
-                            $installed->{$_}{'Provider'} = $name;
-                        }
-                    }
-               }
-               if ( $version =~ /\+b(\d+)$/ ) {
-                    $binnmu = $1;
-               }
-               $version = $sourcev if $sourcev;
-               $binaryv = $version;
-               $binaryv =~ s/\+b\d+$//;
-               $installed->{$name}{'Sourcev'} = $sourcev ? $sourcev : $binaryv;
-               $binaryv .= "+b$binnmu" if defined($binnmu);
-
-               next if $architecture ne $arch;
-               $name = $source if $source;
-               next if defined($merge_srcvers{$name}) and $merge_srcvers{$name} eq $version;
-               $merge_srcvers{$name} = $version;
-
-               my $pkg = $pkgs->{$name};
-
-               if (defined $pkg) {
-                       if (isin( $pkg->{'state'}, qw(Not-For-Us)) ||
-                               (isin($pkg->{'state'}, qw(Installed)) &&
-                                version_lesseq($binaryv, $pkg->{'installed_version'}))) {
-                               print "Skipping $name because State == $pkg->{'state'}\n"
-                                       if $verbose >= 2;
-                               next;
-                       }
-                       if ($pkg->{'binary_nmu_version'} ) {
-                               my $nmuver = binNMU_version($pkg->{'version'}, $pkg->{'binary_nmu_version'});
-                               if (version_less( $binaryv, $nmuver )) {
-                                       print "Skipping $name ($version) because have newer ".
-                                               "version ($nmuver) in db.\n"
-                                                       if $verbose >= 2;
-                                       next;
-                               }
-                       } elsif (version_less($version, $pkg->{'version'})) {
-                               print "Skipping $name ($version) because have newer ".
-                                       "version ($pkg->{'version'}) in db.\n"
-                                               if $verbose >= 2;
-                               next;
-                       }
-
-                       if (!pkg_version_eq($pkg, $version) &&
-                          $pkg->{'state'} ne "Installed") {
-                               warn "Warning: $name: newer version than expected appeared ".
-                                        "in archive ($version vs. $pkg->{'version'})\n";
-                               delete $pkg->{'builder'};
-                       }
-
-                       if (!isin( $pkg->{'state'}, qw(Uploaded) )) {
-                               warn "Warning: Package $name was not in uploaded state ".
-                                        "before (but in '$pkg->{'state'}').\n";
-                               delete $pkg->{'builder'};
-                               delete $pkg->{'depends'};
-                       }
-               } else {
-                       $pkg = {};
-                       $pkg->{'version'} = $version;
-               }
-               
-               change_state( \$pkg, 'Installed' );
-               $pkg->{'package'} = $name;
-               $pkg->{'installed_version'} = $binaryv;
-               if (defined $pkg->{'permbuildpri'}) {
-                       $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
-               } else {
-                       delete $pkg->{'buildpri'};
-               }
-               $pkg->{'version'} = $version
-                       if version_less( $pkg->{'version'}, $version);
-               delete $pkg->{'binary_nmu_version'};
-               delete $pkg->{'binary_nmu_changelog'};
-               log_ta( $pkg, "--merge-packages" );
-               update_source_info($pkg);
-               print "$name ($version) is up-to-date now.\n" if $verbose;
-       }
-
-       check_dep_wait( "--merge-packages", $installed );
-       return $installed;
-}
-
-sub pretend_avail {
-       my ($package, $name, $version, $installed);
-       
-       foreach $package (@_) {
-               $package =~ s,^.*/,,; # strip path
-               $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
-               $package =~ s/_[\w\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;
-               }
-               $installed->{$name}{'version'} = $version;
-       }
-
-       check_dep_wait( "--pretend-avail", $installed );
-}
-
-sub check_dep_wait {
-       my $action = shift;
-       my $installed = shift;
-       
-       # check all packages in state Dep-Wait if dependencies are all
-       # available now
-       my $name;
-       my $db = get_all_source_info();
-        foreach $name (keys %$db) {
-               next if $name =~ /^_/;
-               my $pkg = $db->{$name};
-               next if $pkg->{'state'} ne "Dep-Wait";
-               my $deps = $pkg->{'depends'};
-               if (!$deps) {
-                       print "$name: was in state Dep-Wait, but with empty ",
-                                 "dependencies!\n";
-                       goto make_needs_build;
-               }
-               my $deplist = parse_deplist($deps);
-               my $new_deplist;
-               my $allok = 1;
-               my @removed_deps;
-               foreach (keys %$deplist) {
-                       if (!exists $installed->{$_} ||
-                               ($deplist->{$_}->{'rel'} && $deplist->{$_}->{'version'} &&
-                                !version_compare( $installed->{$_}{'version'},
-                                                                  $deplist->{$_}->{'rel'},
-                                                                  $deplist->{$_}->{'version'}))) {
-                               $allok = 0;
-                               $new_deplist->{$_} = $deplist->{$_};
-                       }
-                       else {
-                               push( @removed_deps, $_ );
-                       }
-               }
-               if ($allok) {
-                 make_needs_build:
-                       change_state( \$pkg, 'Needs-Build' );
-                       log_ta( $pkg, $action );
-                       delete $pkg->{'builder'};
-                       delete $pkg->{'depends'};
-                       print "$name ($pkg->{'version'}) has all ",
-                                 "dependencies available now\n" if $verbose;
-                       $new_vers{$name}++;
-                       update_source_info($pkg);
-               }
-               elsif (@removed_deps) {
-                       $pkg->{'depends'} = build_deplist( $new_deplist );
-                       print "$name ($pkg->{'version'}): some dependencies ",
-                                 "(@removed_deps) available now, but not all yet\n"
-                               if $verbose;
-                       update_source_info($pkg);
-               }
-       }
-}
-
-# This function accepts quinn-diff output (either from a file named on
-# the command line, or on stdin) and sets the packages named there to
-# state 'Needs-Build'.
-sub parse_quinn_diff {
-       my $partial = shift;
-       my %quinn_pkgs;
-       my $dubious = "";
-       
-       my $pkgs = get_all_source_info();
-
-       while( <> ) {
-               my $change = 0;
-               next if !m,^([-\w\d/]*)/                        # section
-                              ([-\w\d.+]+)_                    # package name
-                                  ([\w\d:.~+-]+)\.dsc\s*       # version
-                                  \[([^:]*):                           # priority
-                                  ([^]]+)\]\s*$,x;                     # rest of notes
-               my($section,$name,$version,$priority,$notes) = ($1, $2, $3, $4, $5);
-               $quinn_pkgs{$name}++;
-               $section ||= "unknown";
-               $priority ||= "unknown";
-               $priority = "unknown" if $priority eq "-";
-               $priority = "standard" if ($name eq "debian-installer");
-
-               my $pkg = $pkgs->{$name};
-
-               # Always update section and priority.
-               if (defined($pkg)) {
-
-                       $pkg->{'section'}  = $section, $change++ if not defined
-                               $pkg->{'section'} or $section ne "unknown";
-                       $pkg->{'priority'} = $priority, $change++ if not defined
-                               $pkg->{'priority'} or $priority ne "unknown";
-               }
-
-               if (defined($pkg) &&
-                       $pkg->{'state'} =~ /^Dep-Wait/ &&
-                       version_less( $pkg->{'version'}, $version )) {
-                       change_state( \$pkg, 'Dep-Wait' );
-                       $pkg->{'version'}  = $version;
-                       delete $pkg->{'binary_nmu_version'};
-                       delete $pkg->{'binary_nmu_changelog'};
-                       log_ta( $pkg, "--merge-quinn" );
-                       $change++;
-                       print "$name ($version) still waiting for dependencies.\n"
-                               if $verbose;
-               }
-               elsif (defined($pkg) &&
-                          $pkg->{'state'} =~ /-Removed$/ &&
-                          version_eq($pkg->{'version'}, $version)) {
-                       # reinstantiate a package that has been removed earlier
-                       # (probably due to a quinn-diff malfunction...)
-                       my $newstate = $pkg->{'state'};
-                       $newstate =~ s/-Removed$//;
-                       change_state( \$pkg, $newstate );
-                       $pkg->{'version'}  = $version;
-                       $pkg->{'notes'}    = $notes;
-                       log_ta( $pkg, "--merge-quinn" );
-                       $change++;
-                       print "$name ($version) reinstantiated to $newstate.\n"
-                               if $verbose;
-               }
-               elsif (defined($pkg) &&
-                          $pkg->{'state'} eq "Not-For-Us" &&
-                          version_less( $pkg->{'version'}, $version )) {
-                       # for Not-For-Us packages just update the version etc., but
-                       # keep the state
-                       change_state( \$pkg, "Not-For-Us" );
-                       $pkg->{'package'}  = $name;
-                       $pkg->{'version'}  = $version;
-                       $pkg->{'notes'}    = $notes;
-                       delete $pkg->{'builder'};
-                       log_ta( $pkg, "--merge-quinn" );
-                       $change++;
-                       print "$name ($version) still Not-For-Us.\n" if $verbose;
-               }
-               elsif (!defined($pkg) ||
-                          $pkg->{'state'} ne "Not-For-Us" &&
-                          (version_less( $pkg->{'version'}, $version ) ||
-                          ($pkg->{'state'} eq "Installed" && version_less($pkg->{'installed_version'}, $version)))) {
-
-                       if (defined( $pkg->{'state'} ) && isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
-                               send_mail( $pkg->{'builder'},
-                                                  "new version of $name (dist=$distribution)",
-                                                  "As far as I'm informed, you're currently ".
-                                                  "building the package $name\n".
-                                                  "in version $pkg->{'version'}.\n\n".
-                                                  "Now there's a new source version $version. ".
-                                                  "If you haven't finished\n".
-                                                  "compiling $name yet, you can stop it to ".
-                                                  "save some work.\n".
-                                                  "Just to inform you...\n".
-                                                  "(This is an automated message)\n" );
-                               print "$name: new version ($version) while building ".
-                                         "$pkg->{'version'} -- sending mail ".
-                                         "to builder ($pkg->{'builder'})\n"
-                                 if $verbose;
-                       }
-                       change_state( \$pkg, 'Needs-Build' );
-                       $pkg->{'package'}  = $name;
-                       $pkg->{'version'}  = $version;
-                       $pkg->{'section'}  = $section;
-                       $pkg->{'priority'} = $priority;
-                       $pkg->{'notes'}    = $notes;
-                       delete $pkg->{'builder'};
-                       delete $pkg->{'binary_nmu_version'};
-                       delete $pkg->{'binary_nmu_changelog'};
-                       log_ta( $pkg, "--merge-quinn" );
-                       $new_vers{$name}++;
-                       $change++;
-                       print "$name ($version) needs rebuilding now.\n" if $verbose;
-               }
-               elsif (defined($pkg) &&
-                          !version_eq( $pkg->{'version'}, $version ) &&
-                          isin( $pkg->{'state'}, qw(Installed Not-For-Us) )) {
-                       print "$name: skipping because version in db ".
-                                 "($pkg->{'version'}) is >> than ".
-                                 "what quinn-diff says ($version) ".
-                                 "(state is $pkg->{'state'})\n"
-                                         if $verbose;
-                       $dubious .= "$pkg->{'state'}: ".
-                                               "db ${name}_$pkg->{'version'} >> ".
-                                               "quinn $version\n" if !$partial;
-               }
-               elsif ($verbose >= 2) {
-                       if ($pkg->{'state'} eq "Not-For-Us") {
-                               print "Skipping $name because State == ".
-                                         "$pkg->{'state'}\n";
-                       }
-                       elsif (!version_less($pkg->{'version'}, $version)) {
-                               print "Skipping $name because version in db ".
-                                         "($pkg->{'version'}) is >= than ".
-                                         "what quinn-diff says ($version)\n";
-                       }
-               }
-               update_source_info($pkg) if $change;
-       }
-
-       if ($dubious) {
-               send_mail( $conf::db_maint,
-                                  "Dubious versions in " . table_name() . " "
-                                  . $distribution . " table",
-                                  "The following packages have a newer version in the ".
-                                  "wanna-build database\n".
-                                  "than what quinn-diff says, and this is strange for ".
-                                  "their state\n".
-                                  "It could be caused by a lame mirror, or the version ".
-                                  "in the database\n".
-                                  "is wrong.\n\n".
-                                  $dubious );
-       }
-
-       # Now re-check the DB for packages in states Needs-Build, Failed,
-       # Dep-Wait or BD-Uninstallable and remove them if they're not listed
-       # anymore by quinn-diff.
-       if ( !$partial ) {
-               my $name;
-               my $db = get_all_source_info();
-               foreach $name (keys %$db) {
-                       next if $name =~ /^_/;
-                       my $pkg = $db->{$name};
-                       next if defined $pkg->{'binary_nmu_version'};
-                       next if !isin( $pkg->{'state'},
-                                                  qw(Needs-Build Building Built Build-Attempted Uploaded Failed Dep-Wait BD-Uninstallable) );
-                       my $virtual_delete = $pkg->{'state'} eq 'Failed';
-                                                                
-                       if (!$quinn_pkgs{$name}) {
-                               change_state( \$pkg, $virtual_delete ?
-                                                         $pkg->{'state'}."-Removed" :
-                                                         'deleted' );
-                               log_ta( $pkg, "--merge-quinn" );
-                               print "$name ($pkg->{'version'}): ".
-                                         ($virtual_delete ? "(virtually) " : "") . "deleted ".
-                                         "from database, because not in quinn-diff anymore\n"
-                                                 if $verbose;
-                               if ($virtual_delete) {
-                                   update_source_info($pkg);
-                               } else {
-                                   del_source_info($name);
-                               }
-                       }
-               }
-       }
+       log_ta( $pkg, "--dep-wait" ) unless $simulate;
+       update_source_info($pkg) unless $simulate;
+       print "$name: registered as waiting for dependencies\n" if $verbose || $simulate;
 }
 
 
 }
 
 
@@ -1748,85 +985,28 @@ BEGIN {
        }
        $sectval{'unknown'}     = -165;
 
        }
        $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 {
 }
 
 sub sort_list_func {
-       my( $letter, $x, $ax, $bx );
-
-       foreach $letter (split( "", $list_order )) {
-         SWITCH: foreach ($letter) {
-                 /C/ && do {
-                       $x = $b->{'calprio'} <=> $a->{'calprio'};
-                       return $x if $x != 0;
-                       last SWITCH;
-                 };
-                 /W/ && do {
-                       $x = $b->{'state_days'} <=> $a->{'state_days'};
-                       return $x if $x != 0;
-                       last SWITCH;
-                 };
-                 /P/ && do {
-                       $x = $b->{'buildpri'} <=> $a->{'buildpri'};
-                       return $x if $x != 0;
-                       last SWITCH;
-                 };
-                 /p/ && do {
-                         $x = $prioval{$a->{'priority'}} <=> $prioval{$b->{'priority'}};
-                         return $x if $x != 0;
-                         last SWITCH;
-                 };
-                 /s/ && do {
-                         $x = $sectval{$a->{'section'}} <=> $sectval{$b->{'section'}};
-                         return $x if $x != 0;
-                         last SWITCH;
-                 };
-                 /n/ && do {
-                         $x = $a->{'package'} cmp $b->{'package'};
-                         return $x if $x != 0;
-                         last SWITCH;
-                 };
-                 /b/ && do {
-                         $x = $a->{'builder'} cmp $b->{'builder'};
-                         return $x if $x != 0;
-                         last SWITCH;
-                 };
-                 /c/ && do {
-                         $ax = ($a->{'notes'} =~ /^(out-of-date|partial)/) ? 0 :
-                                   ($a->{'notes'} =~ /^uncompiled/) ? 2 : 1;
-                         $bx = ($b->{'notes'} =~ /^(out-of-date|partial)/) ? 0 :
-                                   ($b->{'notes'} =~ /^uncompiled/) ? 2 : 1;
-                         $x = $ax <=> $bx;
-                         return $x if $x != 0;
-                         last SWITCH;
-                 };
-                 /f/ && do {
-                         my $ca = defined $a->{'failed_category'} ?
-                                 $a->{'failed_category'} : "none";
-                         my $cb = defined $b->{'failed_category'} ?
-                                 $b->{'failed_category'} : "none";
-                         $x = $catval{$ca} <=> $catval{$cb};
-                         return $x if $x != 0;
-                         last SWITCH;
-                 };
-                 /S/ && do {
-                         my $pa = $prioval{$a->{'priority'}} >
-                                 $prioval{'standard'};
-                         my $pb = $prioval{$b->{'priority'}} >
-                                 $prioval{'standard'};
-                         $x = $pa <=> $pb;
-                         return $x if $x != 0;
-                         last SWITCH;
-                 };
-         }
+    my $map_funcs = {
+        'C' => ['<->', sub { return $_[0]->{'calprio'}; }],
+        'W' => ['<->', sub { return $_[0]->{'state_days'}; }],
+        '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; }],
+        'S' => ['<->', sub { return isin($_[0]->{'priority'}, qw(required important standard)); }],
+        'T' => ['<->', sub { return $_[0]->{'state_time'} % 86400;} ], # Fractions of a day
+    };
+
+       foreach my $letter (split( //, $list_order )) {
+            my $r;
+            $r = (&{$map_funcs->{$letter}[1]}($b)//0 ) <=> (&{$map_funcs->{$letter}[1]}($a)//0 ) if $map_funcs->{$letter}[0] eq '<->';
+            $r = (&{$map_funcs->{$letter}[1]}($a)//0 ) <=> (&{$map_funcs->{$letter}[1]}($b)//0 ) if $map_funcs->{$letter}[0] eq '<=>';
+            $r = (&{$map_funcs->{$letter}[1]}($a)//"") cmp (&{$map_funcs->{$letter}[1]}($b)//"") if $map_funcs->{$letter}[0] eq 'cmp';
+            return $r if $r != 0;
        }
        return 0;
 }
        }
        return 0;
 }
@@ -1834,6 +1014,9 @@ sub sort_list_func {
 sub calculate_prio {
        my $priomap = $yamlmap->{priority};
        my $pkg = shift;
 sub calculate_prio {
        my $priomap = $yamlmap->{priority};
        my $pkg = shift;
+        my @s=split("/", $pkg->{'section'}//"");
+        $pkg->{'component'} = $s[0] if $s[1];
+        $pkg->{'component'} ||= 'main';
        $pkg->{'calprio'} = 0;
        foreach my $k (keys %{$priomap->{keys}}) {
                $pkg->{'calprio'} += $priomap->{keys}->{$k}{$pkg->{$k}} if $pkg->{$k} and $priomap->{keys}->{$k}{$pkg->{$k}};
        $pkg->{'calprio'} = 0;
        foreach my $k (keys %{$priomap->{keys}}) {
                $pkg->{'calprio'} += $priomap->{keys}->{$k}{$pkg->{$k}} if $pkg->{$k} and $priomap->{keys}->{$k}{$pkg->{$k}};
@@ -1845,6 +1028,13 @@ sub calculate_prio {
        my $scale = $priomap->{'waitingdays'}->{'scale'} || 1;
        $pkg->{'calprio'} += $days * $scale;
 
        my $scale = $priomap->{'waitingdays'}->{'scale'} || 1;
        $pkg->{'calprio'} += $days * $scale;
 
+        my $btime = max($pkg->{'anytime'}//0, $pkg->{'successtime'}//0);
+        my $bhours = $btime ? int($btime/3600) : ($priomap->{'buildhours'}->{'default'} || 2);
+       $bhours = $priomap->{'buildhours'}->{'min'} if $priomap->{'buildhours'}->{'min'} and $bhours < $priomap->{'buildhours'}->{'min'};
+       $bhours = $priomap->{'buildhours'}->{'max'} if $priomap->{'buildhours'}->{'max'} and $bhours > $priomap->{'buildhours'}->{'max'};
+       $scale = $priomap->{'buildhours'}->{'scale'} || 1;
+        $pkg->{'calprio'} -= $bhours * $scale;
+
        $pkg->{'calprio'} += $pkg->{'permbuildpri'} if  $pkg->{'permbuildpri'};
        $pkg->{'calprio'} += $pkg->{'buildpri'} if  $pkg->{'buildpri'};
 
        $pkg->{'calprio'} += $pkg->{'permbuildpri'} if  $pkg->{'permbuildpri'};
        $pkg->{'calprio'} += $pkg->{'buildpri'} if  $pkg->{'buildpri'};
 
@@ -1852,39 +1042,177 @@ sub calculate_prio {
 }
 
 
 }
 
 
+sub seconds2time {
+    my $t = shift;
+    return "" unless $t;
+    my $sec = $t % 60;
+    my $min = int($t/60) % 60;
+    my $hours = int($t / 3600);
+    return sprintf("%d:%02d:%02d", $hours, $min, $sec) if $hours;
+    return sprintf("%d:%02d", $min, $sec);
+}
+
+
+sub use_fmt {
+    my $r;
+
+    if (ref($_[0]) eq 'CODE') {
+        $r = &{$_[0]};
+    } else {
+        $r = $_[0];
+    }
+
+    shift;
+    my $t = shift;
+
+    $r ||= "";
+    return $r unless $t;
+
+    my $pkg = shift;
+    my $var = shift;
+    if (substr($t,0,1) eq '!') {
+        $t = substr($t,1);
+        return "" if $r;
+    } else {
+        return "" unless $r;
+    }
+    if ($t =~ /%/) {
+        return print_format($t, $pkg, $var);
+    }
+    return $t;
+}
+sub make_fmt { my $c = shift; my $pkg = shift; my $var = shift; return sub { use_fmt($c, $_[0], $pkg, $var); } };
+
+sub print_format {
+    my $printfmt = shift;
+    my $pkg = shift;
+    my $var = shift;
+
+=pod
+
+Within an format string, the following values are allowed (need to be preceded by %).
+This can be combined to e.g.
+wanna-build --format='wanna-build -A %a --give-back %p_%v' -A mipsel --list=failed
+
+a Architecture
+c section (e.g. libs or utils)
+D in case of BD-Uninstallable the reason for the uninstallability
+d distribution
+E in case of Dep-Wait the packages being waited on, in case of Needs-Build the number in the queue
+F in case of Failed the fail reason
+n newline
+o time of last successful build (seconds)
+O time of last successful build (formated)
+P previous state
+p Package name
+q time of last build (seconds)
+Q time of last build (formated)
+r max time of last (successful) build (seconds)
+R max time of last (successful) build (formated)
+S Package state
+s Time in this state in full seconds since epoch
+t time of state change
+T time since state change
+u Builder (e.g. buildd_mipsel-rem)
+v Package version
+V full Package version (i.e. with +b.., = %v%{+b}B%B
+X the string normally between [], e.g. optional:out-of-date:calprio{61}:days{25}
+
+%{Text}?  print Text in case ? is not empty; ? is never printed
+%{!Text}? print Text in case ? is empty; ? is never printed
+Text could contain further %. To start with !, use %!
+
+=cut
+
+    return stringf($printfmt, (
+        'p' => make_fmt( $pkg->{'package'}, $pkg, $var),
+        'a' => make_fmt( $arch, $pkg, $var),
+        's' => make_fmt( sub { return floor(str2time($pkg->{'state_change'})); }, $pkg, $var),
+        'v' => make_fmt( $pkg->{'version'}, $pkg, $var),
+        'V' => make_fmt( sub { $pkg->{'binary_nmu_version'} ? $pkg->{'version'}."+b".$pkg->{'binary_nmu_version'} : $pkg->{'version'} }, $pkg, $var),
+        'S' => make_fmt( $pkg->{'state'}, $pkg, $var),
+        'u' => make_fmt( $pkg->{'builder'}, $pkg, $var),
+        'X' => make_fmt( sub {
+            no warnings;
+            my $c = "$pkg->{'priority'}:$pkg->{'notes'}";
+            $c .= ":PREV-FAILED" if $pkg->{'previous_state'} && $pkg->{'previous_state'} =~ /^Failed/;
+            $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'}."}";
+            return $c;
+            }, $pkg, $var),
+        'c' => make_fmt( $pkg->{'section'}, $pkg, $var),
+        'P' => make_fmt( $pkg->{'previous_state'} || "unknwon", $pkg, $var),
+        'E' => make_fmt( sub { return $pkg->{'depends'} if $pkg->{'state'} eq "Dep-Wait";
+            return $var->{scnt}{'Needs-Build'} + 1 if $pkg->{'state'} eq 'Needs-Build';
+            return ""; }, $pkg, $var),
+       'F' => make_fmt( sub { return "" unless $pkg->{'failed'};
+           my $failed = $pkg->{'failed'};
+           $failed =~ s/\\/\\\\/g;
+            return $pkg->{'package'}."#".$arch."-failure\n ".
+           join("\\0a",split("\n",$failed))."\\0a\n"; }, $pkg, $var),
+        'D' => make_fmt( sub { return "" unless $pkg->{'bd_problem'};
+            return $pkg->{'package'}."#".$arch."-bd-problem\n".
+            join("\\0a",split("\n",$pkg->{'bd_problem'}))."\\0a\n"; }, $pkg, $var),
+        'B' => make_fmt( sub { return $pkg->{'binary_nmu_version'} if defined $pkg->{'binary_nmu_version'}; }, $pkg, $var),
+        'd' => make_fmt( $pkg->{'distribution'}, $pkg, $var),
+        't' => make_fmt( $pkg->{'state_change'}, $pkg, $var),
+        'T' => make_fmt( sub { return seconds2time(time() - floor(str2time($pkg->{'state_change'}))); }, $pkg, $var),
+        'o' => make_fmt( $pkg->{'successtime'}, $pkg, $var),
+        'O' => make_fmt( sub { return seconds2time ( $pkg->{'successtime'}); }, $pkg, $var),
+        'q' => make_fmt( $pkg->{'anytime'}, $pkg, $var),
+        'Q' => make_fmt( sub { return seconds2time ( $pkg->{'anytime'}); }, $pkg, $var),
+        'r' => make_fmt( sub { my $c = max($pkg->{'successtime'}//0, $pkg->{'anytime'}//0); return $c if $c; return; }, $pkg, $var),
+        'R' => make_fmt( sub { return seconds2time ( max($pkg->{'successtime'}//0, $pkg->{'anytime'}//0)); }, $pkg, $var),
+    ));
+}
+
 sub list_packages {
        my $state = shift;
 sub list_packages {
        my $state = shift;
-       my( $name, $pkg, @list );
+       my @list;
        my $cnt = 0;
        my %scnt;
        my $ctime = time;
 
        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});
        }
 
        }
 
-       foreach $pkg (sort sort_list_func @list) {
-               print "$pkg->{'section'}/$pkg->{'package'}_$pkg->{'version'}";
-               print ": $pkg->{'state'}"
+        # filter components
+        @list = grep { my $i = $_->{'component'}; grep { $i eq $_ } split /[, ]+/, $yamlmap->{"restrict"}{'component'} } @list;
+        # extra depends / conflicts only from api 1 on
+        @list = grep { !$_->{'extra_depends'} and !$_->{'extra_conflicts'} } @list if $api < 1 ;
+        # filter out packages for needs-build in noautobuild state - same could exist for weaknoautobuild if buildds would tell us what they do
+        if (($state eq 'needs-build') && ($yamlmap->{"restrict"}) && ($yamlmap->{"restrict"}{"noautobuild"})) {
+            foreach my $key (map {keys %$_} @{$yamlmap->{"restrict"}{"noautobuild"}}) {
+                @list = grep { $_->{'package'} ne $key } @list;
+            }
+        }
+
+        # first adjust ownprintformat, then set printformat accordingly
+        $printformat ||= $yamlmap->{"format"}{$ownprintformat} if $ownprintformat;
+        $printformat ||= $yamlmap->{"format"}{"default"}{$state};
+        $printformat ||= $yamlmap->{"format"}{"default"}{"default"};
+        undef $printformat if ($ownprintformat && $ownprintformat eq 'none');
+
+       foreach my $pkg (sort sort_list_func @list) {
+                no warnings;
+                if ($printformat) {
+                    print print_format($printformat, $pkg, {'cnt' => $cnt, 'scnt' => \%scnt})."\n";
+                   ++$cnt;
+                   $scnt{$pkg->{'state'}}++;
+                    next;
+                }
+               print print_format("%c/%p_%v", $pkg, {});
+               print print_format(": %S", $pkg, {})
                        if $state eq "all";
                        if $state eq "all";
-               print " by $pkg->{'builder'}"
-                       if $pkg->{'state'} ne "Needs-Build" && $pkg->{'builder'};
-               print " [$pkg->{'priority'}:$pkg->{'notes'}";
-               print ":PREV-FAILED"
-                       if $pkg->{'previous_state'} =~ /^Failed/;
-               print ":bp{" . $pkg->{'buildpri'} . "}"
-                       if defined $pkg->{'buildpri'};
-               print ":binNMU{" . $pkg->{'binary_nmu_version'} . "}"
-                       if defined $pkg->{'binary_nmu_version'};
-               print ":calprio{". $pkg->{'calprio'}."}";
-               print ":days{". $pkg->{'state_days'}."}";
-               print "]\n";
+               print print_format("%{ by }u%u", $pkg, {})
+                       if $pkg->{'state'} ne "Needs-Build";
+               print print_format(" [%X]\n", $pkg, {});
                print "  Reasons for failing:\n",
                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"
                          join("\n    ",split("\n",$pkg->{'failed'})), "\n"
                        if $pkg->{'state'} =~ /^Failed/;
                print "  Dependencies: $pkg->{'depends'}\n"
@@ -1892,25 +1220,26 @@ sub list_packages {
                print "  Reasons for BD-Uninstallable:\n    ",
                          join("\n    ",split("\n",$pkg->{'bd_problem'})), "\n"
                        if $pkg->{'state'} eq "BD-Uninstallable";
                print "  Reasons for BD-Uninstallable:\n    ",
                          join("\n    ",split("\n",$pkg->{'bd_problem'})), "\n"
                        if $pkg->{'state'} eq "BD-Uninstallable";
-               print "  Previous state was $pkg->{'previous_state'} until ",
-                         "$pkg->{'state_change'}\n"
-                       if $verbose && $pkg->{'previous_state'};
-               print "  Previous state $pkg->{'previous_state'} left $pkg->{'state_time'} ago\n"
+               print "  Previous state was $pkg->{'previous_state'}\n"
                        if $verbose && $pkg->{'previous_state'};
                print "  No previous state recorded\n"
                        if $verbose && !$pkg->{'previous_state'};
                        if $verbose && $pkg->{'previous_state'};
                print "  No previous state recorded\n"
                        if $verbose && !$pkg->{'previous_state'};
+               print "  State changed at $pkg->{'state_change'}\n"
+                       if $verbose && $pkg->{'state_change'};
+               print "  Previous state $pkg->{'previous_state'} left $pkg->{'state_time'} ago\n"
+                       if $verbose && $pkg->{'previous_state'};
                print "  Previous failing reasons:\n    ",
                      join("\n    ",split("\n",$pkg->{'old_failed'})), "\n"
                        if $verbose && $pkg->{'old_failed'};
                ++$cnt;
                $scnt{$pkg->{'state'}}++ if $state eq "all";
        }
                print "  Previous failing reasons:\n    ",
                      join("\n    ",split("\n",$pkg->{'old_failed'})), "\n"
                        if $verbose && $pkg->{'old_failed'};
                ++$cnt;
                $scnt{$pkg->{'state'}}++ if $state eq "all";
        }
-       if ($state eq "all") {
+       if ($state eq "all" && !$printformat) {
                foreach (sort keys %scnt) {
                        print "Total $scnt{$_} package(s) in state $_.\n";
                }
        }
                foreach (sort keys %scnt) {
                        print "Total $scnt{$_} package(s) in state $_.\n";
                }
        }
-       print "Total $cnt package(s)\n";
+       print "Total $cnt package(s)\n" unless $printformat;
        
 }
 
        
 }
 
@@ -1918,7 +1247,7 @@ sub info_packages {
        my( $name, $pkg, $key, $dist );
        my @firstkeys = qw(package version builder state section priority
                                           installed_version previous_state state_change);
        my( $name, $pkg, $key, $dist );
        my @firstkeys = qw(package version builder state section priority
                                           installed_version previous_state state_change);
-       my @dists = $info_all_dists ? keys %conf::distributions : ($distribution);
+       my @dists = $info_all_dists ? keys %distributions : ($distribution);
        my %beautykeys = ( 'package' => 'Package', 'version' => 'Version', 'builder' => 'Builder',
                'state' => 'State', 'section' => 'Section', 'priority' => 'Priority',
                'installed_version' => 'Installed-Version', 'previous_state' => 'Previous-State',
        my %beautykeys = ( 'package' => 'Package', 'version' => 'Version', 'builder' => 'Builder',
                'state' => 'State', 'section' => 'Section', 'priority' => 'Priority',
                'installed_version' => 'Installed-Version', 'previous_state' => 'Previous-State',
@@ -1926,10 +1255,15 @@ 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',
                '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',
                'distribution' => 'Distribution', 'old_failed' => 'Old-Failed',
                'permbuildpri' => 'PermBuildPri', 'rel' => 'Rel',
-               'calprio' => 'CalculatedPri', 'state_days' => 'State-Days'
+               'calprio' => 'CalculatedPri', 'state_days' => 'State-Days', 'state_time' => 'State-Time',
+                'successtime' => 'Success-build-time',
+                'anytime' => 'Build-time',
+                'extra_depends' => 'Extra-Dependencies',
+                'extra_conflicts' => 'Extra-Conflicts',
+                'build_arch_all' => 'Build-Arch-All',
                         );
        
        foreach $name (@_) {
                         );
        
        foreach $name (@_) {
@@ -1971,6 +1305,7 @@ sub info_packages {
 }
 
 sub forget_packages {
 }
 
 sub forget_packages {
+        no warnings;
        my( $name, $pkg, $key, $data );
        
        foreach $name (@_) {
        my( $name, $pkg, $key, $data );
        
        foreach $name (@_) {
@@ -2010,11 +1345,11 @@ sub read_db {
        my $file = shift;
 
        print "Reading ASCII database from $file..." if $verbose >= 1;
        my $file = shift;
 
        print "Reading ASCII database from $file..." if $verbose >= 1;
-       open( F, "<$file" ) or
+       open( my $fh, '<', $file ) or
                die "Can't open database $file: $!\n";
 
        local($/) = ""; # read in paragraph mode
                die "Can't open database $file: $!\n";
 
        local($/) = ""; # read in paragraph mode
-       while( <F> ) {
+       while( <$fh> ) {
                my( %thispkg, $name );
                s/[\s\n]+$//;
                s/\n[ \t]+/\376\377/g;  # fix continuation lines
                my( %thispkg, $name );
                s/[\s\n]+$//;
                s/\n[ \t]+/\376\377/g;  # fix continuation lines
@@ -2042,7 +1377,7 @@ sub read_db {
                                or die $dbh->errstr;
                 }
        }
                                or die $dbh->errstr;
                 }
        }
-       close( F );
+       close( $fh );
        print "done\n" if $verbose >= 1;
 }
 
        print "done\n" if $verbose >= 1;
 }
 
@@ -2073,7 +1408,7 @@ sub check_entry {
        die "Bad state $pkg->{'state'} of package $pkg->{Package}\n"
                if !isin( $pkg->{'state'},
                                  qw(Needs-Build Building Built Build-Attempted Uploaded Installed Dep-Wait Dep-Wait-Removed
        die "Bad state $pkg->{'state'} of package $pkg->{Package}\n"
                if !isin( $pkg->{'state'},
                                  qw(Needs-Build Building Built Build-Attempted Uploaded Installed Dep-Wait Dep-Wait-Removed
-                                        Failed Failed-Removed Not-For-Us BD-Uninstallable
+                                        Failed Failed-Removed Not-For-Us BD-Uninstallable Auto-Not-For-Us
                                         ) );
 }
 
                                         ) );
 }
 
@@ -2082,7 +1417,7 @@ sub export_db {
        my($name,$pkg,$key);
 
        print "Writing ASCII database to $file..." if $verbose >= 1;
        my($name,$pkg,$key);
 
        print "Writing ASCII database to $file..." if $verbose >= 1;
-       open( F, ">$file" ) or
+       open( my $fh, '>', $file ) or
                die "Can't open export $file: $!\n";
 
         my $db = get_all_source_info();
                die "Can't open export $file: $!\n";
 
         my $db = get_all_source_info();
@@ -2095,11 +1430,11 @@ sub export_db {
                        $val =~ s/\n*$//;
                        $val =~ s/^/ /mg;
                        $val =~ s/^ +$/ ./mg;
                        $val =~ s/\n*$//;
                        $val =~ s/^/ /mg;
                        $val =~ s/^ +$/ ./mg;
-                       print F "$key: $val\n";
+                       print $fh "$key: $val\n";
                }
                }
-               print F "\n";
+               print $fh "\n";
        }
        }
-       close( F );
+       close( $fh );
        print "done\n" if $verbose >= 1;
 }
 
        print "done\n" if $verbose >= 1;
 }
 
@@ -2109,6 +1444,7 @@ sub change_state {
        my $newstate = shift;
        my $state = \$pkg->{'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;
        return if defined($$state) and $$state eq $newstate;
         $pkg->{'previous_state'} = $$state if defined($$state);
        $pkg->{'state_change'} = $curr_date;
@@ -2117,14 +1453,12 @@ sub change_state {
        if (defined($$state) and $$state eq 'Failed') {
                $pkg->{'old_failed'} =
                        "-"x20 . " $pkg->{'version'} " . "-"x20 . "\n" .
        if (defined($$state) and $$state eq 'Failed') {
                $pkg->{'old_failed'} =
                        "-"x20 . " $pkg->{'version'} " . "-"x20 . "\n" .
-                       $pkg->{'failed'} . "\n" .
-                       $pkg->{'old_failed'};
+                       ($pkg->{'failed'} // ""). "\n" .
+                       ($pkg->{'old_failed'} // "");
                delete $pkg->{'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;
 }
 
        $$state = $newstate;
 }
 
@@ -2140,6 +1474,13 @@ sub log_ta {
                   "changed from $prevstate to $pkg->{'state'} ".
                   "by $real_user as $user";
        
                   "changed from $prevstate to $pkg->{'state'} ".
                   "by $real_user as $user";
        
+        if ($simulate) {
+            printf "update transactions: %s %s %s %s %s %s %s %s\n",
+               $pkg->{'package'}, $distribution,
+               $pkg->{'version'}, $action, $prevstate, $pkg->{'state'},
+                $real_user, $user;
+            return;
+        }
        $dbh->do('INSERT INTO ' . transactions_table_name() .
                        ' (package, distribution, version, action, ' .
                        ' prevstate, state, real_user, set_user, time) ' .
        $dbh->do('INSERT INTO ' . transactions_table_name() .
                        ' (package, distribution, version, action, ' .
                        ' prevstate, state, real_user, set_user, time) ' .
@@ -2169,13 +1510,13 @@ sub send_mail {
        $to .= '@' . $domain if $to !~ /\@/;
        $text =~ s/^\.$/../mg;
        local $SIG{'PIPE'} = 'IGNORE';
        $to .= '@' . $domain if $to !~ /\@/;
        $text =~ s/^\.$/../mg;
        local $SIG{'PIPE'} = 'IGNORE';
-       open( PIPE,  "| $conf::mailprog -oem $to" )
+       open( my $pipe,  '|-', "$conf::mailprog -oem $to" )
                or die "Can't open pipe to $conf::mailprog: $!\n";
        chomp $text;
                or die "Can't open pipe to $conf::mailprog: $!\n";
        chomp $text;
-       print PIPE "From: $from\n";
-       print PIPE "Subject: $subject\n\n";
-       print PIPE "$text\n";
-       close( PIPE );
+       print $pipe "From: $from\n";
+       print $pipe "Subject: $subject\n\n";
+       print $pipe "$text\n";
+       close( $pipe );
 }
 
 # for parsing input to dep-wait
 }
 
 # for parsing input to dep-wait
@@ -2184,6 +1525,7 @@ sub parse_deplist {
     my $verify = shift;
     my %result;
     
     my $verify = shift;
     my %result;
     
+    return $verify ? 0 : \%result unless $deps;
     foreach (split( /\s*,\s*/, $deps )) {
         if ($verify) {
             # verification requires > starting prompts, no | crap
     foreach (split( /\s*,\s*/, $deps )) {
         if ($verify) {
             # verification requires > starting prompts, no | crap
@@ -2227,79 +1569,200 @@ sub build_deplist {
        return $result;
 }
 
        return $result;
 }
 
+
+sub filterarch {
+    return "" unless $_[0];
+    return Dpkg::Deps::deps_parse($_[0], ("reduce_arch" => 1, "host_arch" => $_[1]))->output();
+}
+
+sub wb_edos_builddebcheck {
+# Copyright (C) 2008 Ralf Treinen <treinen@debian.org>
+# 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, version 2 of the License.
+# integrated into wanna-builds code by Andreas Barth 2010
+
+    my $args = shift;
+    my $sourceprefix="source---";
+    my $architecture=$args->{'arch'};
+    my $edosoptions = "-failures -explain -quiet";
+    my $packagefiles = $args->{'pkgs'};
+    my $sourcesfile = $args->{'src'};
+
+    my $packagearch="";
+    foreach my $packagefile (@$packagefiles) {
+        open(my $fh,'<', $packagefile);
+        while (<$fh>) {
+            next unless /^Architecture/;
+            next if /^Architecture:\s*all/;
+            /Architecture:\s*([^\s]*)/;
+            if ($packagearch eq "") {
+               $packagearch = $1;
+            } elsif ( $packagearch ne $1) {
+               return "Package file contains different architectures: $packagearch, $1";
+            }
+        }
+        close $fh;
+    }
+
+    if ( $architecture eq "" ) {
+        if ( $packagearch eq "" ) {
+       return "No architecture option given, " .
+           "and no non-all architecture found in the Packages file";
+        } else {
+           $architecture = $packagearch;
+        }
+    } else {
+        if ( $packagearch ne "" & $architecture ne $packagearch) {
+           return "Architecture option is $architecture ".
+           "but the package file contains architecture $packagearch";
+        }   
+    }
+
+    print "calling: edos-debcheck $edosoptions < $sourcesfile ".join('', map {" -I ".$_ } @$packagefiles)."\n";
+    open(my $result_cmd, '-|',
+        "edos-debcheck $edosoptions < $sourcesfile ".join('', map {" -I ".$_ } @$packagefiles));
+
+    my $explanation="";
+    my $result={};
+    my $binpkg="";
+
+    while (<$result_cmd>) {
+# source---pulseaudio (= 0.9.15-4.1~bpo50+1): FAILED
+#   source---pulseaudio (= 0.9.15-4.1~bpo50+1) depends on missing:
+#   - libltdl-dev (>= 2.2.6a-2)
+# source---libcanberra (= 0.22-1~bpo50+1): FAILED
+#   source---libcanberra (= 0.22-1~bpo50+1) depends on missing:
+#   - libltdl-dev
+#   - libltdl7-dev (>= 2.2.6)
+
+        if (/^\s+/) {
+           s/^(\s*)$sourceprefix(.*)depends on/$1$2build-depends on/o;
+            s/^(\s*)$sourceprefix(.*) and (.*) conflict/$1$2 build-conflicts with $3/o;
+            $explanation .= $_;
+        } else {
+            if (/^$sourceprefix(.*) \(.*\): FAILED/o) {
+                $result->{$binpkg} = $explanation if $binpkg;
+                $explanation = "";
+                $binpkg = $1;
+            } elsif (/^(depwait---.*) \(.*\): FAILED/o) {
+                $result->{$binpkg} = $explanation if $binpkg;
+                $explanation = "";
+                $binpkg = $1;
+            } else { # else something broken is happening
+                #print "ignoring $_\n";
+                1;
+            }
+        }
+    }
+
+    close $result_cmd;
+    $result->{$binpkg} = $explanation if $binpkg;
+    return $result;
+
+}
+
+
 sub call_edos_depcheck {
 sub call_edos_depcheck {
-    my $packagesfile = shift;
-    my $srcs = shift;
+    return if $simulate_edos;
+    my $args = shift;
+    my $srcs = $args->{'srcs'};
     my $key;
     my $key;
-    
-    return if defined ($conf::distributions{$distribution}{noadw});
+   
+    # Do not dispatch edos-debcheck if BD-Uninstallable is deactivated for the target.
+    # ("noadw")  Depwait will always be 1 in normal use.
+    return if defined ($distributions{$distribution}{noadw}) && not defined $args->{'depwait'};
 
     # We need to check all of needs-build, as any new upload could make
     # something in needs-build have uninstallable deps
     # We also check everything in bd-uninstallable, as any new upload could
     # make that work again
 
     # We need to check all of needs-build, as any new upload could make
     # something in needs-build have uninstallable deps
     # We also check everything in bd-uninstallable, as any new upload could
     # make that work again
-    my %interesting_packages;
-    my $db = get_all_source_info();
+    my (%interesting_packages, %interesting_packages_depwait);
+    my $db = get_all_source_info(); # TODO: Filter for needs-build bd-uninst dep-wait, that's all we need.
     foreach $key (keys %$db) {
        my $pkg = $db->{$key};
     foreach $key (keys %$db) {
        my $pkg = $db->{$key};
-        if (defined $pkg and isin($pkg->{'state'}, qw/Needs-Build BD-Uninstallable/)) {
-               $interesting_packages{$key} = undef;
+        if (defined $pkg and isin($pkg->{'state'}, qw/Needs-Build BD-Uninstallable/) and not defined ($distributions{$distribution}{noadw})) {
+               $interesting_packages{$key} = undef; # add key to interesting packages
+       }
+        if (defined $pkg and isin($pkg->{'state'}, qw/Dep-Wait/) and defined $args->{'depwait'}) {
+                # Depwaits are checked by creating pseudo binaries for edos-debcheck, so collect them.
+               $interesting_packages_depwait{$key} = undef;
+                # we always check for BD-Uninstallability in depwait - could be that depwait is satisfied but package is uninstallable
+               $interesting_packages{$key} = undef unless defined ($distributions{$distribution}{noadw});
        }
     }
     
     #print "I would look at these sources with edos-depcheck:\n";
     #print join " ", keys %interesting_packages,"\n";
        }
     }
     
     #print "I would look at these sources with edos-depcheck:\n";
     #print join " ", keys %interesting_packages,"\n";
+    return unless %interesting_packages || %interesting_packages_depwait;
 
 
-    my $tmpfile_pattern = "/tmp/wanna-build-interesting-sources-$distribution.$$-";
-    my ($tmpfile, $i);
-    for( $i = 0;; ++$i ) {
-           $tmpfile = $tmpfile_pattern . $i;
-           last if ! -e $tmpfile;
-    }
-
-    open SOURCES, '>', $tmpfile or die "Could not open temporary file $tmpfile\n";
+    my $tmpfile_pattern = "/tmp/wanna-build-interesting-sources-$distribution.$$-XXXXX";
+    use File::Temp qw/ tempfile /;
+    my ($SOURCES, $tmpfile) = tempfile( $tmpfile_pattern, UNLINK => 1 );
     for my $key (keys %interesting_packages) {
        my $pkg = $db->{$key};
     for my $key (keys %interesting_packages) {
        my $pkg = $db->{$key};
-       print SOURCES "Package: $key\n";
-       print SOURCES "Version: $pkg->{'version'}\n";
-       print SOURCES "Build-Depends: $srcs->{$key}{'dep'}\n" if $srcs->{$key}{'dep'};
-       print SOURCES "Build-Conflicts: $srcs->{$key}{'conf'}\n" if $srcs->{$key}{'conf'};
-       print SOURCES "Architecture: all\n";
-       print SOURCES "\n";
+        # we print the source files as binary ones (with "source---"-prefixed),
+        # so we can try if these "binary" packages are installable.
+        # 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'}, $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'}, $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";
+       print $SOURCES "\n";
     }
     }
-    close SOURCES;
-
-    if (open(EDOS,"-|","wb-edos-builddebcheck", "-a", $arch, $packagesfile, $tmpfile))
-    {
-       local($/) = ""; # read in paragraph mode
-       while( <EDOS> ) {
-               my( $key, $reason ) ;
-               s/\s*$//m;
-               /^Package:\s*(\S+)$/mi and $key = $1;
-               /^Failed-Why:(([^\n]|\n ([^\n]|\.))*)$/msi and $reason = $1;
-               $reason =~ s/^\s*//mg;
-               $reason ||= 'No reason given by edos-debcheck';
+    for my $key (keys %interesting_packages_depwait) {
+       my $pkg = $db->{$key};
+        # we print the source files as binary ones (with "depwait---"-prefixed),
+        # so we can try if these "binary" packages are installable.
+        # If such a "binary" package is installable, the corresponding source package goes out of depwait
+       print $SOURCES "Package: depwait---$key\n";
+       print $SOURCES "Version: $pkg->{'version'}\n";
+       print $SOURCES "Depends: $pkg->{'depends'}\n";
+       print $SOURCES "Architecture: all\n";
+       print $SOURCES "\n";
+    }
+    close $SOURCES;
 
 
+    my $edosresults = wb_edos_builddebcheck({'arch' => $args->{'arch'}, 'pkgs' => $args->{'pkgs'}, 'src' => $tmpfile});
+    if (ref($edosresults) eq 'HASH') {
+        foreach my $key (grep { $_ !~ /^depwait---/ } keys %$edosresults) {
                if (exists $interesting_packages{$key}) {
                if (exists $interesting_packages{$key}) {
-                   $interesting_packages{$key} = $reason;
+                   $interesting_packages{$key} = $edosresults->{$key};
                } else {
                    #print "TODO: edos reported a package we do not care about now\n" if $verbose;
                }
                } else {
                    #print "TODO: edos reported a package we do not care about now\n" if $verbose;
                }
-       }
-       close EDOS;
+        }
+        foreach my $key (grep { $_ =~ /^depwait---/ } keys %$edosresults) {
+                $key =~ /^depwait---(.*)/ and $key = $1;
+               if (exists $interesting_packages_depwait{$key}) {
+                   $interesting_packages_depwait{$key} = $edosresults->{"depwait---".$key};
+               } else {
+                   #print "TODO: edos reported a package we do not care about now\n" if $verbose;
+               }
+        }
     } else {
     } else {
-       print "ERROR: Could not run wb-edos-builddebcheck. I am continuing, assuming\n" .
-             "all packages have installable build-dependencies."
+        # if $edosresults isn't an hash, then something went wrong and the string is the error message
+        print "ERROR: Could not run wb-edos-builddebcheck. I am continuing, assuming\n" .
+             "all packages have installable build-dependencies."
     }
     
     unlink( $tmpfile );
 
     for my $key (keys %interesting_packages) {
     }
     
     unlink( $tmpfile );
 
     for my $key (keys %interesting_packages) {
+        next if defined $interesting_packages_depwait{$key};
        my $pkg = $db->{$key};
        my $pkg = $db->{$key};
+        # (defined $interesting_packages{$key}) => edos found an uninstallability
        my $change = 
            (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') ||
            (not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable');
        my $change = 
            (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') ||
            (not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable');
-       my $problemchange = $interesting_packages{$key} ne $pkg->{'bd_problem'};
+       my $problemchange = ($interesting_packages{$key}//"") ne ($pkg->{'bd_problem'}//"");
        if ($change) {
            if (defined $interesting_packages{$key}) {
                    change_state( \$pkg, 'BD-Uninstallable' );
        if ($change) {
            if (defined $interesting_packages{$key}) {
                    change_state( \$pkg, 'BD-Uninstallable' );
@@ -2314,13 +1777,31 @@ sub call_edos_depcheck {
            }   
        }
        if ($change) {
            }   
        }
        if ($change) {
-           log_ta( $pkg, "--merge-all" );
-           print "edos-builddebchange changed state of ${key}_$pkg->{'version'} to $pkg->{'state'}\n" if $verbose;
+           log_ta( $pkg, "--merge-all (edos)" ) unless $simulate;
+           print "edos-builddebchange changed state of ${key}_$pkg->{'version'} ($args->{'arch'}) to $pkg->{'state'}\n" if $verbose || $simulate;
        }
        if ($change || $problemchange) {
        }
        if ($change || $problemchange) {
-           update_source_info($pkg);
+           update_source_info($pkg) unless $simulate;
        }
     }
        }
     }
+
+    for my $key (keys %interesting_packages_depwait) {
+        if ($interesting_packages_depwait{$key}) {
+            print "dep-wait for $key ($args->{'arch'}) not fullfiled yet\n" if $verbose || $simulate;
+            next;
+        }
+       my $pkg = $db->{$key};
+        # The depwait could be cleared with the result still being uninstallable.
+        if (defined $interesting_packages{$key}) {
+           change_state( \$pkg, 'BD-Uninstallable' );
+           $pkg->{'bd_problem'} = $interesting_packages{$key};
+        } else {
+           change_state( \$pkg, 'Needs-Build' );
+        }
+       log_ta( $pkg, "edos_depcheck: depwait" ) unless $simulate;
+       update_source_info($pkg) unless $simulate;
+       print "edos-builddebchange changed state of ${key}_$pkg->{'version'} ($args->{'arch'}) from dep-wait to $pkg->{'state'}\n" if $verbose || $simulate;
+    }
 }
 
 sub usage {
 }
 
 sub usage {
@@ -2330,7 +1811,10 @@ sub usage {
 Usage: $prgname <options...> <package_version...>
 Options:
     -v, --verbose: Verbose execution.
 Usage: $prgname <options...> <package_version...>
 Options:
     -v, --verbose: Verbose execution.
-    -A arch: Architecture this operation is for.
+    --simulate: Do not actually execute the action.
+        (Not yet implemented for all operations.  Check the source.)
+    -A arch: Architecture this operation is for.  (REQUIRED)
+    -d dist: Distribution/suite this operation is for. Defaults to unstable.
     --take: Take package for building [default operation]
     -f, --failed: Record in database that a build failed due to
         deficiencies in the package (that aren't fixable without a new
     --take: Take package for building [default operation]
     -f, --failed: Record in database that a build failed due to
         deficiencies in the package (that aren't fixable without a new
@@ -2350,14 +1834,20 @@ Options:
         BD-Uninstallable, until the installability of its Build-Dependencies
         were verified. This happens at each call of --merge-all, usually
         every 15 minutes.
         BD-Uninstallable, until the installability of its Build-Dependencies
         were verified. This happens at each call of --merge-all, usually
         every 15 minutes.
-    --merge-quinn: Merge quinn-diff output into database.
-    --merge-packages: Merge Packages files into database.
-    --pretend-avail: Pretend that given packages are available now and give
-        free packages waiting for them
+    --build-priority=VALUE: Adjust the build priority of the currently
+        queued build.
+    --permanent-build-priority=VALUE: Adjust the permanent build
+        priority of a source package in a given distribution.
+    --extra-depends=BUILD-DEPENDS: Specify additional build-dependencies
+        used for the build.
+    --extra-conflicts=BUILD-DEPENDS: Specify additional build-conflicts
+        used for the build.
     -i SRC_PKG, --info SRC_PKG: Show information for source package
     -l STATE, --list=STATE: List all packages in state STATE; can be
         combined with -U to restrict to a specific user; STATE can
         also be 'all'
     -i SRC_PKG, --info SRC_PKG: Show information for source package
     -l STATE, --list=STATE: List all packages in state STATE; can be
         combined with -U to restrict to a specific user; STATE can
         also be 'all'
+    --min-age=VALUE, --max-age=VALUE: Filter the output of --list
+        by the age of the builds.
     -m MESSAGE, --message=MESSAGE: Give reason why package failed or
         source dependency list
         (used with -f, --dep-wait, and --binNMU)
     -m MESSAGE, --message=MESSAGE: Give reason why package failed or
         source dependency list
         (used with -f, --dep-wait, and --binNMU)
@@ -2369,13 +1859,16 @@ Options:
         automatically choosen
     --import FILE: Import database from a ASCII file FILE
     --export FILE: Export database to a ASCII file FILE
         automatically choosen
     --import FILE: Import database from a ASCII file FILE
     --export FILE: Export database to a ASCII file FILE
+    --format string, --own-format name: specify how the listing of packages
+        should look like. Please check the source for details. Own-Format
+        definitions are in ~/.wanna-build.yaml within the format section.
+
+There are more options not relevant for normal usage - please check source
+if you need them.
 
 The remaining arguments (depending on operation) usually start with
 "name_version", the trailer is ignored. This allows to pass the names
 of .dsc files, for which file name completion can be used.
 
 The remaining arguments (depending on operation) usually start with
 "name_version", the trailer is ignored. This allows to pass the names
 of .dsc files, for which file name completion can be used.
---merge-packages and --merge-quinn take Package/quin--diff file names
-on the command line or read stdin. --list needs nothing more on the
-command line. --info takes source package names (without version).
 EOF
        exit 1;
 }
 EOF
        exit 1;
 }
@@ -2403,20 +1896,30 @@ sub transactions_table_name {
        return '"' . $arch . $schema_suffix . '".transactions';
 }
 
        return '"' . $arch . $schema_suffix . '".transactions';
 }
 
+sub pkg_history_table_name {
+       return '"' . $arch . $schema_suffix . '".pkg_history';
+}
+
 sub get_readonly_source_info {
        my $name = shift;
        # SELECT FLOOR(EXTRACT('epoch' FROM age(localtimestamp, '2010-01-22  23:45')) / 86400) -- change to that?
 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 $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change::timestamp)) as state_days FROM ' . 
-               table_name() . ' WHERE package = ? AND distribution = ?',
+        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"
+            . " FROM " .  table_name()
+            . ' WHERE package = ? AND distribution = ?';
+       my $pkg = $dbh->selectrow_hashref( $q,
                undef, $name, $distribution);
        return $pkg;
 }
 
 sub get_source_info {
        my $name = shift;
                undef, $name, $distribution);
        return $pkg;
 }
 
 sub get_source_info {
        my $name = shift;
-       my $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change::timestamp)) as state_days FROM ' . 
-               table_name() . ' WHERE package = ? AND distribution = ?' .
-               ' FOR UPDATE',
+        return get_readonly_source_info($name) if $simulate;
+       lock_table();
+       my $pkg = $dbh->selectrow_hashref('SELECT *, 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 FROM ' . 
+               table_name() . ' WHERE package = ? AND distribution = ?',
                undef, $name, $distribution);
        return $pkg;
 }
                undef, $name, $distribution);
        return $pkg;
 }
@@ -2424,46 +1927,93 @@ sub get_source_info {
 sub get_all_source_info {
        my %options = @_;
 
 sub get_all_source_info {
        my %options = @_;
 
-       my $q = 'SELECT *, '.
-               'extract(days from date_trunc(\'days\', now() - state_change::timestamp)) as state_days, '.
-               'date_trunc(\'seconds\', now() - state_change::timestamp) as state_time'.
-               ' FROM ' . table_name()
-               . ' WHERE distribution = ? ';
-       my @args = ($distribution);
+        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"
+            . " FROM " .  table_name()
+                . " left join ( "
+                  . "select distinct on (package, distribution) build_time, package, distribution from ".pkg_history_table_name()." where result = 'successful' order by package, distribution, timestamp "
+                  . " ) as successtime using (package, distribution) "
+                . " left join ( "
+                  . "select distinct on (package, distribution) build_time, package, distribution from ".pkg_history_table_name()." order by package, distribution, timestamp desc"
+                  . " ) as anytime using (package, distribution) "
+           . " WHERE TRUE ";
+        my @args = ();
+        if ($distribution) {
+            my @dists = split(/[, ]+/, $distribution);
+            $q .= ' AND ( distribution = ? '.(' OR distribution = ? ' x $#dists).' )';
+            foreach my $d ( @dists ) {
+                push @args, ($d);
+            }
+        }
        if ($options{state} && uc($options{state}) ne "ALL") {
                $q .= ' AND upper(state) = ? ';
                push @args, uc($options{state});
        }
 
        if ($options{state} && uc($options{state}) ne "ALL") {
                $q .= ' AND upper(state) = ? ';
                push @args, uc($options{state});
        }
 
-       if ($options{user}) {
+       if ($options{user} && uc($options{state}) ne "NEEDS-BUILD") { # if it's NEEDS-BUILD, we don't look at users
                #this basically means "this user, or no user at all":
                #this basically means "this user, or no user at all":
-               $q .= ' AND (builder = ? OR upper(state) = ?)';
+               $q .= " AND (builder = ? OR upper(state) = 'NEEDS-BUILD')";
                push @args, $options{user};
                push @args, $options{user};
-               push @args, "NEEDS-BUILD";
-       }
-
-       if ($options{category}) {
-               $q .= ' AND failed_category <> ? AND upper(state) = ? ';
-               push @args, $options{category};
-               push @args, "FAILED";
        }
 
        }
 
-       if ($options{list_min_age} > 0) {
-               $q .= ' AND age(state_change::timestamp) > ? ';
+       if ($options{list_min_age} && $options{list_min_age} > 0) {
+               $q .= ' AND age(state_change) > ? ';
                push @args, $options{list_min_age} . " days";
        }
 
                push @args, $options{list_min_age} . " days";
        }
 
-       if ($options{list_min_age} < 0) {
-               $q .= ' AND age(state_change::timestamp) < ? ';
+       if ($options{list_min_age} && $options{list_min_age} < 0) {
+               $q .= ' AND age(state_change) < ? ';
                push @args, -$options{list_min_age} . " days";
        }
 
                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;
 }
 
        return $db;
 }
 
+sub show_distribution_architectures {
+        my $args = shift;
+       my $q = 'SELECT distribution, spacecat_all(architecture) AS architectures '.
+               'FROM distribution_architectures '.
+               'GROUP BY distribution';
+       my $rows = $dbh->selectall_hashref($q, 'distribution');
+        if ($args->{suite}) {
+            print $rows->{$args->{'suite'}}->{'architectures'}."\n";
+        } else {
+           foreach my $name (keys %$rows) {
+               print $name.': '.$rows->{$name}->{'architectures'}."\n";
+           }
+        }
+}
+
+sub show_distribution_aliases {
+       foreach my $alias (keys %distribution_aliases) {
+               print $alias.': '.$distribution_aliases{$alias}."\n";
+       }
+}
+
 sub update_source_info {
        my $pkg = shift;
 sub update_source_info {
        my $pkg = shift;
+        $pkg->{'extra_depends'} = $extra_depends if defined $extra_depends;
+        undef $pkg->{'extra_depends'} unless $pkg->{'extra_depends'};
+        $pkg->{'extra_conflicts'} = $extra_conflicts if defined $extra_conflicts;
+        undef $pkg->{'extra_conflicts'} unless $pkg->{'extra_conflicts'};
+        print Dumper $pkg if $verbose and $simulate;
+        return if $simulate;
 
        my $pkg2 = get_source_info($pkg->{'package'});
        if (! defined $pkg2)
 
        my $pkg2 = get_source_info($pkg->{'package'});
        if (! defined $pkg2)
@@ -2485,11 +2035,12 @@ sub update_source_info {
                        'old_failed = ?, ' .
                        'binary_nmu_version = ?, ' .
                        'binary_nmu_changelog = ?, ' .
                        'old_failed = ?, ' .
                        'binary_nmu_version = ?, ' .
                        'binary_nmu_changelog = ?, ' .
-                       'failed_category = ?, ' .
                        'permbuildpri = ?, ' .
                        'buildpri = ?, ' .
                        'depends = ?, ' .
                        'rel = ?, ' .
                        'permbuildpri = ?, ' .
                        'buildpri = ?, ' .
                        'depends = ?, ' .
                        'rel = ?, ' .
+                       'extra_depends = ?, ' .
+                       'extra_conflicts = ?, ' .
                        'bd_problem = ? ' .
                        'WHERE package = ? AND distribution = ?',
                undef,
                        'bd_problem = ? ' .
                        'WHERE package = ? AND distribution = ?',
                undef,
@@ -2505,17 +2056,19 @@ sub update_source_info {
                $pkg->{'old_failed'},
                $pkg->{'binary_nmu_version'},
                $pkg->{'binary_nmu_changelog'},
                $pkg->{'old_failed'},
                $pkg->{'binary_nmu_version'},
                $pkg->{'binary_nmu_changelog'},
-               $pkg->{'failed_category'},
                $pkg->{'permbuildpri'},
                $pkg->{'buildpri'},
                $pkg->{'depends'},
                $pkg->{'rel'},
                $pkg->{'permbuildpri'},
                $pkg->{'buildpri'},
                $pkg->{'depends'},
                $pkg->{'rel'},
+                $pkg->{'extra_depends'},
+                $pkg->{'extra_conflicts'},
                $pkg->{'bd_problem'},
                $pkg->{'package'},
                $distribution) or die $dbh->errstr;
 }
 
 sub add_source_info {
                $pkg->{'bd_problem'},
                $pkg->{'package'},
                $distribution) or die $dbh->errstr;
 }
 
 sub add_source_info {
+        return if $simulate;
        my $pkg = shift;
        $dbh->do('INSERT INTO ' . table_name() .
                        ' (package, distribution) values (?, ?)',
        my $pkg = shift;
        $dbh->do('INSERT INTO ' . table_name() .
                        ' (package, distribution) values (?, ?)',
@@ -2523,6 +2076,7 @@ sub add_source_info {
 }
 
 sub del_source_info {
 }
 
 sub del_source_info {
+        return if $simulate;
        my $name = shift;
        $dbh->do('DELETE FROM ' . table_name() .
                        ' WHERE package = ? AND distribution = ?',
        my $name = shift;
        $dbh->do('DELETE FROM ' . table_name() .
                        ' WHERE package = ? AND distribution = ?',
@@ -2538,6 +2092,7 @@ sub get_user_info {
 }
 
 sub update_user_info {
 }
 
 sub update_user_info {
+        return if $simulate;
        my $user = shift;
        $dbh->do('UPDATE ' . user_table_name() .
                        ' SET last_seen = now() WHERE username = ?' .
        my $user = shift;
        $dbh->do('UPDATE ' . user_table_name() .
                        ' SET last_seen = now() WHERE username = ?' .
@@ -2548,6 +2103,7 @@ sub update_user_info {
 
 
 sub add_user_info {
 
 
 sub add_user_info {
+        return if $simulate;
        my $user = shift;
        $dbh->do('INSERT INTO ' . user_table_name() .
                        ' (username, distribution, last_seen)' .
        my $user = shift;
        $dbh->do('INSERT INTO ' . user_table_name() .
                        ' (username, distribution, last_seen)' .
@@ -2556,9 +2112,152 @@ sub add_user_info {
                or die $dbh->errstr;
 }
 
                or die $dbh->errstr;
 }
 
-sub lock_table()
-{
-       $dbh->do('LOCK TABLE ' . table_name() .
-               ' IN EXCLUSIVE MODE', undef) or die $dbh->errstr;
+sub lock_table {
+        return if $simulate;
+       $q = 'SELECT 1 AS result FROM public.locks' .
+            ' WHERE architecture = ? AND distribution = ? FOR UPDATE';
+       my $result = $dbh->selectrow_hashref($q, undef, $arch, $distribution) or die $dbh->errstr;
+       die unless $result->{'result'} == 1;
+}
+
+sub parse_argv {
+# parts the array $_[0] and $_[1] and returns the sub-array (modifies the original one)
+    my @ret = ();
+    my $args = shift;
+    my $separator = shift;
+    while($args->[0] && $args->[0] ne $separator) { 
+        push @ret, shift @$args;
+    }
+    shift @$args if @$args;
+    return @ret;
 }
 
 }
 
+sub parse_all_v3 {
+    my $srcs = shift;
+    my $vars = shift;
+    my $db = get_all_source_info();
+    my $binary = $srcs->{'_binary'};
+
+    SRCS:
+    foreach my $name (keys %$srcs) {
+        next if $name eq '_binary';
+
+        # 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 '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
+            for my $bin (@{$pkgs->{'binary'}}) {
+                if ($binary->{$bin} and $binary->{$bin}->{'arch'} ne 'all' and vercmp($pkgs->{'version'}, $binary->{$bin}->{'version'}) < 0) {
+                   print Dumper($binary->{$bin}) . "\n";
+                    print "$logstr skipped because binaries (assumed to be) overwritten (" .
+                       $bin . ", " . $pkgs->{'version'} . " vs. " . $binary->{$bin}->{'version'} . ")\n" if $verbose || $simulate;
+                    next SRCS;
+                }
+            }
+            $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->{'notes'} ? "/".$pkg->{'notes'} : "")."):";
+
+        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 arch-not-in-arch-list packages-arch-specific overwritten-by-arch-all arch-all-only>)) {
+            my $change = 0;
+            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) {
+                change_state( \$pkg, $tstate);
+                if (isin( $tstate, qw<Installed>)) {
+                    delete $pkg->{'depends'};
+                    delete $pkg->{'extra_depends'};
+                    delete $pkg->{'extra_conflicts'};
+                }
+                $change++;
+            }
+            my $attrs = { 'version' => 'version', 'installed_version' => 'version', 'binary_nmu_version' => 'binnmu', 'section' => 'section', 'priority' => 'priority' };
+            foreach my $k (keys %$attrs) {
+                next if isin( $tstate, qw<Auto-Not-For-Us>) && isin( $k, qw<installed_version binary_nmu_version>);
+                if (($pkg->{$k}//"") ne ($pkgs->{$attrs->{$k}}//"")) {
+                    $pkg->{$k} = $pkgs->{$attrs->{$k}};
+                    $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;
+                log_ta( $pkg, "--merge-v3: $tstate" ) unless $simulate;
+                update_source_info($pkg) unless $simulate;
+            }
+            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";
+            next SRCS;
+        }
+        next if $pkgs->{'version'} eq $pkg->{'version'} and $pkgs->{'binnmu'}//0 >= int($pkg->{'binary_nmu_version'}//0);
+        next if $pkgs->{'version'} eq $pkg->{'version'} and !isin( $pkg->{'state'}, qw(Installed));
+        next if isin( $pkg->{'state'}, qw(Not-For-Us Failed-Removed));
+
+        if (defined( $pkg->{'state'} ) && isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
+            send_mail( $pkg->{'builder'},
+                "new version of $name (dist=$distribution)",
+                "As far as I'm informed, you're currently building the package $name\n".
+                "in version $pkg->{'version'}.\n\n".
+                "Now there's a new source version $pkgs->{'version'}. If you haven't finished\n".
+                "compiling $name yet, you can stop it to save some work.\n".
+                "Just to inform you...\n".
+                "(This is an automated message)\n" ) unless $simulate;
+           print "$logstr new version while building $pkg->{'version'} -- sending mail to builder ($pkg->{'builder'})\n"
+                                 if $verbose || $simulate;
+           }
+        change_state( \$pkg, 'Needs-Build');
+        $pkg->{'notes'} = $pkgs->{'status'};
+        $pkg->{'version'} = $pkgs->{'version'};
+        $pkg->{'section'} = $pkgs->{'section'};
+        $pkg->{'priority'} = $pkgs->{'priority'};
+        $pkg->{'dep'} = $pkgs->{'depends'};
+        $pkg->{'conf'} = $pkgs->{'conflicts'};
+        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;
+    }
+
+    foreach my $name (keys %$db) {
+        next if $srcs->{$name};
+        my $pkg = $db->{$name};
+        my $logstr = "merge-v3 $vars->{'time'} ".$name."_$pkg->{'version'} ($vars->{'arch'}, $vars->{'suite'}, previous: $pkg->{'state'}):";
+        # package disappeared - delete
+        change_state( \$pkg, 'deleted' );
+        log_ta( $pkg, "--merge-v3: deleted" ) unless $simulate;
+       print "$logstr deleted from database\n" if $verbose || $simulate;
+        del_source_info($name) unless $simulate;
+        delete $db->{$name};
+    }
+}