Take that more as a "proof of concept", hasn't gone thru nearly enough testing
yet.

This does rewrite pkg_delete to look a lot more like pkg_add works, and this
is the reason why I've rejected small tweaks to pkg_delete in the recent past,
since I knew I had to do this one day.

In particular, this should deal with nasty interdependencies (yes, you can
end up with that in some nasty corners of the tree with pkg_add -r).

It should also be easily tweaked to be able to remove all !manual-installs
packages that are no longer needed.

And also, it deletes what it can, and tells you automatically what it cannot
delete.

If things don't work, well tough... like I said, it's not been tested a lot
yet.

diff --git a/OpenBSD/PkgDelete.pm b/OpenBSD/PkgDelete.pm
index 66f3cd1..e7b9926 100644
--- a/OpenBSD/PkgDelete.pm
+++ b/OpenBSD/PkgDelete.pm
@@ -50,6 +50,42 @@ sub todo
        return sprintf("%u/%u", $state->{done} - $offset, $state->{total});
 }
 
+sub track
+{
+       my ($state, $list) = @_;
+       for my $set (@$list) {
+               for my $pkgname ($set->older_names) {
+                       $state->{todo}->{$pkgname} = $set;
+               }
+       }
+}
+
+sub find
+{
+       my ($state, $pkgname) = @_;
+       return $state->{todo}->{$pkgname};
+}
+
+sub finished
+{
+       my ($state, $set) = @_;
+       $set->{finished} = 1;
+       for my $pkgname ($set->older_names) {
+               delete $state->{todo}->{$pkgname};
+       }
+}
+
+sub stem2location
+{
+       my ($self, $locator, $name, $state) = @_;
+       require OpenBSD::Search;
+       my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name));
+       if (@$l > 1 && !$state->defines('allversions')) {
+               $l = 
OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l);
+       }
+       return $state->choose_location($name, $l);
+}
+
 package OpenBSD::PkgDelete;
 our @ISA = qw(OpenBSD::AddDelete);
 
@@ -58,63 +94,107 @@ use OpenBSD::RequiredBy;
 use OpenBSD::Delete;
 use OpenBSD::PackageInfo;
 use OpenBSD::UpdateSet;
