xref: /openbsd/usr.sbin/pkg_add/OpenBSD/Search.pm (revision 039cbdaa)
1# ex:ts=8 sw=4:
2# $OpenBSD: Search.pm,v 1.33 2023/06/13 09:07:17 espie Exp $
3#
4# Copyright (c) 2007 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::Search;
21sub match_locations($self, $o)
22{
23	require OpenBSD::PackageLocation;
24
25	my @l = map {$o->new_location($_)} $self->match($o);
26	return \@l;
27}
28
29sub keep_all($self)
30{
31	$self->{keep_all} = 1;
32	return $self;
33}
34
35package OpenBSD::Search::PkgSpec;
36our @ISA=(qw(OpenBSD::Search));
37
38sub filter($self, @list)
39{
40	return $self->{spec}->match_ref(\@list);
41}
42
43sub filter_libs($self, @list)
44{
45	return $self->{spec}->match_libs_ref(\@list);
46}
47
48sub match_locations($self, $o)
49{
50	return $self->{spec}->match_locations($o->locations_list);
51}
52
53sub filter_locations($self, $l)
54{
55	return $self->{spec}->match_locations($l);
56}
57
58sub new($class, $pattern, $with_partial = 0)
59{
60	require OpenBSD::PkgSpec;
61
62	bless { spec => $class->spec_class->new($pattern, $with_partial)},
63	    $class;
64}
65
66sub add_pkgpath_hint($self, $pkgpath)
67{
68	$self->{pkgpath} = $pkgpath;
69	return $self;
70}
71
72sub spec_class($)
73{ "OpenBSD::PkgSpec" }
74
75sub is_valid($self)
76{
77	return $self->{spec}->is_valid;
78}
79
80package OpenBSD::Search::Exact;
81our @ISA=(qw(OpenBSD::Search::PkgSpec));
82sub spec_class($)
83{ "OpenBSD::PkgSpec::Exact" }
84
85package OpenBSD::Search::Stem;
86our @ISA=(qw(OpenBSD::Search));
87
88sub new($class, $stem)
89{
90	# TODO this is where we currently handle "branch" matches
91	# but it's likely the stem/ % mechanisms should be seen as more
92	# generic cases of PackageSpecs eventually to better results
93	if ($stem =~ m/^(.*)\%(.*)/) {
94		return ($class->_new($1),
95		    OpenBSD::Search::FilterLocation->match_partialpath($2));
96	} else {
97		return $class->_new($stem);
98	}
99}
100
101sub _new($class, $stem)
102{
103	if ($stem =~ m/^(.*)\-\-(.*)/) {
104		# XXX
105		return OpenBSD::Search::Exact->new("$1-*-$2");
106    	}
107	return bless {"$stem" => 1}, $class;
108}
109
110sub split($class, $pkgname)
111{
112	require OpenBSD::PackageName;
113
114	return $class->new(OpenBSD::PackageName::splitstem($pkgname));
115}
116
117sub add_stem($self, $extra)
118{
119	$self->{$extra} = 1;
120
121}
122
123sub match($self, $o)
124{
125	my @r = ();
126	for my $k (keys %$self) {
127		push(@r, $o->stemlist->find($k));
128	}
129	return @r;
130}
131
132sub _keep($self, $stem)
133{
134	return defined $self->{$stem};
135}
136
137sub filter($self, @l)
138{
139	my @result = ();
140	require OpenBSD::PackageName;
141	for my $pkg (@l) {
142		if ($self->_keep(OpenBSD::PackageName::splitstem($pkg))) {
143			push(@result, $pkg);
144		}
145	}
146	return @result;
147}
148
149package OpenBSD::Search::PartialStem;
150our @ISA=(qw(OpenBSD::Search::Stem));
151
152sub match($self, $o)
153{
154	my @r = ();
155	for my $k (keys %$self) {
156		push(@r, $o->stemlist->find_partial($k));
157	}
158	return @r;
159}
160
161sub _keep($self, $stem)
162{
163	for my $partial (keys %$self) {
164		if ($stem =~ /\Q$partial\E/) {
165			return 1;
166		}
167	}
168	return 0;
169}
170
171package OpenBSD::Search::FilterLocation;
172our @ISA=(qw(OpenBSD::Search));
173sub new($class, $code)
174{
175	return bless {code => $code}, $class;
176}
177
178sub filter_locations($self, $l)
179{
180	return &{$self->{code}}($l);
181}
182
183sub more_recent_than($class, $name, $rfound)
184{
185	require OpenBSD::PackageName;
186
187	my $f = OpenBSD::PackageName->from_string($name);
188
189	return $class->new(
190sub($l) {
191	my $r = [];
192	for my $e (@$l) {
193		if ($f->{version}->compare($e->pkgname->{version}) <= 0) {
194			push(@$r, $e);
195		}
196		if (ref $rfound) {
197			$$rfound = 1;
198		}
199	}
200	return $r;
201	});
202}
203
204sub keep_most_recent($class)
205{
206	return $class->new(
207sub($l) {
208	# no need to filter
209	return $l if @$l <= 1;
210
211	require OpenBSD::PackageName;
212	my $h = {};
213	# we have to prove we have to keep it
214	while (my $e = pop @$l) {
215		my $stem = $e->pkgname->{stem};
216		my $keep = 1;
217		# so let's compare with every element in $h with the same stem
218		for my $f (@{$h->{$e->pkgname->{stem}}}) {
219			# if this is not the same flavors,
220			# we don't filter
221			if ($f->pkgname->flavor_string ne $e->pkgname->flavor_string) {
222				next;
223			}
224			# unsigned packages will break here
225			my $u = $e->update_info;
226			if (!defined $u) {
227				$keep = 0;
228				last;
229			}
230			# okay, now we need to prove there's a common pkgpath
231			if (!$u->match_pkgpath($f->update_info)) {
232				next;
233			}
234
235			if ($f->pkgname->{version}->compare($e->pkgname->{version}) < 0) {
236			    $f = $e;
237			}
238			$keep = 0;
239			last;
240
241		}
242		if ($keep) {
243			push(@{$h->{$e->pkgname->{stem}}}, $e);
244		}
245	}
246	my $largest = [];
247	push @$largest, map {@$_} values %$h;
248	return $largest;
249}
250	);
251}
252
253sub match_partialpath($class, $subdir)
254{
255	return $class->new(
256sub($l) {
257	if (@$l == 0) {
258		return $l;
259	}
260	my @l2 = ();
261	for my $loc (@$l) {
262		if (!$loc) {
263			next;
264		}
265		my $p2 = $loc->update_info;
266		if (!$p2) {
267			next;
268		}
269		if ($p2->pkgpath->partial_match($subdir)) {
270			push(@l2, $loc);
271		}
272	}
273	return \@l2;
274}
275	);
276}
277
2781;
279