1a409537dSespie#!/usr/bin/perl 2a409537dSespie# ex:ts=8 sw=4: 3*303a35c3Sespie# $OpenBSD: PkgDelete.pm,v 1.15 2011/07/13 12:32:15 espie Exp $ 4a409537dSespie# 5a409537dSespie# Copyright (c) 2003-2010 Marc Espie <espie@openbsd.org> 6a409537dSespie# 7a409537dSespie# Permission to use, copy, modify, and distribute this software for any 8a409537dSespie# purpose with or without fee is hereby granted, provided that the above 9a409537dSespie# copyright notice and this permission notice appear in all copies. 10a409537dSespie# 11a409537dSespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12a409537dSespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13a409537dSespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14a409537dSespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15a409537dSespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16a409537dSespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17a409537dSespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18a409537dSespie 19a409537dSespieuse strict; 20a409537dSespieuse warnings; 21a409537dSespie 22a409537dSespieuse OpenBSD::AddDelete; 23a409537dSespie 24681090d4Sespiepackage OpenBSD::PkgDelete::Tracker; 25681090d4Sespie 26681090d4Sespiesub new 27681090d4Sespie{ 28681090d4Sespie my $class = shift; 29681090d4Sespie bless {}, $class; 30681090d4Sespie} 31681090d4Sespie 32681090d4Sespiesub sets_todo 33681090d4Sespie{ 34681090d4Sespie my ($self, $offset) = @_; 35681090d4Sespie return sprintf("%u/%u", (scalar keys %{$self->{done}})-$offset, 36681090d4Sespie scalar keys %{$self->{total}}); 37681090d4Sespie} 38681090d4Sespie 39681090d4Sespiesub handle_set 40681090d4Sespie{ 41681090d4Sespie my ($self, $set) = @_; 42681090d4Sespie $self->{total}{$set} = 1; 43681090d4Sespie if ($set->{finished}) { 44681090d4Sespie $self->{done}{$set} = 1; 45681090d4Sespie } 46681090d4Sespie} 47681090d4Sespie 48681090d4Sespiesub todo 49681090d4Sespie{ 50681090d4Sespie my ($self, $list) = @_; 51681090d4Sespie for my $set (@$list) { 52681090d4Sespie for my $pkgname ($set->older_names) { 53681090d4Sespie $self->{todo}{$pkgname} = $set; 54681090d4Sespie } 55681090d4Sespie $self->handle_set($set); 56681090d4Sespie } 57681090d4Sespie} 58681090d4Sespie 59681090d4Sespie 60681090d4Sespiesub done 61681090d4Sespie{ 62681090d4Sespie my ($self, $set) = @_; 63681090d4Sespie $set->{finished} = 1; 64681090d4Sespie for my $pkgname ($set->older_names) { 65681090d4Sespie delete $self->{todo}{$pkgname}; 66681090d4Sespie } 67681090d4Sespie $self->handle_set($set); 68681090d4Sespie} 69681090d4Sespie 70681090d4Sespiesub find 71681090d4Sespie{ 72681090d4Sespie my ($self, $pkgname) = @_; 73681090d4Sespie return $self->{todo}{$pkgname}; 74681090d4Sespie} 75681090d4Sespie 76681090d4Sespie 77681090d4Sespie 78a409537dSespiepackage OpenBSD::PkgDelete::State; 79a409537dSespieour @ISA = qw(OpenBSD::AddDelete::State); 80a409537dSespie 81681090d4Sespiesub new 82681090d4Sespie{ 83681090d4Sespie my $class = shift; 84681090d4Sespie my $self = $class->SUPER::new(@_); 85681090d4Sespie $self->{tracker} = OpenBSD::PkgDelete::Tracker->new; 86681090d4Sespie return $self; 87681090d4Sespie} 88681090d4Sespie 89681090d4Sespiesub tracker 90681090d4Sespie{ 91681090d4Sespie my $self = shift; 92681090d4Sespie return $self->{tracker}; 93681090d4Sespie} 94681090d4Sespie 958a660df1Sespiesub handle_options 968a660df1Sespie{ 978a660df1Sespie my $state = shift; 988a660df1Sespie $state->SUPER::handle_options('', 99681090d4Sespie '[-acIinqsvx] [-B pkg-destdir] [-D name[=value]] pkg-name [...]'); 1008a660df1Sespie 1018a660df1Sespie my $base = $state->opt('B') // $ENV{'PKG_DESTDIR'} // ''; 1028a660df1Sespie if ($base ne '') { 1038a660df1Sespie $base.='/' unless $base =~ m/\/$/o; 1048a660df1Sespie } 1058a660df1Sespie $ENV{'PKG_DESTDIR'} = $base; 1068a660df1Sespie 1078a660df1Sespie $state->{destdir} = $base; 1088a660df1Sespie if ($base eq '') { 1098a660df1Sespie $state->{destdirname} = ''; 1108a660df1Sespie } else { 1118a660df1Sespie $state->{destdirname} = '${PKG_DESTDIR}'; 1128a660df1Sespie } 1138a660df1Sespie} 1148a660df1Sespie 115a409537dSespiesub todo 116a409537dSespie{ 11700f714c9Sespie my ($state, $offset) = @_; 118681090d4Sespie return $state->tracker->sets_todo($offset); 119681090d4Sespie} 120681090d4Sespie 121681090d4Sespiesub stem2location 122681090d4Sespie{ 123681090d4Sespie my ($self, $locator, $name, $state) = @_; 124681090d4Sespie require OpenBSD::Search; 125681090d4Sespie my $l = $locator->match_locations(OpenBSD::Search::Stem->new($name)); 126681090d4Sespie if (@$l > 1 && !$state->defines('allversions')) { 127681090d4Sespie $l = OpenBSD::Search::FilterLocation->keep_most_recent->filter_locations($l); 128681090d4Sespie } 129681090d4Sespie return $state->choose_location($name, $l); 130a409537dSespie} 131a409537dSespie 132*303a35c3Sespiesub deleteset 133*303a35c3Sespie{ 134*303a35c3Sespie my $self = shift; 135*303a35c3Sespie require OpenBSD::UpdateSet; 136*303a35c3Sespie 137*303a35c3Sespie return OpenBSD::DeleteSet->new($self); 138*303a35c3Sespie} 139*303a35c3Sespie 140*303a35c3Sespiesub deleteset_from_location 141*303a35c3Sespie{ 142*303a35c3Sespie my ($self, $location) = @_; 143*303a35c3Sespie return $self->deleteset->add_older(OpenBSD::Handle->from_location($location)); 144*303a35c3Sespie} 145*303a35c3Sespie 146a409537dSespiepackage OpenBSD::PkgDelete; 147a409537dSespieour @ISA = qw(OpenBSD::AddDelete); 148a409537dSespie 149a409537dSespieuse OpenBSD::PackingList; 150a409537dSespieuse OpenBSD::RequiredBy; 151a409537dSespieuse OpenBSD::Delete; 152a409537dSespieuse OpenBSD::PackageInfo; 153a409537dSespieuse OpenBSD::UpdateSet; 154681090d4Sespieuse OpenBSD::Handle; 155a409537dSespie 156a409537dSespie 157681090d4Sespiesub add_location 158681090d4Sespie{ 159681090d4Sespie my ($self, $state, $l) = @_; 160681090d4Sespie push(@{$state->{setlist}}, 161*303a35c3Sespie $state->deleteset_from_location($l)); 162681090d4Sespie} 163681090d4Sespie 164681090d4Sespiesub create_locations 165681090d4Sespie{ 166681090d4Sespie my ($state, @l) = @_; 167681090d4Sespie my $inst = $state->repo->installed; 168681090d4Sespie my $result = []; 169681090d4Sespie for my $name (@l) { 170681090d4Sespie my $l = $inst->find($name, $state->{arch}); 171681090d4Sespie if (!defined $l) { 172681090d4Sespie $state->errsay("Can't find #1 in installed packages", 173681090d4Sespie $name); 174681090d4Sespie $state->{bad}++; 175681090d4Sespie } else { 176*303a35c3Sespie push(@$result, $state->deleteset_from_location($l)); 177681090d4Sespie } 178681090d4Sespie } 179681090d4Sespie return $result; 180681090d4Sespie} 181681090d4Sespie 182a409537dSespiesub process_parameters 183a409537dSespie{ 184a409537dSespie my ($self, $state) = @_; 1857096cf21Sespie 186681090d4Sespie my $inst = $state->repo->installed; 1877096cf21Sespie 188681090d4Sespie if (@ARGV == 0 && $state->{automatic}) { 189681090d4Sespie for my $l (@{$inst->locations_list}) { 190681090d4Sespie $self->add_location($state, $l); 191a409537dSespie } 192a409537dSespie } else { 193681090d4Sespie for my $pkgname (@ARGV) { 194681090d4Sespie my $l; 195681090d4Sespie 196681090d4Sespie if (OpenBSD::PackageName::is_stem($pkgname)) { 197681090d4Sespie $l = $state->stem2location($inst, $pkgname, $state); 198681090d4Sespie } else { 199681090d4Sespie $l = $inst->find($pkgname, $state->{arch}); 200681090d4Sespie } 201681090d4Sespie if (!defined $l) { 202681090d4Sespie $state->say("Problem finding #1", $pkgname); 203a409537dSespie $state->{bad}++; 204681090d4Sespie } else { 205681090d4Sespie $self->add_location($state, $l); 206a409537dSespie } 207a409537dSespie } 208681090d4Sespie } 209a409537dSespie} 210a409537dSespie 211a409537dSespiesub finish_display 212a409537dSespie{ 213a409537dSespie} 214a409537dSespie 215681090d4Sespiesub really_remove 216681090d4Sespie{ 217681090d4Sespie my ($set, $state) = @_; 218681090d4Sespie if ($state->{not}) { 219681090d4Sespie $state->status->what("Pretending to delete"); 220681090d4Sespie } else { 221681090d4Sespie $state->status->what("Deleting"); 222681090d4Sespie } 223*303a35c3Sespie if (!$state->progress->set_header($set->print)) { 224681090d4Sespie $state->say($state->{not} ? 225681090d4Sespie "Pretending to delete #1" : 226681090d4Sespie "Deleting #1", 227*303a35c3Sespie $set->print) if $state->verbose; 228681090d4Sespie } 229681090d4Sespie for my $pkgname ($set->older_names) { 230681090d4Sespie $state->log->set_context('-'.$pkgname); 231681090d4Sespie OpenBSD::Delete::delete_package($pkgname, $state); 232681090d4Sespie } 233681090d4Sespie $set->cleanup; 234681090d4Sespie $state->tracker->done($set); 235681090d4Sespie $state->progress->next($state->ntogo); 236681090d4Sespie} 237681090d4Sespie 238f1ddee08Sespiesub delete_dependencies 239f1ddee08Sespie{ 240f1ddee08Sespie my $state = shift; 241f1ddee08Sespie 242f1ddee08Sespie if ($state->defines("dependencies")) { 243f1ddee08Sespie return 1; 244f1ddee08Sespie } 245f1ddee08Sespie if ($state->{interactive}) { 246f1ddee08Sespie return $state->confirm("Delete them as well", 0); 247f1ddee08Sespie } 248f1ddee08Sespie return 0; 249f1ddee08Sespie} 250f1ddee08Sespie 251f1ddee08Sespiesub fix_bad_dependencies 252f1ddee08Sespie{ 253f1ddee08Sespie my $state = shift; 254f1ddee08Sespie 255f1ddee08Sespie if ($state->defines("baddepend")) { 256f1ddee08Sespie return 1; 257f1ddee08Sespie } 258f1ddee08Sespie if ($state->{interactive}) { 259f1ddee08Sespie return $state->confirm("Delete anyways", 0); 260f1ddee08Sespie } 261f1ddee08Sespie return 0; 262f1ddee08Sespie} 263f1ddee08Sespie 264681090d4Sespiesub remove_set 265681090d4Sespie{ 266681090d4Sespie my ($set, $state) = @_; 267681090d4Sespie 268681090d4Sespie my $todo = {}; 269681090d4Sespie my $bad = {}; 270681090d4Sespie $set = $set->real_set; 271681090d4Sespie if ($set->{finished}) { 272681090d4Sespie return (); 273681090d4Sespie } 274681090d4Sespie for my $pkgname ($set->older_names) { 275681090d4Sespie unless (is_installed($pkgname)) { 276681090d4Sespie $state->errsay("#1 was not installed", $pkgname); 277681090d4Sespie $set->{finished} = 1; 278681090d4Sespie $set->cleanup(OpenBSD::Handle::NOT_FOUND); 279681090d4Sespie $state->{bad}++; 280681090d4Sespie return (); 281681090d4Sespie } 282681090d4Sespie my $r = OpenBSD::RequiredBy->new($pkgname); 283681090d4Sespie for my $pkg ($r->list) { 284681090d4Sespie next if $set->{older}->{$pkg}; 285681090d4Sespie my $f = $state->tracker->find($pkg); 286681090d4Sespie if (defined $f) { 287681090d4Sespie $todo->{$pkg} = $f; 288681090d4Sespie } else { 289681090d4Sespie $bad->{$pkg} = 1; 290681090d4Sespie } 291681090d4Sespie } 292681090d4Sespie } 293681090d4Sespie if (keys %$bad > 0) { 294f1ddee08Sespie my $bad2 = {}; 295f1ddee08Sespie for my $pkg (keys %$bad) { 296f1ddee08Sespie if (!is_installed($pkg)) { 297f1ddee08Sespie $bad2->{$pkg} = 1; 298f1ddee08Sespie } 299f1ddee08Sespie } 300f1ddee08Sespie if (keys %$bad2 > 0) { 301f1ddee08Sespie $state->errsay("#1 depends on non-existant #2", 302*303a35c3Sespie $set->print, join(' ', sort keys %$bad2)); 303f1ddee08Sespie if (fix_bad_dependencies($state)) { 304f1ddee08Sespie for my $pkg (keys %$bad2) { 305f1ddee08Sespie delete $bad->{$pkg}; 306f1ddee08Sespie } 307f1ddee08Sespie } 308f1ddee08Sespie } 309f1ddee08Sespie } 310f1ddee08Sespie if (keys %$bad > 0) { 31130df58edSespie if (!$state->{automatic} || $state->verbose) { 312681090d4Sespie $state->errsay("can't delete #1 without deleting #2", 313*303a35c3Sespie $set->print, join(' ', sort keys %$bad)); 31430df58edSespie } 31530df58edSespie if (!$state->{automatic}) { 316f1ddee08Sespie if (delete_dependencies($state)) { 317681090d4Sespie my $l = create_locations($state, keys %$bad); 318681090d4Sespie $state->tracker->todo($l); 319681090d4Sespie return (@$l, $set); 320681090d4Sespie } 321681090d4Sespie $state->{bad}++; 322681090d4Sespie } 323681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 324681090d4Sespie $state->tracker->done($set); 325681090d4Sespie return (); 326681090d4Sespie } 327681090d4Sespie # XXX this si where we should detect loops 328681090d4Sespie if (keys %$todo > 0) { 329681090d4Sespie if ($set->{once}) { 330681090d4Sespie for my $set2 (values %$todo) { 331681090d4Sespie # XXX merge all ? 332681090d4Sespie $set->add_older($set2->older); 333681090d4Sespie $set2->{merged} = $set; 334681090d4Sespie $set2->{finished} = 1; 335681090d4Sespie } 336681090d4Sespie delete $set->{once}; 337681090d4Sespie return ($set); 338681090d4Sespie } 339681090d4Sespie $set->{once} = 1; 340681090d4Sespie return (values %$todo, $set); 341681090d4Sespie } 342681090d4Sespie if ($state->{automatic}) { 343681090d4Sespie for my $pkg ($set->older) { 344681090d4Sespie $pkg->complete_old; 345681090d4Sespie if ($pkg->plist->has('manual-installation')) { 34630df58edSespie $state->say("Won't delete manually installed #1", 347*303a35c3Sespie $set->print) if $state->verbose; 348681090d4Sespie $set->cleanup(OpenBSD::Handle::CANT_DELETE); 349681090d4Sespie $state->tracker->done($set); 350681090d4Sespie return (); 351681090d4Sespie } 352681090d4Sespie } 353681090d4Sespie } 354681090d4Sespie really_remove($set, $state); 355681090d4Sespie return (); 356681090d4Sespie} 357681090d4Sespie 358a409537dSespiesub main 359a409537dSespie{ 360a409537dSespie my ($self, $state) = @_; 361a409537dSespie 3627096cf21Sespie my %done; 3637096cf21Sespie my $removed; 3647096cf21Sespie 365681090d4Sespie $state->tracker->todo($state->{setlist}); 366a409537dSespie # and finally, handle the removal 367681090d4Sespie while (my $set = shift @{$state->{setlist}}) { 368681090d4Sespie $state->status->what->set($set); 369681090d4Sespie unshift(@{$state->{setlist}}, remove_set($set, $state)); 370a409537dSespie } 371a409537dSespie} 372a409537dSespie 373a409537dSespiesub new_state 374a409537dSespie{ 3757e83eca3Sespie my ($self, $cmd) = @_; 3767e83eca3Sespie return OpenBSD::PkgDelete::State->new($cmd); 377a409537dSespie} 378a409537dSespie 379a409537dSespie1; 380