1# ex:ts=8 sw=4:
2# $OpenBSD: CollisionReport.pm,v 1.49 2023/06/13 09:07:17 espie Exp $
3#
4# Copyright (c) 2003-2006 Marc Espie <espie@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18use v5.36;
19
20package OpenBSD::PackingElement;
21sub handle_collisions($, $, $, $)
22{
23}
24
25package OpenBSD::PackingElement::FileBase;
26sub handle_collisions($self, $todo, $pkg, $bypkg)
27{
28	my $name = $self->fullname;
29	if (defined $todo->{$name}) {
30		push(@{$bypkg->{$pkg}}, $name);
31		delete $todo->{$name};
32	}
33}
34
35package OpenBSD::CollisionReport;
36use OpenBSD::PackingList;
37use OpenBSD::PackageInfo;
38
39sub find_collisions($todo, $state)
40{
41	my $verbose = $state->verbose >= 3;
42	my $bypkg = {};
43	for my $name (keys %$todo) {
44		my $pkg = $state->vstat->value($state->{destdir}.$name);
45		if (defined $pkg) {
46			push(@{$bypkg->{$pkg}}, $name);
47			delete $todo->{$name};
48		}
49	}
50
51
52	if (!%$todo) {
53		return $bypkg;
54	}
55	for my $pkg (installed_packages()) {
56		$state->say("Looking for collisions in #1", $pkg) if $verbose;
57		# XXX in -n mode, some stuff is not really there
58		# avoid warnings
59		next unless -d installed_info($pkg);
60		my $plist = OpenBSD::PackingList->from_installation($pkg,
61		    \&OpenBSD::PackingList::FilesOnly);
62		next if !defined $plist;
63		$plist->handle_collisions($todo, $pkg, $bypkg);
64	}
65	return $bypkg;
66}
67
68sub collision_report($list, $state, $set)
69{
70	my $destdir = $state->{destdir};
71
72	if ($state->defines('removecollisions')) {
73		require OpenBSD::Error;
74		for my $f (@$list) {
75			$state->unlink(1, $destdir.$f->fullname);
76		}
77		return;
78	}
79	my %todo = map {($_->fullname, $_->{d})} @$list;
80	my %extra = map {($_->fullname, $_->{newly_found})} @$list;
81	my $clueless_bat;
82	my $clueless_bat2;
83	my $found = 0;
84
85	$state->errsay("Collision in #1: the following files already exist",
86	    $set->print);
87	if (!$state->defines('dontfindcollisions')) {
88		my $bypkg = find_collisions(\%todo, $state);
89		for my $pkg (sort keys %$bypkg) {
90		    for my $item (sort @{$bypkg->{$pkg}}) {
91		    	$found++;
92			$state->errsay("\t#1 (#2 and #3)", $item, $pkg,
93			    $extra{$item});
94		    }
95		    if ($pkg =~ m/^(?:partial\-|borked\.\d+$)/o) {
96			$clueless_bat = $pkg;
97		    }
98		    if ($pkg =~ m/^\.libs\d*-*$/o) {
99			$clueless_bat2 = $pkg;
100		    }
101		}
102	}
103	if (%todo) {
104
105		for my $item (sort keys %todo) {
106			my $old = $todo{$item};
107		    $state->errprint("\t#1 from #2", $item, $extra{$item});
108		    if (defined $old && -f $destdir.$item) {
109			    my $d = $old->new($destdir.$item);
110
111			    if ($d->equals($old)) {
112				    $state->errsay(" (same checksum)");
113			    } else {
114				    $state->errsay(" (different checksum)");
115			    }
116		    } else {
117			    $state->errsay;
118		    }
119	    	}
120	}
121	if (defined $clueless_bat) {
122		$state->errprint("The package name #1 suggests that a former installation\n".
123		    "of a similar package got interrupted.  It is likely that\n".
124		    "\tpkg_delete #1\n".
125		    "will solve the problem\n", $clueless_bat);
126	}
127	if (defined $clueless_bat2) {
128		$state->errprint("The package name #1 suggests remaining libraries\n".
129		    "from a former package update.  It is likely that\n".
130		    "\tpkg_delete #1\n".
131		    "will solve the problem\n", $clueless_bat2);
132	}
133	my $dorepair = 0;
134	if ($found == 0) {
135		$dorepair = $state->defines('repair') ||
136		    $state->confirm_defaults_to_no(
137		    "It seems to be a missing package registration\nRepair");
138	}
139	if ($dorepair == 1) {
140		for my $f (@$list) {
141
142			if ($state->unlink($state->verbose >= 2,
143			    $destdir.$f->fullname)) {
144				$state->{problems}--;
145			} else {
146				return;
147			}
148		}
149		$state->{repairdependencies} = 1;
150	}
151}
152
1531;
154