+use OpenBSD::Handle;
 
 
 sub process_parameters
 {
        my ($self, $state) = @_;
 
-       my @realnames;
-       my @toremove;
-
-       OpenBSD::PackageInfo::solve_installed_names(\@ARGV, \@realnames,
-           "(removing them all)", $state);
-
-       @toremove = OpenBSD::RequiredBy->compute_closure(@realnames);
-
-       if (@toremove > @realnames) {
-               my $details = $state->verbose >= 2 ||
-                   $state->defines('verbosedeps');
-               my $show = sub {
-                       my ($p, $d) = @_;
-                       $state->say("Can't remove #1".
-                           " without also removing:\n#2",
-                           join(' ', @$p), join(' ', @$d));
-               };
-               if ($state->{interactive} || !$details) {
-                       my %deps = map {($_, 1)} @toremove;
-                       for my $p (@realnames) {
-                               delete $deps{$p};
-                       }
-                       &$show([@realnames], [keys %deps]);
-                       if (@realnames > 1 && (keys %deps) > 1 &&
-                           $state->confirm("Do you want details", 1)) {
-                               $details = 1;
-                       }
-               }
-               if ($details) {
-                       for my $pkg (@realnames) {
-                               my @deps = 
OpenBSD::RequiredBy->compute_closure($pkg);
-                               next unless @deps > 1;
-                               @deps = grep {$_ ne $pkg} @deps;
-                               &$show([$pkg], [@deps]);
-                       }
+       if (@ARGV == 0) {
+               @ARGV = sort(installed_packages());
+       }
+       my $inst = $state->repo->installed;
+       for my $pkgname (@ARGV) {
+               my $l;
+
+               if (OpenBSD::PackageName::is_stem($pkgname)) {
+                       $l = $state->stem2location($inst, $pkgname, $state);
+               } else {
+                       $l = $inst->find($pkgname, $state->{arch});
                }
-               my $them = @toremove > 1 ? 'them' : 'it';
-               if ($state->defines('dependencies') or
-                   $state->confirm("Do you want to remove $them as well", 0)) {
-                       $state->say("(removing #1 as well)", $them);
+               if (!defined $l) {
+                       $state->say("Problem finding #1", $pkgname);
                } else {
-                       $state->{bad}++;
+                       push(@{$state->{setlist}},
+                           
$state->updateset->add_older(OpenBSD::Handle->from_location($l)));
                }
        }
-       $state->{toremove} = \@toremove;
 }
 
 sub finish_display
 {
 }
 
+sub really_remove
+{
+       my ($set, $state) = @_;
+       for my $pkgname ($set->older_names) {
+               $state->status->object($pkgname);
+               if (!$state->progress->set_header($pkgname)) {
+                       $state->say($state->{not} ?
+                           "Pretending to delete #1" :
+                           "Deleting #1",
+                           $pkgname) if $state->verbose;
+               }
+               $state->log->set_context('-'.$pkgname);
+               OpenBSD::Delete::delete_package($pkgname, $state);
+       }
+       $set->{finished} = 1;
+}
+
+sub remove_set
+{
+       my ($set, $state) = @_;
+
+       my $todo = {};
+       my $bad = {};
+       $set = $set->real_set;
+       if ($set->{finished}) {
+               return ();
+       }
+       for my $pkgname ($set->older_names) {
+               unless (is_installed($pkgname)) {
+                       $state->errsay("#1 was not installed", $pkgname);
+                       $set->{finished} = 1;
+                       return ();
+               }
+               my $r = OpenBSD::RequiredBy->new($pkgname);
+               for my $pkg ($r->list) {
+                       next if $set->{older}->{$pkg};
+                       my $f = $state->find($pkg);
+                       if (defined $f) {
+                               $todo->{$pkg} = $f;
+                       } else {
+                               $bad->{$pkg} = 1;
+                       }
+               }
+       }
+       if (keys %$bad > 0) {
+               $state->errsay("can't delete #1 without deleting #2",
+                   join(' ', $set->older_names), join(' ', sort keys %$bad));
+               $set->{finished} = 1;
+               return ();
+       }
+       # XXX this si where we should detect loops
+       if (keys %$todo > 0) {
+               if ($set->{once}) {
+                       for my $set2 (values %$todo) {
+                               # XXX merge all ?
+                               $set->add_older($set2->older);
+                               $set2->{merged} = $set;
+                               $set2->{finished} = 1;
+                       }
+                       delete $set->{once};
+                       return ($set);
+               }
+               $set->{once} = 1;
+               return (values %$todo, $set);
+       }
+       really_remove($set, $state);
+       return ();
+}
+
 sub main
 {
        my ($self, $state) = @_;
@@ -122,55 +202,13 @@ sub main
        my %done;
        my $removed;
 
+       $state->{total} = scalar @{$state->{setlist}};
+       $state->track($state->{setlist});
        # and finally, handle the removal
-       do {
-               $removed = 0;
-               if ($state->{not}) {
-                       $state->status->what("Pretending to delete");
-               } else {
-                       $state->status->what("Deleting");
-               }
-               $state->{total} = scalar @{$state->{toremove}};
-               DELETE: for my $pkgname (@{$state->{toremove}}) {
-                       if ($done{$pkgname}) {
-                               next;
-                       }
-                       unless (is_installed($pkgname)) {
-                               $state->errsay("#1 was not installed", 
$pkgname);
-                               $done{$pkgname} = 1;
-                               $removed++;
-                               next;
-                       }
-                       my $r = OpenBSD::RequiredBy->new($pkgname);
-                       if ($r->list > 0) {
-                               if ($state->defines('baddepend')) {
-                                       for my $p ($r->list) {
-                                               if ($done{$p}) {
-                                                       $r->delete($p);
-                                               } else {
-                                                       next DELETE;
-                                               }
-                                       }
-                               } else {
-                                       next;
-                               }
-                       }
-                       my $info = sub {
-                       };
-
-                       $state->status->object($pkgname);
-                       if (!$state->progress->set_header($pkgname)) {
-                               $state->say($state->{not} ?
-                                   "Pretending to delete #1" :
-                                   "Deleting #1",
-                                   $pkgname) if $state->verbose;
-                       }
-                       $state->log->set_context('-'.$pkgname);
-                       OpenBSD::Delete::delete_package($pkgname, $state);
-                       $done{$pkgname} = 1;
-                       $removed++;
-               }
-       } while ($removed);
+       while (my $set = shift @{$state->{setlist}}) {
+               $state->status->what->set($set);
+               unshift(@{$state->{setlist}}, remove_set($set, $state));
+       }
 }
 
 sub new_state

Reply via email to