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