]> git.donarmstrong.com Git - wannabuild.git/blob - bin/wanna-build
use ~/.wanna-build.yaml if it exists
[wannabuild.git] / bin / wanna-build
1 #!/usr/bin/perl
2
3 # wanna-build: coordination script for Debian buildds
4 # Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
5 # Copyright (C) 2005-2008 Ryan Murray <rmurray@debian.org>
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation; either version 2 of the
10 # License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 #
21
22 package conf;
23 # defaults
24 $basedir ||= "/var/lib/debbuild";
25 $dbbase ||= "build-db";
26 $transactlog ||= "transactions.log";
27 $mailprog ||= "/usr/sbin/sendmail";
28 require "/org/wanna-build/etc/wanna-build.conf";
29 die "$conf::basedir is not a directory\n" if ! -d $conf::basedir;
30 die "dbbase is empty\n" if ! $dbbase;
31 die "transactlog is empty\n" if ! $transactlog;
32 die "mailprog binary $conf::mailprog does not exist or isn't executable\n"
33         if !-x $conf::mailprog;
34 die "no distributions defined\n" if ! %distributions;
35 package main;
36
37 use strict;
38 use POSIX;
39 use FileHandle;
40 use File::Copy;
41 use DBI;
42 use lib '/org/wanna-build/bin';
43 use WannaBuild;
44 use YAML::Tiny;
45 use Data::Dumper;
46 use Hash::Merge qw ( merge );
47 use String::Format;
48 use Date::Parse;
49
50 our ($verbose, $mail_logs, $list_order, $list_state,
51     $curr_date, $op_mode, $user, $real_user, $distribution,
52     $fail_reason, $opt_override, $import_from, $export_to, $opt_create_db,
53     %prioval, %sectval,
54     $info_all_dists, $arch,
55     $category, %catval, %short_category,
56     $short_date, $list_min_age, $dbbase, @curr_time,
57     $build_priority, %new_vers, $binNMUver, %merge_srcvers, %merge_binsrc,
58     $printformat, $ownprintformat
59     );
60
61 # global vars
62 $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin:/org/wanna-build/bin/";
63 $ENV{'LC_ALL'} = 'C';
64 $verbose = 0;
65 $mail_logs = "";
66 @curr_time = gmtime;
67 $curr_date = strftime("%Y %b %d %H:%M:%S",@curr_time);
68 $short_date = strftime("%m/%d/%y",@curr_time);
69 $| = 1;
70
71 # map program invocation names to operation modes
72 my %prognames = ( "uploaded-build"  => "set-uploaded",
73                                   "failed-build"    => "set-failed",
74                                   "no-build"            => "set-not-for-us",
75                                   "give-back-build" => "set-needs-build",
76                                   "dep-wait-build"  => "set-dep-wait",
77                                   "forget-build"        => "forget",
78                                   "merge-quinn"         => "merge-quinn",
79                                   "merge-packages"  => "merge-packages",
80                                   "merge-sources"   => "merge-sources",
81                                   "build-info"          => "info" );
82
83 %short_category = ( u => "uploaded-fixed-pkg",
84                                     f => "fix-expected",
85                                         r => "reminder-sent",
86                                         n => "nmu-offered",
87                                         e => "easy",
88                                         m => "medium",
89                                         h => "hard",
90                                         c => "compiler-error",
91                                         "" => "none" );
92
93 my $progname;
94 ($progname = $0) =~ s,.*/,,;
95 if ($prognames{$progname}) {
96         $op_mode = $prognames{$progname};
97 }
98 elsif ($progname =~ /^list-(.*)$/) {
99         $op_mode = "list";
100         $list_state = ($1 eq "all") ? "" : $1;
101 }
102
103 my %options =
104         (# flags
105          verbose        => { short => "v", flag => \$verbose },
106          override               => { short => "o", flag => \$opt_override },
107          "create-db"    => { flag => \$opt_create_db },
108          "correct-compare" => { flag => \$WannaBuild::opt_correct_version_cmp },
109          # TODO: remove after buildds no longer pass to wanna-build
110          "no-propagation" => { short => "N" },
111          "no-down-propagation" => { short => "D" },
112          # normal actions
113          take                   => { mode => "set-building" },
114          failed                 => { short => "f", mode => "set-failed" },
115          uploaded               => { short => "u", mode => "set-uploaded" },
116          "no-build"             => { short => "n", mode => "set-not-for-us" },
117          built                  => { mode => "set-built" },
118          attempted              => { mode => "set-attempted" },
119          "give-back"            => { mode => "set-needs-build" },
120          "dep-wait"             => { mode => "set-dep-wait" },
121          forget                 => { mode => "forget" },
122          'forget-user' => { mode => 'forget-user' },
123          "merge-quinn"  => { mode => "merge-quinn" },
124          "merge-partial-quinn" => { mode => "merge-partial-quinn" },
125          "merge-packages" => { mode => "merge-packages" },
126          "merge-sources" => { mode => "merge-sources" },
127          "pretend-avail" => { short => "p", mode => "pretend-avail" },
128          "merge-all"     => { mode => "merge-all" },
129          "merge-all-secondary" => { mode => "merge-all-secondary" },
130          info                   => { short => "i", mode => "info" },
131          'binNMU' => { mode => 'set-binary-nmu', arg => \$binNMUver, 
132                              code => sub { die "Invalid binNMU version: $binNMUver\n"
133                                 if $binNMUver !~ /^([\d]*)$/ and $1 >= 0; } },
134          'perm-build-priority'    => { mode => "set-permanent-build-priority", arg => \$build_priority,
135                               code => sub { die "Invalid build priority: $build_priority\n"
136                                 if $build_priority !~ /^-?[\d]+$/; } },
137          'build-priority'    => { mode => "set-build-priority", arg => \$build_priority,
138                               code => sub { die "Invalid build priority: $build_priority\n"
139                                 if $build_priority !~ /^-?[\d]+$/; } },
140          list                   =>
141          { short => "l", mode => "list", arg => \$list_state,
142            code => sub {
143                    die "Unknown state to list: $list_state\n"
144                            if !isin( $list_state, qw(needs-build building uploaded
145                                                  built build-attempted failed installed dep-wait
146                                                  not-for-us all failed-removed
147                                                  install-wait reupload-wait bd-uninstallable));} },
148          # options with args
149          dist           =>
150          { short => "d", arg => \$distribution,
151            code => sub {
152                    if ($distribution eq "a" || $distribution eq "all") {
153                            $info_all_dists = 1;
154                            $distribution = "";
155                    }
156                    else {
157                            $distribution = "oldstable"   if $distribution eq "o";
158                            $distribution = "stable"   if $distribution eq "s";
159                            $distribution = "testing"  if $distribution eq "t";
160                            $distribution = "unstable" if $distribution eq "u";
161                    }
162            } },
163          order          =>
164          { short => "O", arg => \$list_order,
165            code => sub {
166                    die "Bad ordering character\n"
167                            if $list_order !~ /^[PSpsncbCW]+$/;
168            } },
169          message        => { short => "m", arg => \$fail_reason },
170          # database is deprecated, use arch instead.
171          database       => { short => "b", arg => \$conf::dbbase },
172          arch           => { short => "A", arg => \$arch },
173          user           => { short => "U", arg => \$user },
174          category               => { short => "c", arg => \$category,
175                                                  code => sub {
176                                                          $category = $short_category{$category}
177                                                                  if exists $short_category{$category};
178                                                          die "Unknown category: $category\n"
179                                                                  if !isin( $category, values %short_category );
180                                                  } },
181          "min-age"      => { short => "a", arg => \$list_min_age,
182                                                  code => sub {
183                                                          die "Argument of --min-age must be a non-zero number\n"
184                                                                  if $list_min_age == 0;
185                                                  } },
186          "max-age"      => { arg => \$list_min_age,
187                                                  code => sub {
188                                                          die "Argument of --max-age must be a non-zero number\n"
189                                                                  if $list_min_age == 0;
190                                                          $list_min_age *= -1;
191                                                  } },
192          "format"       => { arg => \$printformat },
193          "own-format"       => { arg => \$ownprintformat },
194          # special actions
195          export         => { arg => \$export_to, mode => "export" },
196          import         => { arg => \$import_from, mode => "import" },
197          "manual-edit"  => { mode => "manual-edit" },
198          );
199
200 while( @ARGV && $ARGV[0] =~ /^-/ ) {
201         $_ = shift @ARGV;
202         last if $_ eq "--";
203         my($opt, $optname, $arg);
204         if (/^--([^=]+)(=|$)/) {
205                 $optname = $1;
206                 $opt = $options{$optname};
207                 $arg = $1 if /^--\Q$optname\E=((.|\n)*)$/;
208         }
209         else {
210                 $optname = substr( $_, 1, 1 );
211                 $opt = (grep { defined($_->{short}) ? $_->{short} eq $optname : 0} values %options)[0];
212                 $arg = $1 if /^-$optname(.+)$/;
213         }
214         if (!$opt) {
215                 warn "Unknown option: --$1\n";
216                 usage();
217         }
218         if ($opt->{arg}) {
219                 if (!defined $arg) {
220                         die "$optname option missing argument\n" if !@ARGV;
221                         $arg = shift @ARGV;
222                 }
223                 ${$opt->{arg}} = $arg;
224         }
225         elsif (defined $arg) {
226                 die "Option $optname takes no argument\n";
227         }
228         
229         if ($opt->{mode}) {
230                 die "Conflicting operation modes\n" if $op_mode;
231                 $op_mode = $opt->{mode};
232         }
233         if ($opt->{flag}) {
234                 ${$opt->{flag}}++;
235         }
236         if ($opt->{code}) {
237                 &{$opt->{code}};
238         }
239 }
240
241 $op_mode = $category ? "set-failed" : "set-building"
242         if !$op_mode; # default operation
243 $distribution ||= "unstable";
244 die "Bad distribution '$distribution'\n"
245         if !isin($distribution, keys %conf::distributions);
246
247 # If they didn't specify an arch, try to get it from database name which
248 # is in the form of $arch/build-db
249 # This is for backwards compatibity with older versions that didn't
250 # specify the arch yet.
251 $conf::dbbase =~ m#^([^/]+)#;
252 $arch ||= $1;
253
254 # TODO: Check that it's an known arch (for that dist), and give
255 # a proper error.
256
257 if ($verbose) {
258         my $version = '$Revision: db181a534e9d $ $Date: 2008/03/26 06:20:22 $ $Author: rmurray $';
259         $version =~ s/(^\$| \$ .*$)//g;
260         print "wanna-build $version for $distribution on $arch\n";
261 }
262
263 if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import export
264                                   merge-packages manual-edit
265                                   merge-sources))) {
266         warn "No packages given.\n";
267         usage();
268 }
269
270 $real_user = (getpwuid($<))[0];
271 die "Can't determine your user name\n"
272         if $op_mode ne "list" && !$user &&
273            !($user = $real_user);
274
275 if (!$fail_reason) {
276         if ($op_mode eq "set-failed" && !$category) {
277                 print "Enter reason for failing (end with '.' alone on ".
278                       "its line):\n";
279                 my $line;
280                 while(!eof(STDIN)) {
281                         $line = <STDIN>;
282                         last if $line eq ".\n";
283                         $fail_reason .= $line;
284                 }
285                 chomp( $fail_reason );
286         } elsif ($op_mode eq "set-dep-wait") {
287                 print "Enter dependencies (one line):\n";
288                 my $line;
289                 while( !$line && !eof(STDIN) ) {
290                         chomp( $line = <STDIN> );
291                 }
292                 die "No dependencies given\n" if !$line;
293                 $fail_reason = $line;
294         } elsif ($op_mode eq "set-binary-nmu" and $binNMUver > 0) {
295                 print "Enter changelog entry (one line):\n";
296                 my $line;
297                 while( !$line && !eof(STDIN) ) {
298                         chomp( $line = <STDIN> );
299                 }
300                 die "No changelog entry given\n" if !$line;
301                 $fail_reason = $line;
302         }
303 }
304
305 my $yamlmap = ();
306 my $yamldir = "/org/wanna-build/etc/yaml";
307 my @files = ('wanna-build.yaml');
308 if ((getpwuid($>))[7]) { push (@files, ((getpwuid($>))[7])."/.wanna-build.yaml"); }
309 if ($user =~ /(buildd.*)-/) { push (@files, "$1.yaml") };
310 if ($user) { push ( @files, "$user.yaml"); }
311 foreach my $file (@files) {
312         my $cfile = File::Spec->rel2abs( $file, $yamldir );
313         if ($verbose >= 2) { print "Trying to read $file ($cfile) ...\n"; }
314         next unless -f $cfile;
315         if ($verbose >= 2) { print "Read $file ($cfile) ...\n"; }
316         my $m = YAML::Tiny->read( $cfile )->[0];
317         $yamlmap = merge($m, $yamlmap);
318 }
319 if (not $yamlmap) {
320         die "FATAL: no configuration found\n";
321 }
322 $list_order = $yamlmap->{"list-order"}{$list_state} if !$list_order and $list_state;
323 $list_order ||= $yamlmap->{"list-order"}{'default'};
324
325 my $dbh;
326
327 END {
328         if (defined $dbh)
329         {
330                 $dbh->disconnect or warn $dbh->errstr;
331         }
332 }
333
334 my $schema_suffix = '';
335 if (isin( $op_mode, qw(list info)) && $distribution !~ /security/ && !(not -t and $user =~ /-/)) {
336         $dbh = DBI->connect("DBI:Pg:service=wanna-build") || 
337                 die "FATAL: Cannot open database: $DBI::errstr\n";
338         $schema_suffix = '_public';
339 }
340 else
341 {
342         $dbh = DBI->connect("DBI:Pg:service=wanna-build-privileged") || 
343                 die "FATAL: Cannot open database: $DBI::errstr\n";
344 }
345
346 # TODO: This shouldn't be needed, file a bug.
347 $dbh->{pg_server_prepare} = 0;
348
349 $dbh->begin_work or die $dbh->errstr;
350
351 process();
352
353 $dbh->commit;
354 $dbh->disconnect;
355
356 if ($mail_logs && $conf::log_mail) {
357         send_mail( $conf::log_mail,
358                            "wanna-build $distribution state changes $curr_date",
359                            "State changes at $curr_date for distribution ".
360                            "$distribution:\n\n$mail_logs\n" );
361 }
362
363 exit 0;
364
365
366 sub process {
367
368         SWITCH: foreach ($op_mode) {
369                 /^set-(.+)/ && do {
370                         add_packages( $1, @ARGV );
371                         last SWITCH;
372                 };
373                 /^list/ && do {
374                         list_packages( $list_state );
375                         last SWITCH;
376                 };
377                 /^info/ && do {
378                         info_packages( @ARGV );
379                         last SWITCH;
380                 };
381                 /^forget-user/ && do {
382                         die "This operation is restricted to admin users\n"
383                                 if (defined @conf::admin_users and
384                                     !isin( $real_user, @conf::admin_users));
385                         forget_users( @ARGV );
386                         last SWITCH;
387                 };
388                 /^forget/ && do {
389                         forget_packages( @ARGV );
390                         last SWITCH;
391                 };
392                 /^merge-partial-quinn/ && do {
393                         die "This operation is restricted to admin users\n"
394                                 if (defined @conf::admin_users and
395                                     !isin( $real_user, @conf::admin_users));
396                         lock_table();
397                         parse_quinn_diff(1);
398                         last SWITCH;
399                 };
400                 /^merge-quinn/ && do {
401                         die "This operation is restricted to admin users\n"
402                                 if (defined @conf::admin_users and
403                                     !isin( $real_user, @conf::admin_users));
404                         lock_table();
405                         parse_quinn_diff(0);
406                         last SWITCH;
407                 };
408                 /^merge-packages/ && do {
409                         die "This operation is restricted to admin users\n"
410                                 if (defined @conf::admin_users and
411                                     !isin( $real_user, @conf::admin_users));
412                         lock_table();
413                         parse_packages(0);
414                         last SWITCH;
415                 };
416                 /^merge-sources/ && do {
417                         die "This operation is restricted to admin users\n"
418                                 if (defined @conf::admin_users and
419                                     !isin( $real_user, @conf::admin_users));
420                         lock_table();
421                         parse_sources(0);
422                         last SWITCH;
423                 };
424                 /^pretend-avail/ && do {
425                         pretend_avail( @ARGV );
426                         last SWITCH;
427                 };
428                 /^merge-all$/ && do {
429                         die "This operation is restricted to admin users\n"
430                                 if (defined @conf::admin_users and
431                                     !isin( $real_user, @conf::admin_users));
432                         lock_table();
433                         my @ARGS = @ARGV;
434                         @ARGV = ( $ARGS[0] );
435                         my $pkgs = parse_packages(0);
436                         @ARGV = ( $ARGS[1] );
437                         parse_quinn_diff(0);
438                         @ARGV = ( $ARGS[2] );
439                         my $srcs = parse_sources(1);
440                         call_edos_depcheck( $ARGS[0], $srcs );
441                         last SWITCH;
442                 };
443                 /^merge-all-secondary/ && do {
444                         die "This operation is restricted to admin users\n"
445                                 if (defined @conf::admin_users and
446                                     !isin( $real_user, @conf::admin_users));
447                         # This is in case the chroot has multiple unrelated
448                         # dist, for instance unstable and experimental.
449                         # This is not for stable and proposed-updates.
450                         # The second packages file contains a combination
451                         # of all Packages files known to the buildd, the
452                         # first only for the current dist.
453                         lock_table();
454                         my @ARGS = @ARGV;
455                         @ARGV = ( $ARGS[0] );
456                         my $pkgs = parse_packages(0);
457                         @ARGV = ( $ARGS[3] );
458                         my $pkgs = parse_packages(1);
459                         @ARGV = ( $ARGS[1] );
460                         parse_quinn_diff(0);
461                         @ARGV = ( $ARGS[2] );
462                         my $srcs = parse_sources(1);
463                         call_edos_depcheck( $ARGS[3], $srcs );
464                         last SWITCH;
465                 };
466                 /^import/ && do {
467                         die "This operation is restricted to admin users\n"
468                                 if (defined @conf::admin_users and
469                                     !isin( $real_user, @conf::admin_users));
470                         $dbh->do("DELETE from " . table_name() . 
471                                 " WHERE distribution = ?", undef,
472                                 $distribution)
473                                 or die $dbh->errstr;
474                         forget_users();
475                         read_db( $import_from );
476                         last SWITCH;
477                 };
478                 /^export/ && do {
479                         export_db( $export_to );
480                         last SWITCH;
481                 };
482
483                 die "Unexpected operation mode $op_mode\n";
484         }
485         if (not -t and $user =~ /-/) {
486                 my $userinfo = get_user_info($user);
487                 if (!defined $userinfo)
488                 {
489                         add_user_info($user);
490                 }
491                 else
492                 {
493                         update_user_info($user);
494                 }
495         }
496 }
497
498 sub add_packages {
499         my $newstate = shift;
500         my( $package, $name, $version, $ok, $reason );
501         
502         foreach $package (@_) {
503                 $package =~ s,^.*/,,; # strip path
504                 $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
505                 $package =~ s/_[a-zA-Z\d-]+\.changes$//; # strip extension
506                 if ($package =~ /^([\w\d.+-]+)_([\w\d:.+~-]+)/) {
507                         ($name,$version) = ($1,$2);
508                 }
509                 else {
510                         warn "$package: can't extract package name and version ".
511                                  "(bad format)\n";
512                         next;
513                 }
514
515                 if ($op_mode eq "set-building") {
516                         add_one_building( $name, $version );
517                 }
518                 elsif ($op_mode eq "set-built") {
519                         add_one_built( $name, $version );
520                 }
521                 elsif ($op_mode eq "set-attempted") {
522                         add_one_attempted( $name, $version );
523                 }
524                 elsif ($op_mode eq "set-uploaded") {
525                         add_one_uploaded( $name, $version );
526                 }
527                 elsif ($op_mode eq "set-failed") {
528                         add_one_failed( $name, $version );
529                 }
530                 elsif ($op_mode eq "set-not-for-us") {
531                         add_one_notforus( $name, $version );
532                 }
533                 elsif ($op_mode eq "set-needs-build") {
534                         add_one_needsbuild( $name, $version );
535                 }
536                 elsif ($op_mode eq "set-dep-wait") {
537                         add_one_depwait( $name, $version );
538                 }
539                 elsif ($op_mode eq "set-build-priority") {
540                         set_one_buildpri( $name, $version, 'buildpri' );
541                 }
542                 elsif ($op_mode eq "set-permanent-build-priority") {
543                         set_one_buildpri( $name, $version, 'permbuildpri' );
544                 }
545                 elsif ($op_mode eq "set-binary-nmu") {
546                         set_one_binnmu( $name, $version );
547                 }
548         }
549 }
550
551 sub add_one_building {
552         my $name = shift;
553         my $version = shift;
554         my( $ok, $reason );
555
556         $ok = 1;
557         my $pkg = get_source_info($name);
558         if (defined($pkg)) {
559                 if ($pkg->{'state'} eq "Not-For-Us") {
560                         $ok = 0;
561                         $reason = "not suitable for this architecture";
562                 }
563                 elsif ($pkg->{'state'} =~ /^Dep-Wait/) {
564                         $ok = 0;
565                         $reason = "not all source dependencies available yet";
566                 }
567                 elsif ($pkg->{'state'} =~ /^BD-Uninstallable/) {
568                         $ok = 0;
569                         $reason = "source dependencies are not installable";
570                 }
571                 elsif ($pkg->{'state'} eq "Uploaded" &&
572                            (version_lesseq($version, $pkg->{'version'}))) {
573                         $ok = 0;
574                         $reason = "already uploaded by $pkg->{'builder'}";
575                         $reason .= " (in newer version $pkg->{'version'})"
576                                 if !version_eq($pkg, $version);
577                 }
578                 elsif ($pkg->{'state'} eq "Installed" &&
579                            version_less($version,$pkg->{'version'})) {
580                         if ($opt_override) {
581                                 print "$name: Warning: newer version $pkg->{'version'} ".
582                                           "already installed, but overridden.\n";
583                         }
584                         else {
585                                 $ok = 0;
586                                 $reason = "newer version $pkg->{'version'} already in ".
587                                                   "archive; doesn't need rebuilding";
588                                 print "$name: Note: If the following is due to an epoch ",
589                                           " change, use --override\n";
590                         }
591                 }
592                 elsif ($pkg->{'state'} eq "Installed" &&
593                            pkg_version_eq($pkg,$version)) {
594                         $ok = 0;
595                         $reason = "is up-to-date in the archive; doesn't need rebuilding";
596                 }
597                 elsif ($pkg->{'state'} eq "Needs-Build" &&
598                            version_less($version,$pkg->{'version'})) {
599                         if ($opt_override) {
600                                 print "$name: Warning: newer version $pkg->{'version'} ".
601                                           "needs building, but overridden.";
602                         }
603                         else {
604                                 $ok = 0;
605                                 $reason = "newer version $pkg->{'version'} needs building, ".
606                                                   "not $version";
607                         }
608                 }
609                 elsif (isin($pkg->{'state'},qw(Building Built Build-Attempted))) {
610                         if (version_less($pkg->{'version'},$version)) {
611                                 print "$name: Warning: Older version $pkg->{'version'} ",
612                                       "is being built by $pkg->{'builder'}\n";
613                                 if ($pkg->{'builder'} ne $user) {
614                                         send_mail( $pkg->{'builder'},
615                                                            "package takeover in newer version",
616                                                            "You are building package '$name' in ".
617                                                            "version $version\n".
618                                                            "(as far as I'm informed).\n".
619                                                            "$user now has taken the newer ".
620                                                            "version $version for building.".
621                                                            "You can abort the build if you like.\n" );
622                                 }
623                         }
624                         else {
625                                 if ($opt_override) {
626                                         print "User $pkg->{'builder'} had already ",
627                                               "taken the following package,\n",
628                                                   "but overriding this as you request:\n";
629                                         send_mail( $pkg->{'builder'}, "package takeover",
630                                                            "The package '$name' (version $version) that ".
631                                                            "was taken by you\n".
632                                                            "has been taken over by $user\n" );
633                                 }
634                                 elsif ($pkg->{'builder'} eq $user) {
635                                         print "$name: Note: already taken by you.\n";
636                                         print "$name: ok\n" if $verbose;
637                                         return;
638                                 }
639                                 else {
640                                         $ok = 0;
641                                         $reason = "already taken by $pkg->{'builder'}";
642                                         $reason .= " (in newer version $pkg->{'version'})"
643                                                 if !version_eq($pkg->{'version'}, $version);
644                                 }
645                         }
646                 }
647                 elsif ($pkg->{'state'} =~ /^Failed/ &&
648                            pkg_version_eq($pkg, $version)) {
649                         if ($opt_override) {
650                                 print "The following package previously failed ",
651                                           "(by $pkg->{'builder'})\n",
652                                           "but overriding this as you request:\n";
653                                 send_mail( $pkg->{'builder'}, "failed package takeover",
654                                                    "The package '$name' (version $version) that ".
655                                                    "is taken by you\n".
656                                                    "and has failed previously has been taken over ".
657                                                    "by $user\n" )
658                                         if $pkg->{'builder'} ne $user;
659                         }
660                         else {
661                                 $ok = 0;
662                                 $reason = "build of $version failed previously:\n    ";
663                                 $reason .= join( "\n    ", split( "\n", $pkg->{'failed'} ));
664                                 $reason .= "\nalso the package doesn't need builing"
665                                         if $pkg->{'state'} eq 'Failed-Removed';
666                         }
667                 }
668         }
669         if ($ok) {
670                 my $ok = 'ok';
671                 if ($pkg->{'binary_nmu_version'}) {
672                         print "$name: Warning: needs binary NMU $pkg->{'binary_nmu_version'}\n" .
673                               "$pkg->{'binary_nmu_changelog'}\n";
674                         $ok = 'aok';
675                 } else {
676                         print "$name: Warning: Previous version failed!\n"
677                                 if $pkg->{'previous_state'} =~ /^Failed/ ||
678                                    $pkg->{'state'} =~ /^Failed/;
679                 }
680                 change_state( \$pkg, 'Building' );
681                 $pkg->{'package'} = $name;
682                 $pkg->{'version'} = $version;
683                 $pkg->{'builder'} = $user;
684                 log_ta( $pkg, "--take" );
685                 update_source_info($pkg);
686                 print "$name: $ok\n" if $verbose;
687         }
688         else {
689                 print "$name: NOT OK!\n  $reason\n";
690         }
691 }
692
693 sub add_one_attempted {
694         my $name = shift;
695         my $version = shift;
696         my $pkg = get_source_info($name);
697
698         if (!defined($pkg)) {
699                 print "$name: not registered yet.\n";
700                 return;
701         }
702
703         if (($pkg->{'state'} ne "Building") && ($pkg->{'state'} ne "Build-Attempted")) {
704                 print "$name: not taken for building (state is $pkg->{'state'}). ",
705                           "Skipping.\n";
706                 return;
707         }
708         if ($pkg->{'builder'} ne $user) {
709                 print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
710                 return;
711         }
712         elsif ( !pkg_version_eq($pkg, $version) ) {
713                 print "$name: version mismatch ".
714                           "$(pkg->{'version'} ".
715                           "by $pkg->{'builder'})\n";
716                 return;
717         }
718
719         change_state( \$pkg, 'Build-Attempted' );
720         log_ta( $pkg, "--attempted" );
721         update_source_info($pkg);
722         print "$name: registered as uploaded\n" if $verbose;
723 }
724
725 sub add_one_built {
726         my $name = shift;
727         my $version = shift;
728         my $pkg = get_source_info($name);
729
730         if (!defined($pkg)) {
731                 print "$name: not registered yet.\n";
732                 return;
733         }
734
735         if (($pkg->{'state'} ne "Building") && ($pkg->{'state'} ne "Build-Attempted")) {
736                 print "$name: not taken for building (state is $pkg->{'state'}). ",
737                           "Skipping.\n";
738                 return;
739         }
740         if ($pkg->{'builder'} ne $user) {
741                 print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
742                 return;
743         }
744         elsif ( !pkg_version_eq($pkg, $version) ) {
745                 print "$name: version mismatch ".
746                           "$(pkg->{'version'} ".
747                           "by $pkg->{'builder'})\n";
748                 return;
749         }
750         change_state( \$pkg, 'Built' );
751         log_ta( $pkg, "--built" );
752         update_source_info($pkg);
753         print "$name: registered as built\n" if $verbose;
754 }
755
756 sub add_one_uploaded {
757         my $name = shift;
758         my $version = shift;
759         my $pkg = get_source_info($name);
760
761         if (!defined($pkg)) {
762                 print "$name: not registered yet.\n";
763                 return;
764         }
765
766         if ($pkg->{'state'} eq "Uploaded" &&
767                 pkg_version_eq($pkg,$version)) {
768                 print "$name: already uploaded\n";
769                 return;
770         }
771         if (!isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
772                 print "$name: not taken for building (state is $pkg->{'state'}). ",
773                           "Skipping.\n";
774                 return;
775         }
776         if ($pkg->{'builder'} ne $user) {
777                 print "$name: not taken by you, but by $pkg->{'builder'}. Skipping.\n";
778                 return;
779         }
780         # strip epoch -- buildd-uploader used to go based on the filename.
781         # (to remove at some point)
782         my $pkgver;
783         ($pkgver = $pkg->{'version'}) =~ s/^\d+://;
784         $version =~ s/^\d+://; # for command line use
785         if ($pkg->{'binary_nmu_version'} ) {
786                 my $nmuver = binNMU_version($pkgver, $pkg->{'binary_nmu_version'});
787                 if (!version_eq( $nmuver, $version )) {
788                         print "$name: version mismatch ($nmuver registered). ",
789                                   "Skipping.\n";
790                         return;
791                 }
792         } elsif (!version_eq($pkgver, $version)) {
793                 print "$name: version mismatch ($pkg->{'version'} registered). ",
794                           "Skipping.\n";
795                 return;
796         }
797
798         change_state( \$pkg, 'Uploaded' );
799         log_ta( $pkg, "--uploaded" );
800         update_source_info($pkg);
801         print "$name: registered as uploaded\n" if $verbose;
802 }
803
804 sub add_one_failed {
805         my $name = shift;
806         my $version = shift;
807         my ($state, $cat);
808         my $pkg = get_source_info($name);
809
810         if (!defined($pkg)) {
811                 print "$name: not registered yet.\n";
812                 return;
813         }
814         $state = $pkg->{'state'};
815
816         if ($state eq "Not-For-Us") {
817                 print "$name: not suitable for this architecture anyway. Skipping.\n";
818                 return;
819         }
820         elsif ($state eq "Failed-Removed") {
821                 print "$name: failed previously and doesn't need building. Skipping.\n";
822                 return;
823         }
824         elsif ($state eq "Installed") {
825                 print "$name: Is already installed in archive. Skipping.\n";
826                 return;
827         }
828         elsif ($pkg->{'builder'} &&
829                    (($user ne $pkg->{'builder'}) &&
830                     !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user))) {
831                 print "$name: not taken by you, but by ".
832                           "$pkg->{'builder'}. Skipping.\n";
833                 return;
834         }
835         elsif ( !pkg_version_eq($pkg, $version) ) {
836                 print "$name: version mismatch ".
837                           "$(pkg->{'version'} ".
838                           "by $pkg->{'builder'})\n";
839                 return;
840         }
841
842         $cat = $category;
843         if (!$cat && $fail_reason =~ /^\[([^\]]+)\]/) {
844                 $cat = $1;
845                 $cat = $short_category{$cat} if exists $short_category{$cat};
846                 if (!isin( $cat, values %short_category )) {
847                         print "$name: Warning: unknown category $cat; discarded\n";
848                         $cat = "";
849                 }
850                 $fail_reason =~ s/^\[[^\]]+\][ \t]*\n*//;
851         }
852
853         if ($state eq "Needs-Build") {
854                 print "$name: Warning: not registered for building previously, ".
855                           "but processing anyway.\n";
856         }
857         elsif ($state eq "Uploaded") {
858                 print "$name: Warning: marked as uploaded previously, ".
859                           "but processing anyway.\n";
860         }
861         elsif ($state eq "Dep-Wait") {
862                 print "$name: Warning: marked as waiting for dependencies, ".
863                           "but processing anyway.\n";
864         }
865         elsif ($state eq "BD-Uninstallable") {
866                 print "$name: Warning: marked as having uninstallable build-dependencies, ".
867                           "but processing anyway.\n";
868         }
869         elsif ($state eq "Failed") {
870                 print "$name: already registered as failed; will append new message\n"
871                         if $fail_reason;
872                 print "$name: already registered as failed; changing category\n"
873                         if $cat;
874         }
875
876         if (($cat eq "reminder-sent" || $cat eq "nmu-offered") &&
877                 defined $pkg->{'failed_category'} &&
878                 $pkg->{'failed_category'} ne $cat) {
879                 (my $action = $cat) =~ s/-/ /;
880                 $fail_reason .= "\n$short_date: $action";
881         }
882
883         change_state( \$pkg, 'Failed' );
884         $pkg->{'builder'} = $user;
885         $pkg->{'failed'} .= "\n" if $pkg->{'failed'};
886         $pkg->{'failed'} .= $fail_reason;
887         $pkg->{'failed_category'} = $cat if $cat;
888         if (defined $pkg->{'permbuildpri'}) {
889                 $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
890         } else {
891                 delete $pkg->{'buildpri'};
892         }
893         log_ta( $pkg, "--failed" );
894         update_source_info($pkg);
895         print "$name: registered as failed\n" if $verbose;
896 }
897
898 sub add_one_notforus {
899         my $name = shift;
900         my $version = shift;
901         my $pkg = get_source_info($name);
902
903         if ($pkg->{'state'} eq 'Not-For-Us') {
904                 # reset Not-For-Us state in case it's called twice; this is
905                 # the only way to get a package out of this state...
906                 # There is no really good state in which such packages should
907                 # be put :-( So use Failed for now.
908                 change_state( \$pkg, 'Failed' );
909                 $pkg->{'package'} = $name;
910                 $pkg->{'failed'} = "Was Not-For-Us previously";
911                 delete $pkg->{'builder'};
912                 delete $pkg->{'depends'};
913                 log_ta( $pkg, "--no-build(rev)" );
914                 print "$name: now not unsuitable anymore\n";
915
916                 send_mail( $conf::notforus_maint,
917                                    "$name moved out of Not-For-Us state",
918                                    "The package '$name' has been moved out of the Not-For-Us ".
919                                    "state by $user.\n".
920                                    "It should probably also be removed from ".
921                                    "Packages-arch-specific or\n".
922                                    "the action was wrong.\n" )
923                         if $conf::notforus_maint;
924         }
925         else {
926                 change_state( \$pkg, 'Not-For-Us' );
927                 $pkg->{'package'} = $name;
928                 delete $pkg->{'builder'};
929                 delete $pkg->{'depends'};
930                 delete $pkg->{'buildpri'};
931                 delete $pkg->{'binary_nmu_version'};
932                 delete $pkg->{'binary_nmu_changelog'};
933                 log_ta( $pkg, "--no-build" );
934                 print "$name: registered as unsuitable\n" if $verbose;
935
936                 send_mail( $conf::notforus_maint,
937                                    "$name set to Not-For-Us",
938                                    "The package '$name' has been set to state Not-For-Us ".
939                                    "by $user.\n".
940                                    "It should probably also be added to ".
941                                    "Packages-arch-specific or\n".
942                                    "the Not-For-Us state is wrong.\n" )
943                         if $conf::notforus_maint;
944         }
945         update_source_info($pkg);
946 }
947
948 sub add_one_needsbuild {
949         my $name = shift;
950         my $version = shift;
951         my $state;
952         my $pkg = get_source_info($name);
953
954         if (!defined($pkg)) {
955                 print "$name: not registered; can't give back.\n";
956                 return;
957         }
958         $state = $pkg->{'state'};
959
960         if ($state eq "BD-Uninstallable") {
961                 if ($opt_override) {
962                         print "$name: Forcing uninstallability mark to be removed. This is not permanent and might be reset with the next trigger run\n";
963
964                         change_state( \$pkg, 'Needs-Build' );
965                         delete $pkg->{'builder'};
966                         delete $pkg->{'depends'};
967                         log_ta( $pkg, "--give-back" );
968                         update_source_info($pkg);
969                         print "$name: given back\n" if $verbose;
970                         return;
971                 }
972                 else {
973                         print "$name: has uninstallable build-dependencies. Skipping\n",
974                                   "  (use --override to clear dependency list and ",
975                                   "give back anyway)\n";
976                         return;
977                 }
978         }
979         elsif ($state eq "Dep-Wait") {
980                 if ($opt_override) {
981                         print "$name: Forcing source dependency list to be cleared\n";
982                 }
983                 else {
984                         print "$name: waiting for source dependencies. Skipping\n",
985                                   "  (use --override to clear dependency list and ",
986                                   "give back anyway)\n";
987                         return;
988                 }
989         }
990         elsif (!isin( $state, qw(Building Built Build-Attempted))) {
991                 print "$name: not taken for building (state is $state).";
992                 if ($opt_override) {
993                         print "\n$name: Forcing give-back\n";
994                 }
995                 else {
996                         print " Skipping.\n";
997                         return;
998                 }
999         }
1000         if (defined ($pkg->{'builder'}) && $user ne $pkg->{'builder'} &&
1001                 !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user) &&
1002                 !$opt_override) {
1003                 print "$name: not taken by you, but by ".
1004                           "$pkg->{'builder'}. Skipping.\n";
1005                 return;
1006         }
1007         if (!pkg_version_eq($pkg, $version)) {
1008                 print "$name: version mismatch ($pkg->{'version'} registered). ",
1009                           "Skipping.\n";
1010                 return;
1011         }
1012         if ($distribution eq "unstable") {
1013                 change_state( \$pkg, 'BD-Uninstallable' );
1014                 $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
1015         } else {
1016                 change_state( \$pkg, 'Needs-Build' );
1017         }
1018         $pkg->{'builder'} = undef;
1019         $pkg->{'depends'} = undef;
1020         log_ta( $pkg, "--give-back" );
1021         update_source_info($pkg);
1022         print "$name: given back\n" if $verbose;
1023 }
1024
1025 sub set_one_binnmu {
1026         my $name = shift;
1027         my $version = shift;
1028         my $pkg = get_source_info($name);
1029         my $state;
1030
1031         if (!defined($pkg)) {
1032                 print "$name: not registered; can't register for binNMU.\n";
1033                 return;
1034         }
1035         my $db_ver = $pkg->{'version'};
1036
1037         if (!version_eq($db_ver, $version)) {
1038                 print "$name: version mismatch ($db_ver registered). ",
1039                           "Skipping.\n";
1040                 return;
1041         }
1042         $state = $pkg->{'state'};
1043
1044         if (defined $pkg->{'binary_nmu_version'}) {
1045                 if ($binNMUver == 0) {
1046                         change_state( \$pkg, 'Installed' );
1047                         delete $pkg->{'builder'};
1048                         delete $pkg->{'depends'};
1049                         delete $pkg->{'binary_nmu_version'};
1050                         delete $pkg->{'binary_nmu_changelog'};
1051                 } elsif ($binNMUver <= $pkg->{'binary_nmu_version'}) {
1052                         print "$name: already building binNMU $pkg->{'binary_nmu_version'}\n";
1053                         return;
1054                 } else {
1055                         $pkg->{'binary_nmu_version'} = $binNMUver;
1056                         $pkg->{'binary_nmu_changelog'} = $fail_reason;
1057                         $pkg->{'notes'} = 'out-of-date';
1058                         $pkg->{'buildpri'} = $pkg->{'permbuildpri'}
1059                                 if (defined $pkg->{'permbuildpri'});
1060                 }
1061                 log_ta( $pkg, "--binNMU" );
1062                 update_source_info($pkg);
1063                 return;
1064         } elsif ($binNMUver == 0) {
1065                 print "${name}_$version: no scheduled binNMU to cancel.\n";
1066                 return;
1067         }
1068
1069         if ($state ne 'Installed') {
1070                 print "${name}_$version: not installed; can't register for binNMU.\n";
1071                 return;
1072         }
1073
1074         my $fullver = binNMU_version($version,$binNMUver);
1075         if ( version_lesseq( $fullver, $pkg->{'installed_version'} ) )
1076         {
1077                 print "$name: binNMU $fullver is not newer than current version $pkg->{'installed_version'}\n";
1078                 return;
1079         }
1080
1081         if ($distribution eq "unstable") {
1082                 change_state( \$pkg, 'BD-Uninstallable' );
1083                 $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
1084         }
1085         else
1086         {
1087                 change_state( \$pkg, 'Needs-Build' );
1088         }
1089         delete $pkg->{'builder'};
1090         delete $pkg->{'depends'};
1091         $pkg->{'binary_nmu_version'} = $binNMUver;
1092         $pkg->{'binary_nmu_changelog'} = $fail_reason;
1093         $pkg->{'notes'} = 'out-of-date';
1094         log_ta( $pkg, "--binNMU" );
1095         update_source_info($pkg);
1096         print "${name}: registered for binNMU $fullver\n" if $verbose;
1097 }
1098
1099 sub set_one_buildpri {
1100         my $name = shift;
1101         my $version = shift;
1102         my $key = shift;
1103         my $pkg = get_source_info($name);
1104         my $state;
1105
1106         if (!defined($pkg)) {
1107                 print "$name: not registered; can't set priority.\n";
1108                 return;
1109         }
1110         $state = $pkg->{'state'};
1111
1112         if ($state eq "Not-For-Us") {
1113                 print "$name: not suitable for this architecture. Skipping.\n";
1114                 return;
1115         } elsif ($state eq "Failed-Removed") {
1116                 print "$name: failed previously and doesn't need building. Skipping.\n";
1117                 return;
1118         }
1119         if (!pkg_version_eq($pkg, $version)) {
1120                 print "$name: version mismatch ($pkg->{'version'} registered). ",
1121                           "Skipping.\n";
1122                 return;
1123         }
1124         if ( $build_priority == 0 ) {
1125                 delete $pkg->{'buildpri'}
1126                         if $key eq 'permbuildpri' and defined $pkg->{'buildpri'}
1127                         and $pkg->{'buildpri'} == $pkg->{$key};
1128                 delete $pkg->{$key};
1129         } else {
1130                 $pkg->{'buildpri'} = $build_priority
1131                         if $key eq 'permbuildpri';
1132                 $pkg->{$key} = $build_priority;
1133         }
1134         update_source_info($pkg);
1135         print "$name: set to build priority $build_priority\n" if $verbose;
1136 }
1137
1138 sub add_one_depwait {
1139         my $name = shift;
1140         my $version = shift;
1141         my $state;
1142         my $pkg = get_source_info($name);
1143
1144         if (!defined($pkg)) {
1145                 print "$name: not registered yet.\n";
1146                 return;
1147         }
1148         $state = $pkg->{'state'};
1149
1150         if ($state eq "Dep-Wait") {
1151                 print "$name: merging with previously registered dependencies\n";
1152         }
1153         
1154         if (isin( $state, qw(Needs-Build Failed BD-Uninstallable))) {
1155                 print "$name: Warning: not registered for building previously, ".
1156                           "but processing anyway.\n";
1157         }
1158         elsif ($state eq "Not-For-Us") {
1159                 print "$name: not suitable for this architecture anyway. Skipping.\n";
1160                 return;
1161         }
1162         elsif ($state eq "Failed-Removed") {
1163                 print "$name: failed previously and doesn't need building. Skipping.\n";
1164                 return;
1165         }
1166         elsif ($state eq "Installed") {
1167                 print "$name: Is already installed in archive. Skipping.\n";
1168                 return;
1169         }
1170         elsif ($state eq "Uploaded") {
1171                 print "$name: Is already uploaded. Skipping.\n";
1172                 return;
1173         }
1174         elsif ($pkg->{'builder'} &&
1175                    $user ne $pkg->{'builder'}) {
1176                 print "$name: not taken by you, but by ".
1177                           "$pkg->{'builder'}. Skipping.\n";
1178                 return;
1179         }
1180         elsif ( !pkg_version_eq($pkg,$version)) {
1181                 print "$name: version mismatch ".
1182                           "($pkg->{'version'} ".
1183                           "by $pkg->{'builder'})\n";
1184                 return;
1185         }
1186         elsif ($fail_reason =~ /^\s*$/ ||
1187                    !parse_deplist( $fail_reason, 1 )) {
1188                 print "$name: Bad dependency list\n";
1189                 return;
1190         }
1191         change_state( \$pkg, 'Dep-Wait' );
1192         $pkg->{'builder'} = $user;
1193         if (defined $pkg->{'permbuildpri'}) {
1194                 $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
1195         } else {
1196                 delete $pkg->{'buildpri'};
1197         }
1198         my $deplist = parse_deplist( $pkg->{'depends'} );
1199         my $new_deplist = parse_deplist( $fail_reason );
1200         # add new dependencies, maybe overwriting old entries
1201         foreach (keys %$new_deplist) {
1202                 $deplist->{$_} = $new_deplist->{$_};
1203         }
1204         $pkg->{'depends'} = build_deplist($deplist);
1205         log_ta( $pkg, "--dep-wait" );
1206         update_source_info($pkg);
1207         print "$name: registered as waiting for dependencies\n" if $verbose;
1208 }
1209
1210
1211 sub parse_sources {
1212         my %pkgs;
1213         my %srcver;
1214         my $name;
1215         my $full = shift;
1216
1217         my $db = get_all_source_info();
1218
1219         local($/) = ""; # read in paragraph mode
1220         while( <> ) {
1221                 my( $version, $arch, $section, $priority, $builddep, $buildconf, $binaries );
1222                 s/\s*$//m;
1223                 /^Package:\s*(\S+)$/mi and $name = $1;
1224                 /^Version:\s*(\S+)$/mi and $version = $1;
1225                 /^Architecture:\s*(\S+)$/mi and $arch = $1;
1226                 /^Section:\s*(\S+)$/mi and $section = $1;
1227                 /^Priority:\s*(\S+)$/mi and $priority = $1;
1228                 /^Build-Depends:\s*(.*)$/mi and $builddep = $1;
1229                 /^Build-Conflicts:\s*(.*)$/mi and $buildconf = $1;
1230                 /^Binary:\s*(.*)$/mi and $binaries = $1;
1231
1232                 next if (defined $srcver{$name} and version_less( $version, $srcver{$name} ));
1233                 $srcver{$name} = $version;
1234
1235                 $pkgs{$name}{'ver'} = $version;
1236                 $pkgs{$name}{'bin'} = $binaries;
1237                 $pkgs{$name}{'dep'} = $builddep;
1238                 $pkgs{$name}{'conf'} = $buildconf;
1239                 my $pkg = $db->{$name};
1240
1241                 if (defined $pkg) {
1242                         my $change = 0;
1243
1244                         if ($arch eq "all" && !version_less( $version, $pkg->{'version'} )) {
1245                                 # package is now Arch: all, delete it from db
1246                                 change_state( \$pkg, 'deleted' );
1247                                 log_ta( $pkg, "--merge-sources" );
1248                                 print "$name ($pkg->{'version'}): deleted ".
1249                                           "from database, because now Arch: all\n"
1250                                                   if $verbose;
1251                                 del_source_info($name);
1252                                 delete $db->{$name};
1253                                 next;
1254                         }
1255
1256                         # The "Version" should always be the source version --
1257                         # not a possible binNMU version number.
1258                         $pkg->{'version'} = $version, $change++
1259                                 if ($pkg->{'state'} eq 'Installed' and
1260                                 !version_eq( $pkg->{'version'}, $version));
1261                         # Always update priority and section, if available
1262                         $pkg->{'priority'} = $priority, $change++
1263                                 if defined $priority and (not defined($pkg->{'priority'}) or $pkg->{'priority'} ne $priority);
1264
1265                         $pkg->{'section'} = $section, $change++
1266                                 if defined $section and (not defined($pkg->{'section'}) or $pkg->{'section'} ne $section);
1267
1268                         update_source_info($pkg) if $change;
1269                 }
1270         }
1271         # Now that we only have the latest source version, build the list
1272         # of binary packages from the Sources point of view
1273         foreach $name (keys %pkgs) {
1274             foreach my $bin (split( /\s*,\s*/, $pkgs{$name}{'bin'} ) ) {
1275                 $merge_binsrc{$bin} = $name;
1276             }
1277         }
1278         # remove installed packages that no longer have source available
1279         # or binaries installed
1280         foreach $name (keys %$db) {
1281                 next if $name =~ /^_/;
1282                 my $pkg = $db->{$name};
1283                 if (not defined($pkgs{$name})) {
1284                         change_state( \$pkg, 'deleted' );
1285                         log_ta( $pkg, "--merge-sources" );
1286                         print "$name ($pkg->{'version'}): ".
1287                                   "deleted from database, because ".
1288                                   "not in Sources anymore\n"
1289                                           if $verbose;
1290                         del_source_info($name);
1291                         delete $db->{$name};
1292                 } else {
1293                         next if !isin( $pkg->{'state'}, qw(Installed) );
1294                         if ($full && not defined $merge_srcvers{$name}) {
1295                             change_state( \$pkg, 'deleted' );
1296                             log_ta( $pkg, "--merge-sources" );
1297                             print "$name ($pkg->{'version'}): ".
1298                                       "deleted from database, because ".
1299                                       "binaries don't exist anymore\n"
1300                                               if $verbose;
1301                             del_source_info($name);
1302                             delete $db->{$name};
1303                         } elsif ($full && version_less( $merge_srcvers{$name}, $pkg->{'version'})) {
1304                             print "$name ($pkg->{'version'}): ".
1305                                       "package is Installed but binaries are from ".
1306                                       $merge_srcvers{$name}. "\n"
1307                                               if $verbose;
1308                         }
1309                 }
1310         }
1311         return \%pkgs;
1312 }
1313
1314 # This function looks through a Packages file and sets the state of
1315 # packages to 'Installed'
1316 sub parse_packages {
1317         my $depwait_only = shift;
1318         my $installed;
1319
1320         my $pkgs = get_all_source_info();
1321         local($/) = ""; # read in paragraph mode
1322         while( <> ) {
1323                 my( $name, $version, $depends, $source, $sourcev, $architecture, $provides, $binaryv, $binnmu );
1324                 s/\s*$//m;
1325                 /^Package:\s*(\S+)$/mi and $name = $1;
1326                 /^Version:\s*(\S+)$/mi and $version = $1;
1327                 /^Depends:\s*(.*)$/mi and $depends = $1;
1328                 /^Source:\s*(\S+)(\s*\((\S+)\))?$/mi and ($source,$sourcev) = ($1, $3);
1329                 /^Architecture:\s*(\S+)$/mi and $architecture = $1;
1330                 /^Provides:\s*(.*)$/mi and $provides = $1;
1331                 next if !$name || !$version;
1332                 next if ($arch ne $architecture and $architecture ne "all");
1333                 next if (defined ($installed->{$name}) and $installed->{$name}{'version'} ne "" and
1334                         version_lesseq( $version, $installed->{$name}{'version'} ));
1335                 $installed->{$name}{'version'} = $version;
1336                 next if $depwait_only;
1337                 $installed->{$name}{'depends'} = $depends;
1338                 $installed->{$name}{'all'} = 1 if $architecture eq "all";
1339                 undef $installed->{$name}{'Provider'};
1340                 $installed->{$name}{'Source'} = $source ? $source : $name;
1341                 if ($provides) {
1342                     foreach (split( /\s*,\s*/, $provides )) {
1343                         if (not defined ($installed->{$_})) {
1344                             $installed->{$_}{'version'} = "";
1345                             $installed->{$_}{'Provider'} = $name;
1346                         }
1347                     }
1348                 }
1349                 if ( $version =~ /\+b(\d+)$/ ) {
1350                     $binnmu = $1;
1351                 }
1352                 $version = $sourcev if $sourcev;
1353                 $binaryv = $version;
1354                 $binaryv =~ s/\+b\d+$//;
1355                 $installed->{$name}{'Sourcev'} = $sourcev ? $sourcev : $binaryv;
1356                 $binaryv .= "+b$binnmu" if defined($binnmu);
1357
1358                 next if $architecture ne $arch;
1359                 $name = $source if $source;
1360                 next if defined($merge_srcvers{$name}) and $merge_srcvers{$name} eq $version;
1361                 $merge_srcvers{$name} = $version;
1362
1363                 my $pkg = $pkgs->{$name};
1364
1365                 if (defined $pkg) {
1366                         if (isin( $pkg->{'state'}, qw(Not-For-Us)) ||
1367                                 (isin($pkg->{'state'}, qw(Installed)) &&
1368                                  version_lesseq($binaryv, $pkg->{'installed_version'}))) {
1369                                 print "Skipping $name because State == $pkg->{'state'}\n"
1370                                         if $verbose >= 2;
1371                                 next;
1372                         }
1373                         if ($pkg->{'binary_nmu_version'} ) {
1374                                 my $nmuver = binNMU_version($pkg->{'version'}, $pkg->{'binary_nmu_version'});
1375                                 if (version_less( $binaryv, $nmuver )) {
1376                                         print "Skipping $name ($version) because have newer ".
1377                                                 "version ($nmuver) in db.\n"
1378                                                         if $verbose >= 2;
1379                                         next;
1380                                 }
1381                         } elsif (version_less($version, $pkg->{'version'})) {
1382                                 print "Skipping $name ($version) because have newer ".
1383                                         "version ($pkg->{'version'}) in db.\n"
1384                                                 if $verbose >= 2;
1385                                 next;
1386                         }
1387
1388                         if (!pkg_version_eq($pkg, $version) &&
1389                            $pkg->{'state'} ne "Installed") {
1390                                 warn "Warning: $name: newer version than expected appeared ".
1391                                          "in archive ($version vs. $pkg->{'version'})\n";
1392                                 delete $pkg->{'builder'};
1393                         }
1394
1395                         if (!isin( $pkg->{'state'}, qw(Uploaded) )) {
1396                                 warn "Warning: Package $name was not in uploaded state ".
1397                                          "before (but in '$pkg->{'state'}').\n";
1398                                 delete $pkg->{'builder'};
1399                                 delete $pkg->{'depends'};
1400                         }
1401                 } else {
1402                         $pkg = {};
1403                         $pkg->{'version'} = $version;
1404                 }
1405                 
1406                 change_state( \$pkg, 'Installed' );
1407                 $pkg->{'package'} = $name;
1408                 $pkg->{'installed_version'} = $binaryv;
1409                 if (defined $pkg->{'permbuildpri'}) {
1410                         $pkg->{'buildpri'} = $pkg->{'permbuildpri'};
1411                 } else {
1412                         delete $pkg->{'buildpri'};
1413                 }
1414                 $pkg->{'version'} = $version
1415                         if version_less( $pkg->{'version'}, $version);
1416                 delete $pkg->{'binary_nmu_version'};
1417                 delete $pkg->{'binary_nmu_changelog'};
1418                 log_ta( $pkg, "--merge-packages" );
1419                 update_source_info($pkg);
1420                 print "$name ($version) is up-to-date now.\n" if $verbose;
1421         }
1422
1423         check_dep_wait( "--merge-packages", $installed );
1424         return $installed;
1425 }
1426
1427 sub pretend_avail {
1428         my ($package, $name, $version, $installed);
1429         
1430         foreach $package (@_) {
1431                 $package =~ s,^.*/,,; # strip path
1432                 $package =~ s/\.(dsc|diff\.gz|tar\.gz|deb)$//; # strip extension
1433                 $package =~ s/_[\w\d]+\.changes$//; # strip extension
1434                 if ($package =~ /^([\w\d.+-]+)_([\w\d:.+~-]+)/) {
1435                         ($name,$version) = ($1,$2);
1436                 }
1437                 else {
1438                         warn "$package: can't extract package name and version ".
1439                                  "(bad format)\n";
1440                         next;
1441                 }
1442                 $installed->{$name}{'version'} = $version;
1443         }
1444
1445         check_dep_wait( "--pretend-avail", $installed );
1446 }
1447
1448 sub check_dep_wait {
1449         my $action = shift;
1450         my $installed = shift;
1451         
1452         # check all packages in state Dep-Wait if dependencies are all
1453         # available now
1454         my $name;
1455         my $db = get_all_source_info();
1456         foreach $name (keys %$db) {
1457                 next if $name =~ /^_/;
1458                 my $pkg = $db->{$name};
1459                 next if $pkg->{'state'} ne "Dep-Wait";
1460                 my $deps = $pkg->{'depends'};
1461                 if (!$deps) {
1462                         print "$name: was in state Dep-Wait, but with empty ",
1463                                   "dependencies!\n";
1464                         goto make_needs_build;
1465                 }
1466                 my $deplist = parse_deplist($deps);
1467                 my $new_deplist;
1468                 my $allok = 1;
1469                 my @removed_deps;
1470                 foreach (keys %$deplist) {
1471                         if (!exists $installed->{$_} ||
1472                                 ($deplist->{$_}->{'rel'} && $deplist->{$_}->{'version'} &&
1473                                  !version_compare( $installed->{$_}{'version'},
1474                                                                    $deplist->{$_}->{'rel'},
1475                                                                    $deplist->{$_}->{'version'}))) {
1476                                 $allok = 0;
1477                                 $new_deplist->{$_} = $deplist->{$_};
1478                         }
1479                         else {
1480                                 push( @removed_deps, $_ );
1481                         }
1482                 }
1483                 if ($allok) {
1484                   make_needs_build:
1485                         change_state( \$pkg, 'Needs-Build' );
1486                         log_ta( $pkg, $action );
1487                         delete $pkg->{'builder'};
1488                         delete $pkg->{'depends'};
1489                         print "$name ($pkg->{'version'}) has all ",
1490                                   "dependencies available now\n" if $verbose;
1491                         $new_vers{$name}++;
1492                         update_source_info($pkg);
1493                 }
1494                 elsif (@removed_deps) {
1495                         $pkg->{'depends'} = build_deplist( $new_deplist );
1496                         print "$name ($pkg->{'version'}): some dependencies ",
1497                                   "(@removed_deps) available now, but not all yet\n"
1498                                 if $verbose;
1499                         update_source_info($pkg);
1500                 }
1501         }
1502 }
1503
1504 # This function accepts quinn-diff output (either from a file named on
1505 # the command line, or on stdin) and sets the packages named there to
1506 # state 'Needs-Build'.
1507 sub parse_quinn_diff {
1508         my $partial = shift;
1509         my %quinn_pkgs;
1510         my $dubious = "";
1511         
1512         my $pkgs = get_all_source_info();
1513
1514         while( <> ) {
1515                 my $change = 0;
1516                 next if !m,^([-\w\d/]*)/                        # section
1517                                ([-\w\d.+]+)_                    # package name
1518                                    ([\w\d:.~+-]+)\.dsc\s*       # version
1519                                    \[([^:]*):                           # priority
1520                                    ([^]]+)\]\s*$,x;                     # rest of notes
1521                 my($section,$name,$version,$priority,$notes) = ($1, $2, $3, $4, $5);
1522                 $quinn_pkgs{$name}++;
1523                 $section ||= "unknown";
1524                 $priority ||= "unknown";
1525                 $priority = "unknown" if $priority eq "-";
1526                 $priority = "standard" if ($name eq "debian-installer");
1527
1528                 my $pkg = $pkgs->{$name};
1529
1530                 # Always update section and priority.
1531                 if (defined($pkg)) {
1532
1533                         $pkg->{'section'}  = $section, $change++ if not defined
1534                                 $pkg->{'section'} or $section ne "unknown";
1535                         $pkg->{'priority'} = $priority, $change++ if not defined
1536                                 $pkg->{'priority'} or $priority ne "unknown";
1537                 }
1538
1539                 if (defined($pkg) &&
1540                         $pkg->{'state'} =~ /^Dep-Wait/ &&
1541                         version_less( $pkg->{'version'}, $version )) {
1542                         change_state( \$pkg, 'Dep-Wait' );
1543                         $pkg->{'version'}  = $version;
1544                         delete $pkg->{'binary_nmu_version'};
1545                         delete $pkg->{'binary_nmu_changelog'};
1546                         log_ta( $pkg, "--merge-quinn" );
1547                         $change++;
1548                         print "$name ($version) still waiting for dependencies.\n"
1549                                 if $verbose;
1550                 }
1551                 elsif (defined($pkg) &&
1552                            $pkg->{'state'} =~ /-Removed$/ &&
1553                            version_eq($pkg->{'version'}, $version)) {
1554                         # reinstantiate a package that has been removed earlier
1555                         # (probably due to a quinn-diff malfunction...)
1556                         my $newstate = $pkg->{'state'};
1557                         $newstate =~ s/-Removed$//;
1558                         change_state( \$pkg, $newstate );
1559                         $pkg->{'version'}  = $version;
1560                         $pkg->{'notes'}    = $notes;
1561                         log_ta( $pkg, "--merge-quinn" );
1562                         $change++;
1563                         print "$name ($version) reinstantiated to $newstate.\n"
1564                                 if $verbose;
1565                 }
1566                 elsif (defined($pkg) &&
1567                            $pkg->{'state'} eq "Not-For-Us" &&
1568                            version_less( $pkg->{'version'}, $version )) {
1569                         # for Not-For-Us packages just update the version etc., but
1570                         # keep the state
1571                         change_state( \$pkg, "Not-For-Us" );
1572                         $pkg->{'package'}  = $name;
1573                         $pkg->{'version'}  = $version;
1574                         $pkg->{'notes'}    = $notes;
1575                         delete $pkg->{'builder'};
1576                         log_ta( $pkg, "--merge-quinn" );
1577                         $change++;
1578                         print "$name ($version) still Not-For-Us.\n" if $verbose;
1579                 }
1580                 elsif (!defined($pkg) ||
1581                            $pkg->{'state'} ne "Not-For-Us" &&
1582                            (version_less( $pkg->{'version'}, $version ) ||
1583                            ($pkg->{'state'} eq "Installed" && version_less($pkg->{'installed_version'}, $version)))) {
1584
1585                         if (defined( $pkg->{'state'} ) && isin( $pkg->{'state'}, qw(Building Built Build-Attempted))) {
1586                                 send_mail( $pkg->{'builder'},
1587                                                    "new version of $name (dist=$distribution)",
1588                                                    "As far as I'm informed, you're currently ".
1589                                                    "building the package $name\n".
1590                                                    "in version $pkg->{'version'}.\n\n".
1591                                                    "Now there's a new source version $version. ".
1592                                                    "If you haven't finished\n".
1593                                                    "compiling $name yet, you can stop it to ".
1594                                                    "save some work.\n".
1595                                                    "Just to inform you...\n".
1596                                                    "(This is an automated message)\n" );
1597                                 print "$name: new version ($version) while building ".
1598                                           "$pkg->{'version'} -- sending mail ".
1599                                           "to builder ($pkg->{'builder'})\n"
1600                                   if $verbose;
1601                         }
1602                         change_state( \$pkg, 'Needs-Build' );
1603                         $pkg->{'package'}  = $name;
1604                         $pkg->{'version'}  = $version;
1605                         $pkg->{'section'}  = $section;
1606                         $pkg->{'priority'} = $priority;
1607                         $pkg->{'notes'}    = $notes;
1608                         delete $pkg->{'builder'};
1609                         delete $pkg->{'binary_nmu_version'};
1610                         delete $pkg->{'binary_nmu_changelog'};
1611                         log_ta( $pkg, "--merge-quinn" );
1612                         $new_vers{$name}++;
1613                         $change++;
1614                         print "$name ($version) needs rebuilding now.\n" if $verbose;
1615                 }
1616                 elsif (defined($pkg) &&
1617                            !version_eq( $pkg->{'version'}, $version ) &&
1618                            isin( $pkg->{'state'}, qw(Installed Not-For-Us) )) {
1619                         print "$name: skipping because version in db ".
1620                                   "($pkg->{'version'}) is >> than ".
1621                                   "what quinn-diff says ($version) ".
1622                                   "(state is $pkg->{'state'})\n"
1623                                           if $verbose;
1624                         $dubious .= "$pkg->{'state'}: ".
1625                                                 "db ${name}_$pkg->{'version'} >> ".
1626                                                 "quinn $version\n" if !$partial;
1627                 }
1628                 elsif ($verbose >= 2) {
1629                         if ($pkg->{'state'} eq "Not-For-Us") {
1630                                 print "Skipping $name because State == ".
1631                                           "$pkg->{'state'}\n";
1632                         }
1633                         elsif (!version_less($pkg->{'version'}, $version)) {
1634                                 print "Skipping $name because version in db ".
1635                                           "($pkg->{'version'}) is >= than ".
1636                                           "what quinn-diff says ($version)\n";
1637                         }
1638                 }
1639                 update_source_info($pkg) if $change;
1640         }
1641
1642         if ($dubious) {
1643                 send_mail( $conf::db_maint,
1644                                    "Dubious versions in " . table_name() . " "
1645                                    . $distribution . " table",
1646                                    "The following packages have a newer version in the ".
1647                                    "wanna-build database\n".
1648                                    "than what quinn-diff says, and this is strange for ".
1649                                    "their state\n".
1650                                    "It could be caused by a lame mirror, or the version ".
1651                                    "in the database\n".
1652                                    "is wrong.\n\n".
1653                                    $dubious );
1654         }
1655
1656         # Now re-check the DB for packages in states Needs-Build, Failed,
1657         # Dep-Wait or BD-Uninstallable and remove them if they're not listed
1658         # anymore by quinn-diff.
1659         if ( !$partial ) {
1660                 my $name;
1661                 my $db = get_all_source_info();
1662                 foreach $name (keys %$db) {
1663                         next if $name =~ /^_/;
1664                         my $pkg = $db->{$name};
1665                         next if defined $pkg->{'binary_nmu_version'};
1666                         next if !isin( $pkg->{'state'},
1667                                                    qw(Needs-Build Building Built Build-Attempted Uploaded Failed Dep-Wait BD-Uninstallable) );
1668                         my $virtual_delete = $pkg->{'state'} eq 'Failed';
1669                                                                  
1670                         if (!$quinn_pkgs{$name}) {
1671                                 change_state( \$pkg, $virtual_delete ?
1672                                                           $pkg->{'state'}."-Removed" :
1673                                                           'deleted' );
1674                                 log_ta( $pkg, "--merge-quinn" );
1675                                 print "$name ($pkg->{'version'}): ".
1676                                           ($virtual_delete ? "(virtually) " : "") . "deleted ".
1677                                           "from database, because not in quinn-diff anymore\n"
1678                                                   if $verbose;
1679                                 if ($virtual_delete) {
1680                                     update_source_info($pkg);
1681                                 } else {
1682                                     del_source_info($name);
1683                                 }
1684                         }
1685                 }
1686         }
1687 }
1688
1689
1690 # for sorting priorities and sections
1691 BEGIN {
1692         %prioval = ( required             => -5,
1693                                  important            => -4,
1694                                  standard             => -3,
1695                                  optional             => -2,
1696                                  extra                => -1,
1697                                  unknown              => -1 );
1698         %sectval = ( 
1699                                  libs                   => -200,
1700                                  'debian-installer'     => -199,
1701                                  base                   => -198,
1702                                  devel                  => -197,
1703                                  kernel                 => -196,
1704                                  shells                 => -195,
1705                                  perl                   => -194,
1706                                  python                 => -193,
1707                                  graphics               => -192,
1708                                  admin                  => -191,
1709                                  utils                  => -190,
1710                                  x11                    => -189,
1711                                  editors                => -188,
1712                                  net                    => -187,
1713                                  httpd                  => -186,
1714                                  mail                   => -185,
1715                                  news                   => -184,
1716                                  tex                    => -183,
1717                                  text                   => -182,
1718                                  web                    => -181,
1719                                  vcs                    => -180,
1720                                  doc                    => -179,
1721                                  localizations          => -178,
1722                                  interpreters           => -177,
1723                                  ruby                   => -176,
1724                                  java                   => -175,
1725                                  ocaml                  => -174,
1726                                  lisp                   => -173,
1727                                  haskell                => -172,
1728                                  'cli-mono'             => -171,
1729                                  gnome                  => -170,
1730                                  kde                    => -169,
1731                                  xfce                   => -168,
1732                                  gnustep                => -167,
1733                                  database               => -166,
1734                                  video                  => -165,
1735                                  debug                  => -164,
1736                                  games                  => -163,
1737                                  misc                   => -162,
1738                                  fonts                  => -161,
1739                                  otherosfs              => -160,
1740                                  oldlibs                => -159,
1741                                  libdevel               => -158,
1742                                  sound                  => -157,
1743                                  math                   => -156,
1744                                  'gnu-r'                => -155,
1745                                  science                => -154,
1746                                  comm                   => -153,
1747                                  electronics            => -152,
1748                                  hamradio               => -151,
1749                                  embedded               => -150,
1750                                  php                    => -149,
1751                                  zope                   => -148,
1752         );
1753         foreach my $i (keys %sectval) {
1754                 $sectval{"contrib/$i"} = $sectval{$i}+40;
1755                 $sectval{"non-free/$i"} = $sectval{$i}+80;
1756         }
1757         $sectval{'unknown'}     = -165;
1758
1759         %catval =  ( "none"                           => -20,
1760                                  "uploaded-fixed-pkg" => -19,
1761                                  "fix-expected"       => -18,
1762                                  "reminder-sent"      => -17,
1763                                  "nmu-offered"        => -16,
1764                                  "easy"               => -15,
1765                                  "medium"                     => -14,
1766                                  "hard"                   => -13,
1767                                  "compiler-error"     => -12 );
1768 }
1769
1770 sub sort_list_func {
1771     my $map_funcs = {
1772         'C' => ['<=>', sub { return (-1) * $_[0]->{'calprio'}; }],
1773         'W' => ['<=>', sub { return (-1) * $_[0]->{'state_days'}; }],
1774         'P' => ['<=>', sub { return (-1) * $_[0]->{'buildpri'}; }],
1775         'p' => ['<=>', sub { return $prioval{$_[0]->{'priority'}}; }],
1776         's' => ['<=>', sub { return $sectval{$_[0]->{'section'}}; }],
1777         'n' => ['cmp', sub { return $_[0]->{'package'}; }],
1778         'b' => ['cmp', sub { return $_[0]->{'builder'}; }],
1779         'c' => ['<=>', sub { return ($_[0]->{'notes'} =~ /^(out-of-date|partial)/) ? 0: ($_[0]->{'notes'} =~ /^uncompiled/) ? 2 : 1; }],
1780         'f' => ['<=>', sub { return $catval{ $_[0]->{'failed_category'} ? $_[0]->{'failed_category'}: "none" }; }],
1781         'S' => ['<=>', sub { return $prioval{$_[0]->{'priority'}} > $prioval{'standard'}; }],
1782     };
1783
1784         foreach my $letter (split( //, $list_order )) {
1785             my $r;
1786             $r = &{$map_funcs->{$letter}[1]}($a) <=> &{$map_funcs->{$letter}[1]}($b) if $map_funcs->{$letter}[0] eq '<=>';
1787             $r = &{$map_funcs->{$letter}[1]}($a) cmp &{$map_funcs->{$letter}[1]}($b) if $map_funcs->{$letter}[0] eq 'cmp';
1788             return $r if $r != 0;
1789         }
1790         return 0;
1791 }
1792
1793 sub calculate_prio {
1794         my $priomap = $yamlmap->{priority};
1795         my $pkg = shift;
1796         my @s=split("/", $pkg->{'section'});
1797         $pkg->{'component'} = $s[0] if $s[1];
1798         $pkg->{'component'} ||= 'main';
1799         $pkg->{'calprio'} = 0;
1800         foreach my $k (keys %{$priomap->{keys}}) {
1801                 $pkg->{'calprio'} += $priomap->{keys}->{$k}{$pkg->{$k}} if $pkg->{$k} and $priomap->{keys}->{$k}{$pkg->{$k}};
1802         }
1803
1804         my $days = $pkg->{'state_days'};
1805         $days = $priomap->{'waitingdays'}->{'min'} if $priomap->{'waitingdays'}->{'min'} and $days < $priomap->{'waitingdays'}->{'min'};
1806         $days = $priomap->{'waitingdays'}->{'max'} if $priomap->{'waitingdays'}->{'max'} and $days > $priomap->{'waitingdays'}->{'max'};
1807         my $scale = $priomap->{'waitingdays'}->{'scale'} || 1;
1808         $pkg->{'calprio'} += $days * $scale;
1809
1810         $pkg->{'calprio'} += $pkg->{'permbuildpri'} if  $pkg->{'permbuildpri'};
1811         $pkg->{'calprio'} += $pkg->{'buildpri'} if  $pkg->{'buildpri'};
1812
1813         return $pkg;
1814 }
1815
1816
1817 sub use_fmt {
1818     my $r;
1819
1820     if (ref($_[0]) eq 'CODE') {
1821         $r = &{$_[0]};
1822     } else {
1823         $r = $_[0];
1824     }
1825
1826     shift;
1827     my $t = shift;
1828
1829     $r ||= "";
1830     return $r unless $t;
1831
1832     my $pkg = shift;
1833     my $var = shift;
1834     if (substr($t,0,1) eq '!') {
1835         $t = substr($t,1);
1836         return "" if $r;
1837     } else {
1838         return "" unless $r;
1839     }
1840     if ($t =~ /%/) {
1841         return print_format($t, $pkg, $var);
1842     }
1843     return $t;
1844 }
1845 sub make_fmt { my $c = shift; my $pkg = shift; my $var = shift; return sub { use_fmt($c, $_[0], $pkg, $var); } };
1846
1847 sub print_format {
1848     my $printfmt = shift;
1849     my $pkg = shift;
1850     my $var = shift;
1851 =pod
1852 Within an format string, the following values are allowed (need to be preceded by %).
1853 This can be combined to e.g.
1854 wanna-build --format='wanna-build -A %a --give-back %p_%v' -A mipsel --list=failed
1855
1856 a Architecture
1857 c section (e.g. libs or utils)
1858 D in case of BD-Uninstallable the reason for the uninstallability
1859 d distribution
1860 E in case of Dep-Wait the packages being waited on, in case of Needs-Build the number in the queue
1861 F in case of Failed the fail reason
1862 P previous state
1863 p Package name
1864 S Package state
1865 s Time in this state in full seconds since epoch
1866 t time of state change
1867 u Builder (e.g. buildd_mipsel-rem)
1868 v Package version
1869 V full Package version (i.e. with +b.., = %v%{+b}B%B
1870 X the string normally between [], e.g. optional:out-of-date:calprio{61}:days{25}
1871
1872 %{Text}?  print Text in case ? is not empty; ? is never printed
1873 %{!Text}? print Text in case ? is empty; ? is never printed
1874 Text could contain further %. To start with !, use %!
1875
1876 =cut
1877     return stringf($printfmt, (
1878         'p' => make_fmt( $pkg->{'package'}, $pkg, $var),
1879         'a' => make_fmt( $arch, $pkg, $var),
1880         's' => make_fmt( sub { return floor(str2time($pkg->{'state_change'})); }, $pkg, $var),
1881         'v' => make_fmt( $pkg->{'version'}, $pkg, $var),
1882         'V' => make_fmt( sub { $pkg->{'binary_nmu_version'} ? $pkg->{'version'}."+b".$pkg->{'binary_nmu_version'} : $pkg->{'version'} }, $pkg, $var),
1883         'S' => make_fmt( $pkg->{'state'}, $pkg, $var),
1884         'u' => make_fmt( $pkg->{'builder'}, $pkg, $var),
1885         'X' => make_fmt( sub {
1886             my $c = "$pkg->{'priority'}:$pkg->{'notes'}";
1887             $c .= ":PREV-FAILED" if $pkg->{'previous_state'} =~ /^Failed/;
1888             $c .= ":bp{" . $pkg->{'buildpri'} . "}" if defined $pkg->{'buildpri'};
1889             $c .= ":binNMU{" . $pkg->{'binary_nmu_version'} . "}" if defined $pkg->{'binary_nmu_version'};
1890             $c .= ":calprio{". $pkg->{'calprio'}."}";
1891             $c .= ":days{". $pkg->{'state_days'}."}";
1892             return $c;
1893             }, $pkg, $var),
1894         'c' => make_fmt( $pkg->{'section'}, $pkg, $var),
1895         'P' => make_fmt( $pkg->{'previous_state'} || "unknwon", $pkg, $var),
1896         'E' => make_fmt( sub { return $pkg->{'depends'} if $pkg->{'state'} eq "Dep-Wait";
1897             return $var->{scnt}{'Needs-Build'} + 1 if $pkg->{'state'} eq 'Needs-Build';
1898             return ""; }, $pkg, $var),
1899         'F' => make_fmt( sub { return "" unless $pkg->{'failed'};
1900             my $failed = $pkg->{'failed'};
1901             $failed =~ s/\\/\\\\/g;
1902             return $pkg->{'package'}."#".$arch."-failure\n ".
1903             join("\\0a",split("\n",$failed))."\\0a\n"; }, $pkg, $var),
1904         'D' => make_fmt( sub { return "" unless $pkg->{'bd_problem'};
1905             return $pkg->{'package'}."#".$arch."-bd-problem\n".
1906             join("\\0a",split("\n",$pkg->{'bd_problem'}))."\\0a\n"; }, $pkg, $var),
1907         'B' => make_fmt( sub { return $pkg->{'binary_nmu_version'} if defined $pkg->{'binary_nmu_version'}; }, $pkg, $var),
1908         'd' => make_fmt( $distribution, $pkg, $var),
1909         't' => make_fmt( $pkg->{'state_change'}, $pkg, $var),
1910     ));
1911 }
1912
1913 sub list_packages {
1914         my $state = shift;
1915         my( $name, $pkg, @list );
1916         my $cnt = 0;
1917         my %scnt;
1918         my $ctime = time;
1919
1920         my $db = get_all_source_info(state => $state, user => $user, category => $category, list_min_age => $list_min_age);
1921         foreach $name (keys %$db) {
1922                 next if $name =~ /^_/;
1923                 push @list, calculate_prio($db->{$name});
1924         }
1925
1926         # filter components
1927         @list = grep { my $i = $_->{'component'}; grep { $i eq $_ } split /[, ]+/, $yamlmap->{"restrict"}{'component'} } @list;
1928
1929         # first adjust ownprintformat, then set printformat accordingly
1930         $printformat ||= $yamlmap->{"format"}{$ownprintformat};
1931         $printformat ||= $yamlmap->{"format"}{"default"}{$state};
1932         $printformat ||= $yamlmap->{"format"}{"default"}{"default"};
1933         undef $printformat if ($ownprintformat eq 'none');
1934
1935         foreach $pkg (sort sort_list_func @list) {
1936                 if ($printformat) {
1937                     print print_format($printformat, $pkg, {'cnt' => $cnt, 'scnt' => \%scnt})."\n";
1938                     ++$cnt;
1939                     $scnt{$pkg->{'state'}}++;
1940                     next;
1941                 }
1942                 print print_format("%c/%p_%v", $pkg, {});
1943                 print print_format(": %S", $pkg, {})
1944                         if $state eq "all";
1945                 print print_format("%{ by }u%u", $pkg, {})
1946                         if $pkg->{'state'} ne "Needs-Build";
1947                 print print_format(" [%X]\n", $pkg, {});
1948                 print "  Reasons for failing:\n",
1949                           "    [Category: ",
1950                           defined $pkg->{'failed_category'} ? $pkg->{'failed_category'} : "none",
1951                           "]\n    ",
1952                           join("\n    ",split("\n",$pkg->{'failed'})), "\n"
1953                         if $pkg->{'state'} =~ /^Failed/;
1954                 print "  Dependencies: $pkg->{'depends'}\n"
1955                         if $pkg->{'state'} eq "Dep-Wait";
1956                 print "  Reasons for BD-Uninstallable:\n    ",
1957                           join("\n    ",split("\n",$pkg->{'bd_problem'})), "\n"
1958                         if $pkg->{'state'} eq "BD-Uninstallable";
1959                 print "  Previous state was $pkg->{'previous_state'}\n"
1960                         if $verbose && $pkg->{'previous_state'};
1961                 print "  No previous state recorded\n"
1962                         if $verbose && !$pkg->{'previous_state'};
1963                 print "  State changed at $pkg->{'state_change'}\n"
1964                         if $verbose && $pkg->{'state_change'};
1965                 print "  Previous state $pkg->{'previous_state'} left $pkg->{'state_time'} ago\n"
1966                         if $verbose && $pkg->{'previous_state'};
1967                 print "  Previous failing reasons:\n    ",
1968                       join("\n    ",split("\n",$pkg->{'old_failed'})), "\n"
1969                         if $verbose && $pkg->{'old_failed'};
1970                 ++$cnt;
1971                 $scnt{$pkg->{'state'}}++ if $state eq "all";
1972         }
1973         if ($state eq "all" && !$printformat) {
1974                 foreach (sort keys %scnt) {
1975                         print "Total $scnt{$_} package(s) in state $_.\n";
1976                 }
1977         }
1978         print "Total $cnt package(s)\n" unless $printformat;
1979         
1980 }
1981
1982 sub info_packages {
1983         my( $name, $pkg, $key, $dist );
1984         my @firstkeys = qw(package version builder state section priority
1985                                            installed_version previous_state state_change);
1986         my @dists = $info_all_dists ? keys %conf::distributions : ($distribution);
1987         my %beautykeys = ( 'package' => 'Package', 'version' => 'Version', 'builder' => 'Builder',
1988                 'state' => 'State', 'section' => 'Section', 'priority' => 'Priority',
1989                 'installed_version' => 'Installed-Version', 'previous_state' => 'Previous-State',
1990                 'state_change' => 'State-Change',
1991                 'bd_problem' => 'BD-Problem', 
1992                 'binary_nmu_changelog' => 'Binary-NMU-Changelog', 'binary_nmu_version' => 'Binary-NMU-Version',
1993                 'buildpri' => 'BuildPri', 'depends' => 'Depends', 'failed' => 'Failed',
1994                 'failed_category' => 'Failed-Category', 'notes' => 'Notes',
1995                 'distribution' => 'Distribution', 'old_failed' => 'Old-Failed',
1996                 'permbuildpri' => 'PermBuildPri', 'rel' => 'Rel',
1997                 'calprio' => 'CalculatedPri', 'state_days' => 'State-Days'
1998                          );
1999         
2000         foreach $name (@_) {
2001                 $name =~ s/_.*$//; # strip version
2002                 foreach $dist (@dists) {
2003                         my $pname = "$name" . ($info_all_dists ? "($dist)" : "");
2004                         
2005                         $pkg = get_readonly_source_info($name);
2006                         if (!defined( $pkg )) {
2007                                 print "$pname: not registered\n";
2008                                 next;
2009                         }
2010                         $pkg = calculate_prio($pkg);
2011
2012                         print "$pname:\n";
2013                         foreach $key (@firstkeys) {
2014                                 next if !defined $pkg->{$key};
2015                                 my $val = $pkg->{$key};
2016                                 chomp( $val );
2017                                 $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
2018                                 $val =~ s/\n/\n    /g;
2019                                 my $print_key = $key;
2020                                 $print_key = $beautykeys{$print_key} if $beautykeys{$print_key};
2021                                 printf "  %-20s: %s\n", $print_key, $val;
2022                         }
2023                         foreach $key (sort keys %$pkg) {
2024                                 next if isin( $key, @firstkeys );
2025                                 my $val = $pkg->{$key};
2026                                 next if !defined($val);
2027                                 chomp( $val );
2028                                 $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
2029                                 $val =~ s/\n/\n    /g;
2030                                 my $print_key = $key;
2031                                 $print_key = $beautykeys{$print_key} if $beautykeys{$print_key};
2032                                 printf "  %-20s: %s\n", $print_key, $val;
2033                         }
2034                 }
2035         }
2036 }
2037
2038 sub forget_packages {
2039         my( $name, $pkg, $key, $data );
2040         
2041         foreach $name (@_) {
2042                 $name =~ s/_.*$//; # strip version
2043                 $pkg = get_source_info($name);
2044                 if (!defined( $pkg )) {
2045                         print "$name: not registered\n";
2046                         next;
2047                 }
2048
2049                 $data = "";
2050                 foreach $key (sort keys %$pkg) {
2051                         my $val = $pkg->{$key};
2052                         chomp( $val );
2053                         $val =~ s/\n/\n /g;
2054                         $data .= sprintf "  %-20s: %s\n", $key, $val;
2055                 }
2056                 send_mail( $conf::db_maint,
2057                                    "$name deleted from DB " . table_name() . " " . $distribution,
2058                                    "The package '$name' has been deleted from the database ".
2059                                    "by $user.\n\n".
2060                                    "Data registered about the deleted package:\n".
2061                                    "$data\n" ) if $conf::db_maint;
2062                 change_state( \$pkg, 'deleted' );
2063                 log_ta( $pkg, "--forget" );
2064                 del_source_info($name);
2065                 print "$name: deleted from database\n" if $verbose;
2066         }
2067 }
2068
2069 sub forget_users {
2070         $dbh->do("DELETE from " . user_table_name() . 
2071                 " WHERE distribution = ?", undef, $distribution) or die $dbh->errstr;
2072 }
2073
2074 sub read_db {
2075         my $file = shift;
2076
2077         print "Reading ASCII database from $file..." if $verbose >= 1;
2078         open( F, "<$file" ) or
2079                 die "Can't open database $file: $!\n";
2080
2081         local($/) = ""; # read in paragraph mode
2082         while( <F> ) {
2083                 my( %thispkg, $name );
2084                 s/[\s\n]+$//;
2085                 s/\n[ \t]+/\376\377/g;  # fix continuation lines
2086                 s/\376\377\s*\376\377/\376\377/og;
2087   
2088                 while( /^(\S+):[ \t]*(.*)[ \t]*$/mg ) {
2089                         my ($key, $val) = ($1, $2);
2090                         $key =~ s/-/_/g;
2091                         $key =~ tr/A-Z/a-z/;
2092                         $val =~ s/\376\377/\n/g;
2093                         $thispkg{$key} = $val;
2094                 }
2095                 check_entry( \%thispkg );
2096                 # add to db
2097                 if (exists($thispkg{'package'})) {
2098                         update_source_info(\%thispkg);
2099                 }
2100                 elsif(exists($thispkg{'user'})) {
2101                         # user in import, username in database.
2102                         $dbh->do('INSERT INTO ' . user_table_name() .
2103                                         ' (username, distribution, last_seen)' .
2104                                         ' values (?, ?, ?)',
2105                                 undef, $thispkg{'user'}, $distribution,
2106                                 $thispkg{'last_seen'})
2107                                 or die $dbh->errstr;
2108                  }
2109         }
2110         close( F );
2111         print "done\n" if $verbose >= 1;
2112 }
2113
2114 sub check_entry {
2115         my $pkg = shift;
2116         my $field;
2117
2118         return if $op_mode eq "manual-edit"; # no checks then
2119         
2120         # check for required fields
2121         if (exists $pkg->{'user'}) {
2122                 return;
2123         }
2124         if (!exists $pkg->{'package'}) {
2125                 print STDERR "Bad entry: ",
2126                           join( "\n", map { "$_: $pkg->{$_}" } keys %$pkg ), "\n";
2127                 die "Database entry lacks package or username field\n";
2128         }
2129         # if no State: field, generate one (for old db compat)
2130         if (!exists($pkg->{'state'})) {
2131                 $pkg->{'state'} =
2132                         exists $pkg->{'failed'} ? 'Failed' : 'Building';
2133         }
2134         if (!exists $pkg->{'version'} and $pkg->{'state'} ne 'Not-For-Us') {
2135                 die "Database entry for $pkg->{'package'} lacks Version: field\n";
2136         }
2137         # check state field
2138         die "Bad state $pkg->{'state'} of package $pkg->{Package}\n"
2139                 if !isin( $pkg->{'state'},
2140                                   qw(Needs-Build Building Built Build-Attempted Uploaded Installed Dep-Wait Dep-Wait-Removed
2141                                          Failed Failed-Removed Not-For-Us BD-Uninstallable
2142                                          ) );
2143 }
2144
2145 sub export_db {
2146         my $file = shift;
2147         my($name,$pkg,$key);
2148
2149         print "Writing ASCII database to $file..." if $verbose >= 1;
2150         open( F, ">$file" ) or
2151                 die "Can't open export $file: $!\n";
2152
2153         my $db = get_all_source_info();
2154         foreach $name (keys %$db) {
2155                 next if $name =~ /^_/;
2156                 my $pkg = $db->{$name};
2157                 foreach $key (keys %{$pkg}) {
2158                         my $val = $pkg->{$key};
2159                         next if !defined($val);
2160                         $val =~ s/\n*$//;
2161                         $val =~ s/^/ /mg;
2162                         $val =~ s/^ +$/ ./mg;
2163                         print F "$key: $val\n";
2164                 }
2165                 print F "\n";
2166        }
2167        close( F );
2168        print "done\n" if $verbose >= 1;
2169 }
2170
2171 sub change_state {
2172         my $pkgr = shift;
2173         my $pkg = $$pkgr;
2174         my $newstate = shift;
2175         my $state = \$pkg->{'state'};
2176         
2177         return if defined($$state) and $$state eq $newstate;
2178         $pkg->{'previous_state'} = $$state if defined($$state);
2179         $pkg->{'state_change'} = $curr_date;
2180         $pkg->{'do_state_change'} = 1;
2181
2182         if (defined($$state) and $$state eq 'Failed') {
2183                 $pkg->{'old_failed'} =
2184                         "-"x20 . " $pkg->{'version'} " . "-"x20 . "\n" .
2185                         $pkg->{'failed'} . "\n" .
2186                         $pkg->{'old_failed'};
2187                 delete $pkg->{'failed'};
2188                 delete $pkg->{'failed_category'};
2189         }
2190         if (defined($$state) and $$state eq 'BD-Uninstallable') {
2191                 delete $pkg->{'bd_problem'};
2192         }
2193         $$state = $newstate;
2194 }
2195
2196 sub log_ta {
2197         my $pkg = shift;
2198         my $action = shift;
2199         my $dist = $distribution;
2200         my $str;
2201         my $prevstate;
2202
2203         $prevstate = $pkg->{'previous_state'};
2204         $str = "$action($dist): $pkg->{'package'}_$pkg->{'version'} ".
2205                    "changed from $prevstate to $pkg->{'state'} ".
2206                    "by $real_user as $user";
2207         
2208         $dbh->do('INSERT INTO ' . transactions_table_name() .
2209                         ' (package, distribution, version, action, ' .
2210                         ' prevstate, state, real_user, set_user, time) ' .
2211                         ' values (?, ?, ?, ?, ?, ?, ?, ?, ?)',
2212                 undef, $pkg->{'package'}, $distribution,
2213                 $pkg->{'version'}, $action, $prevstate, $pkg->{'state'},
2214                 $real_user, $user, 'now()') or die $dbh->errstr;
2215
2216         if (!($prevstate eq 'Failed' && $pkg->{'state'} eq 'Failed')) {
2217                 $str .= " (with --override)"
2218                         if $opt_override;
2219                 $mail_logs .= "$str\n";
2220         }
2221 }
2222
2223
2224 sub send_mail {
2225         my $to = shift;
2226         my $subject = shift;
2227         my $text = shift;
2228
2229         my $from = $conf::db_maint;
2230         my $domain = $conf::buildd_domain;
2231
2232         $from .= "\@$domain" if $from !~ /\@/;
2233
2234         $to .= '@' . $domain if $to !~ /\@/;
2235         $text =~ s/^\.$/../mg;
2236         local $SIG{'PIPE'} = 'IGNORE';
2237         open( PIPE,  "| $conf::mailprog -oem $to" )
2238                 or die "Can't open pipe to $conf::mailprog: $!\n";
2239         chomp $text;
2240         print PIPE "From: $from\n";
2241         print PIPE "Subject: $subject\n\n";
2242         print PIPE "$text\n";
2243         close( PIPE );
2244 }
2245
2246 # for parsing input to dep-wait
2247 sub parse_deplist {
2248     my $deps = shift;
2249     my $verify = shift;
2250     my %result;
2251     
2252     foreach (split( /\s*,\s*/, $deps )) {
2253         if ($verify) {
2254             # verification requires > starting prompts, no | crap
2255             if (!/^(\S+)\s*(\(\s*(>(?:[>=])?)\s*(\S+)\s*\))?\s*$/) {
2256                 return 0;
2257             }
2258             next;
2259         }
2260         my @alts = split( /\s*\|\s*/, $_ );
2261         # Anything with an | is ignored, as it can be configured on a
2262         # per-buildd basis what will be installed
2263         next if $#alts != 0;
2264         $_ = shift @alts;
2265
2266         if (!/^(\S+)\s*(\(\s*(>=|=|==|>|>>|<<|<=)\s*(\S+)\s*\))?\s*$/) {
2267             warn( "parse_deplist: bad dependency $_\n" );
2268             next;
2269         }
2270         my($dep, $rel, $relv) = ($1, $3, $4);
2271         $rel = ">>" if defined($rel) and $rel eq ">";
2272         $result{$dep}->{'package'} = $dep;
2273         if ($rel && $relv) {
2274             $result{$dep}->{'rel'} = $rel;
2275             $result{$dep}->{'version'} = $relv;
2276         }
2277     }
2278     return 1 if $verify;
2279     return \%result;
2280 }
2281
2282 sub build_deplist {
2283         my $list = shift;
2284         my($key, $result);
2285         
2286         foreach $key (keys %$list) {
2287                 $result .= ", " if $result;
2288                 $result .= $key;
2289                 $result .= " ($list->{$key}->{'rel'} $list->{$key}->{'version'})"
2290                         if $list->{$key}->{'rel'} && $list->{$key}->{'version'};
2291         }
2292         return $result;
2293 }
2294
2295 sub call_edos_depcheck {
2296     my $packagesfile = shift;
2297     my $srcs = shift;
2298     my $key;
2299     
2300     return if defined ($conf::distributions{$distribution}{noadw});
2301
2302     # We need to check all of needs-build, as any new upload could make
2303     # something in needs-build have uninstallable deps
2304     # We also check everything in bd-uninstallable, as any new upload could
2305     # make that work again
2306     my %interesting_packages;
2307     my $db = get_all_source_info();
2308     foreach $key (keys %$db) {
2309         my $pkg = $db->{$key};
2310         if (defined $pkg and isin($pkg->{'state'}, qw/Needs-Build BD-Uninstallable/)) {
2311                 $interesting_packages{$key} = undef;
2312         }
2313     }
2314     
2315     #print "I would look at these sources with edos-depcheck:\n";
2316     #print join " ", keys %interesting_packages,"\n";
2317
2318     my $tmpfile_pattern = "/tmp/wanna-build-interesting-sources-$distribution.$$-";
2319     my ($tmpfile, $i);
2320     for( $i = 0;; ++$i ) {
2321             $tmpfile = $tmpfile_pattern . $i;
2322             last if ! -e $tmpfile;
2323     }
2324
2325     open SOURCES, '>', $tmpfile or die "Could not open temporary file $tmpfile\n";
2326     for my $key (keys %interesting_packages) {
2327         my $pkg = $db->{$key};
2328         print SOURCES "Package: $key\n";
2329         print SOURCES "Version: $pkg->{'version'}\n";
2330         print SOURCES "Build-Depends: $srcs->{$key}{'dep'}\n" if $srcs->{$key}{'dep'};
2331         print SOURCES "Build-Conflicts: $srcs->{$key}{'conf'}\n" if $srcs->{$key}{'conf'};
2332         print SOURCES "Architecture: all\n";
2333         print SOURCES "\n";
2334     }
2335     close SOURCES;
2336
2337     if (open(EDOS,"-|","wb-edos-builddebcheck", "-a", $arch, $packagesfile, $tmpfile))
2338     {
2339         local($/) = ""; # read in paragraph mode
2340         while( <EDOS> ) {
2341                 my( $key, $reason ) ;
2342                 s/\s*$//m;
2343                 /^Package:\s*(\S+)$/mi and $key = $1;
2344                 /^Failed-Why:(([^\n]|\n ([^\n]|\.))*)$/msi and $reason = $1;
2345                 $reason =~ s/^\s*//mg;
2346                 $reason ||= 'No reason given by edos-debcheck';
2347
2348                 if (exists $interesting_packages{$key}) {
2349                     $interesting_packages{$key} = $reason;
2350                 } else {
2351                     #print "TODO: edos reported a package we do not care about now\n" if $verbose;
2352                 }
2353         }
2354         close EDOS;
2355     } else {
2356         print "ERROR: Could not run wb-edos-builddebcheck. I am continuing, assuming\n" .
2357               "all packages have installable build-dependencies."
2358     }
2359     
2360     unlink( $tmpfile );
2361
2362     for my $key (keys %interesting_packages) {
2363         my $pkg = $db->{$key};
2364         my $change = 
2365             (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') ||
2366             (not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable');
2367         my $problemchange = $interesting_packages{$key} ne $pkg->{'bd_problem'};
2368         if ($change) {
2369             if (defined $interesting_packages{$key}) {
2370                     change_state( \$pkg, 'BD-Uninstallable' );
2371                     $pkg->{'bd_problem'} = $interesting_packages{$key};
2372             } else {
2373                     change_state( \$pkg, 'Needs-Build' );
2374             }
2375         }
2376         if ($problemchange) {
2377             if (defined $interesting_packages{$key}) {
2378                     $pkg->{'bd_problem'} = $interesting_packages{$key};
2379             }   
2380         }
2381         if ($change) {
2382             log_ta( $pkg, "--merge-all" );
2383             print "edos-builddebchange changed state of ${key}_$pkg->{'version'} to $pkg->{'state'}\n" if $verbose;
2384         }
2385         if ($change || $problemchange) {
2386             update_source_info($pkg);
2387         }
2388     }
2389 }
2390
2391 sub usage {
2392         my $prgname;
2393         ($prgname = $0) =~ s,^.*/,,;
2394         print <<"EOF";
2395 Usage: $prgname <options...> <package_version...>
2396 Options:
2397     -v, --verbose: Verbose execution.
2398     -A arch: Architecture this operation is for.
2399     --take: Take package for building [default operation]
2400     -f, --failed: Record in database that a build failed due to
2401         deficiencies in the package (that aren't fixable without a new
2402         source version).
2403     -u, --uploaded: Record in the database that the packages build
2404         correctly and were uploaded.
2405     -n, --no-build: Record in the database that the packages aren't
2406         desired for this architecture and shouldn't appear in listings even
2407         if they're out of date.
2408     --dep-wait: Record in the database that the packages are waiting
2409         for some source dependencies to become available
2410     --binNMU num: Schedule a re-build of the package with unchanged source, but
2411          a new version number (source-version + "+b<num>")
2412     --give-back: Mark a package as ready to build that is in state Building,
2413          Built or Build-Attempted. To give back a package in state Failed, use
2414          --override. This command will actually put the package in state
2415          BD-Uninstallable, until the installability of its Build-Dependencies
2416          were verified. This happens at each call of --merge-all, usually
2417          every 15 minutes.
2418     --merge-quinn: Merge quinn-diff output into database.
2419     --merge-packages: Merge Packages files into database.
2420     --pretend-avail: Pretend that given packages are available now and give
2421         free packages waiting for them
2422     -i SRC_PKG, --info SRC_PKG: Show information for source package
2423     -l STATE, --list=STATE: List all packages in state STATE; can be
2424         combined with -U to restrict to a specific user; STATE can
2425         also be 'all'
2426     -m MESSAGE, --message=MESSAGE: Give reason why package failed or
2427         source dependency list
2428         (used with -f, --dep-wait, and --binNMU)
2429     -o, --override: Override another user's lock on a package, i.e.
2430         take it over; a notice mail will be sent to the other user
2431     -U USER, --user=USER: select user name for which listings should
2432         apply, if not given all users are listed.
2433         if -l is missing, set user name to be entered in db; usually
2434         automatically choosen
2435     --import FILE: Import database from a ASCII file FILE
2436     --export FILE: Export database to a ASCII file FILE
2437
2438 The remaining arguments (depending on operation) usually start with
2439 "name_version", the trailer is ignored. This allows to pass the names
2440 of .dsc files, for which file name completion can be used.
2441 --merge-packages and --merge-quinn take Package/quin--diff file names
2442 on the command line or read stdin. --list needs nothing more on the
2443 command line. --info takes source package names (without version).
2444 EOF
2445         exit 1;
2446 }
2447
2448 sub pkg_version_eq {
2449         my $pkg = shift;
2450         my $version = shift;
2451
2452         return 1
2453                if (defined $pkg->{'binary_nmu_version'}) and 
2454                version_compare(binNMU_version($pkg->{'version'},
2455                         $pkg->{'binary_nmu_version'}),'=', $version);
2456         return version_compare( $pkg->{'version'}, "=", $version );
2457 }
2458
2459 sub table_name {
2460         return '"' . $arch . $schema_suffix . '".packages';
2461 }
2462
2463 sub user_table_name {
2464         return '"' . $arch . $schema_suffix . '".users';
2465 }
2466
2467 sub transactions_table_name {
2468         return '"' . $arch . $schema_suffix . '".transactions';
2469 }
2470
2471 sub get_readonly_source_info {
2472         my $name = shift;
2473         # SELECT FLOOR(EXTRACT('epoch' FROM age(localtimestamp, '2010-01-22  23:45')) / 86400) -- change to that?
2474         my $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change)) as state_days FROM ' . 
2475                 table_name() . ' WHERE package = ? AND distribution = ?',
2476                 undef, $name, $distribution);
2477         return $pkg;
2478 }
2479
2480 sub get_source_info {
2481         my $name = shift;
2482         my $pkg = $dbh->selectrow_hashref('SELECT *, extract(days from date_trunc(\'days\', now() - state_change)) as state_days FROM ' . 
2483                 table_name() . ' WHERE package = ? AND distribution = ?' .
2484                 ' FOR UPDATE',
2485                 undef, $name, $distribution);
2486         return $pkg;
2487 }
2488
2489 sub get_all_source_info {
2490         my %options = @_;
2491
2492         my $q = 'SELECT *, '.
2493                 'extract(days from date_trunc(\'days\', now() - state_change)) as state_days, '.
2494                 'date_trunc(\'seconds\', now() - state_change) as state_time'.
2495                 ' FROM ' . table_name()
2496                 . ' WHERE distribution = ? ';
2497         my @args = ($distribution);
2498         if ($options{state} && uc($options{state}) ne "ALL") {
2499                 $q .= ' AND upper(state) = ? ';
2500                 push @args, uc($options{state});
2501         }
2502
2503         if ($options{user}) {
2504                 #this basically means "this user, or no user at all":
2505                 $q .= ' AND (builder = ? OR upper(state) = ?)';
2506                 push @args, $options{user};
2507                 push @args, "NEEDS-BUILD";
2508         }
2509
2510         if ($options{category}) {
2511                 $q .= ' AND failed_category <> ? AND upper(state) = ? ';
2512                 push @args, $options{category};
2513                 push @args, "FAILED";
2514         }
2515
2516         if ($options{list_min_age} > 0) {
2517                 $q .= ' AND age(state_change) > ? ';
2518                 push @args, $options{list_min_age} . " days";
2519         }
2520
2521         if ($options{list_min_age} < 0) {
2522                 $q .= ' AND age(state_change) < ? ';
2523                 push @args, -$options{list_min_age} . " days";
2524         }
2525
2526         my $db = $dbh->selectall_hashref($q, 'package', undef, @args);
2527         return $db;
2528 }
2529
2530 sub update_source_info {
2531         my $pkg = shift;
2532
2533         my $pkg2 = get_source_info($pkg->{'package'});
2534         if (! defined $pkg2)
2535         {
2536                 add_source_info($pkg);
2537         }
2538
2539         $dbh->do('UPDATE ' . table_name() . ' SET ' .
2540                         'version = ?, ' .
2541                         'state = ?, ' .
2542                         'section = ?, ' .
2543                         'priority = ?, ' .
2544                         'installed_version = ?, ' .
2545                         'previous_state = ?, ' .
2546                         (($pkg->{'do_state_change'}) ? "state_change = now()," : "").
2547                         'notes = ?, ' .
2548                         'builder = ?, ' .
2549                         'failed = ?, ' .
2550                         'old_failed = ?, ' .
2551                         'binary_nmu_version = ?, ' .
2552                         'binary_nmu_changelog = ?, ' .
2553                         'failed_category = ?, ' .
2554                         'permbuildpri = ?, ' .
2555                         'buildpri = ?, ' .
2556                         'depends = ?, ' .
2557                         'rel = ?, ' .
2558                         'bd_problem = ? ' .
2559                         'WHERE package = ? AND distribution = ?',
2560                 undef,
2561                 $pkg->{'version'},
2562                 $pkg->{'state'},
2563                 $pkg->{'section'},
2564                 $pkg->{'priority'},
2565                 $pkg->{'installed_version'},
2566                 $pkg->{'previous_state'},
2567                 $pkg->{'notes'},
2568                 $pkg->{'builder'},
2569                 $pkg->{'failed'},
2570                 $pkg->{'old_failed'},
2571                 $pkg->{'binary_nmu_version'},
2572                 $pkg->{'binary_nmu_changelog'},
2573                 $pkg->{'failed_category'},
2574                 $pkg->{'permbuildpri'},
2575                 $pkg->{'buildpri'},
2576                 $pkg->{'depends'},
2577                 $pkg->{'rel'},
2578                 $pkg->{'bd_problem'},
2579                 $pkg->{'package'},
2580                 $distribution) or die $dbh->errstr;
2581 }
2582
2583 sub add_source_info {
2584         my $pkg = shift;
2585         $dbh->do('INSERT INTO ' . table_name() .
2586                         ' (package, distribution) values (?, ?)',
2587                 undef, $pkg->{'package'}, $distribution) or die $dbh->errstr;
2588 }
2589
2590 sub del_source_info {
2591         my $name = shift;
2592         $dbh->do('DELETE FROM ' . table_name() .
2593                         ' WHERE package = ? AND distribution = ?',
2594                 undef, $name, $distribution) or die $dbh->errstr;
2595 }
2596
2597 sub get_user_info {
2598         my $name = shift;
2599         my $user = $dbh->selectrow_hashref('SELECT * FROM ' . 
2600                 user_table_name() . ' WHERE username = ? AND distribution = ?',
2601                 undef, $name, $distribution);
2602         return $user;
2603 }
2604
2605 sub update_user_info {
2606         my $user = shift;
2607         $dbh->do('UPDATE ' . user_table_name() .
2608                         ' SET last_seen = now() WHERE username = ?' .
2609                         ' AND distribution = ?',
2610                 undef, $user, $distribution)
2611                 or die $dbh->errstr;
2612 }
2613
2614
2615 sub add_user_info {
2616         my $user = shift;
2617         $dbh->do('INSERT INTO ' . user_table_name() .
2618                         ' (username, distribution, last_seen)' .
2619                         ' values (?, ?, now())',
2620                 undef, $user, $distribution)
2621                 or die $dbh->errstr;
2622 }
2623
2624 sub lock_table()
2625 {
2626         $dbh->do('LOCK TABLE ' . table_name() .
2627                 ' IN EXCLUSIVE MODE', undef) or die $dbh->errstr;
2628 }
2629