xref: /openbsd/usr.sbin/pkg_add/OpenBSD/PkgSpec.pm (revision 039cbdaa)
1f5d79ea0Sespie# ex:ts=8 sw=4:
2*039cbdaaSespie# $OpenBSD: PkgSpec.pm,v 1.51 2023/06/13 09:07:17 espie Exp $
3f5d79ea0Sespie#
43ac6f416Sespie# Copyright (c) 2003-2007 Marc Espie <espie@openbsd.org>
5f5d79ea0Sespie#
6f5d79ea0Sespie# Permission to use, copy, modify, and distribute this software for any
7f5d79ea0Sespie# purpose with or without fee is hereby granted, provided that the above
8f5d79ea0Sespie# copyright notice and this permission notice appear in all copies.
9f5d79ea0Sespie#
10f5d79ea0Sespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f5d79ea0Sespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f5d79ea0Sespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f5d79ea0Sespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f5d79ea0Sespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f5d79ea0Sespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f5d79ea0Sespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f5d79ea0Sespie
18*039cbdaaSespieuse v5.36;
19f5d79ea0Sespie
20e451b54dSespiepackage OpenBSD::PkgSpec::flavorspec;
21*039cbdaaSespiesub new($class, $spec)
22f5d79ea0Sespie{
23e451b54dSespie	bless \$spec, $class;
24f5d79ea0Sespie}
25f5d79ea0Sespie
26*039cbdaaSespiesub check_1flavor($f, $spec)
27f5d79ea0Sespie{
284b54d96fSespie	for my $flavor (split /\-/o, $spec) {
29f5d79ea0Sespie		# must not be here
304b54d96fSespie		if ($flavor =~ m/^\!(.*)$/o) {
31a7ceec02Sespie			return 0 if $f->{$1};
32f5d79ea0Sespie		# must be here
33f5d79ea0Sespie		} else {
344b54d96fSespie			return 0 unless $f->{$flavor};
35f5d79ea0Sespie		}
36f5d79ea0Sespie	}
37f5d79ea0Sespie	return 1;
38f5d79ea0Sespie}
39f5d79ea0Sespie
40*039cbdaaSespiesub match($self, $h)
41f5d79ea0Sespie{
42f5d79ea0Sespie	# check each flavor constraint
434b54d96fSespie	for my $c (split /\,/o, $$self) {
444b54d96fSespie		if (check_1flavor($h->{flavors}, $c)) {
45f5d79ea0Sespie			return 1;
46f5d79ea0Sespie		}
47f5d79ea0Sespie	}
48f5d79ea0Sespie	return 0;
49f5d79ea0Sespie}
50f5d79ea0Sespie
51e451b54dSespiepackage OpenBSD::PkgSpec::exactflavor;
52e451b54dSespieour @ISA = qw(OpenBSD::PkgSpec::flavorspec);
53*039cbdaaSespiesub new($class, $value)
5448b1a19dSespie{
5548b1a19dSespie	bless {map{($_, 1)} split(/\-/, $value)}, $class;
5648b1a19dSespie}
5748b1a19dSespie
58*039cbdaaSespiesub flavor_string($self)
5948b1a19dSespie{
6048b1a19dSespie	return join('-', sort keys %$self);
6148b1a19dSespie}
6248b1a19dSespie
63*039cbdaaSespiesub match($self, $h)
64f5d79ea0Sespie{
6548b1a19dSespie	if ($self->flavor_string eq $h->flavor_string) {
66e451b54dSespie		return 1;
67e451b54dSespie	} else {
68e451b54dSespie		return 0;
69e451b54dSespie	}
70e451b54dSespie}
71e451b54dSespie
72e451b54dSespiepackage OpenBSD::PkgSpec::versionspec;
739a76099aSespieour @ISA = qw(OpenBSD::PackageName::version);
749a76099aSespiemy $ops = {
759a76099aSespie	'<' => 'lt',
769a76099aSespie	'<=' => 'le',
779a76099aSespie	'>' => 'gt',
789a76099aSespie	'>=' => 'ge',
799a76099aSespie	'=' => 'eq'
809a76099aSespie};
819a76099aSespie
82*039cbdaaSespiesub new($class, $s)
83e451b54dSespie{
849a76099aSespie	my ($op, $version) = ('=', $s);
859a76099aSespie	if ($s =~ m/^(\>\=|\>|\<\=|\<|\=)(.*)$/) {
869a76099aSespie		($op, $version) = ($1, $2);
879a76099aSespie	}
889a76099aSespie	return "OpenBSD::PkgSpec::version::$ops->{$op}"->from_string($version);
89e451b54dSespie}
90e451b54dSespie
91*039cbdaaSespiesub pnum_compare($self, $b)
929a76099aSespie{
939a76099aSespie	if (!defined $self->{p}) {
949a76099aSespie		return 0;
959a76099aSespie	} else {
969a76099aSespie		return $self->SUPER::pnum_compare($b);
979a76099aSespie	}
989a76099aSespie}
999a76099aSespie
100*039cbdaaSespiesub is_exact($)
1019a76099aSespie{
1029a76099aSespie	return 0;
1039a76099aSespie}
1049a76099aSespie
1059a76099aSespiepackage OpenBSD::PkgSpec::version::lt;
1069a76099aSespieour @ISA = qw(OpenBSD::PkgSpec::versionspec);
107*039cbdaaSespiesub match($self, $b)
108e451b54dSespie{
1099a76099aSespie	-$self->compare($b->{version}) < 0 ? 1 : 0;
1109a76099aSespie}
111e451b54dSespie
1129a76099aSespiepackage OpenBSD::PkgSpec::version::le;
1139a76099aSespieour @ISA = qw(OpenBSD::PkgSpec::versionspec);
114*039cbdaaSespiesub match($self, $b)
1159a76099aSespie{
1169a76099aSespie	-$self->compare($b->{version}) <= 0 ? 1 : 0;
1179a76099aSespie}
1189a76099aSespie
1199a76099aSespiepackage OpenBSD::PkgSpec::version::gt;
1209a76099aSespieour @ISA = qw(OpenBSD::PkgSpec::versionspec);
121*039cbdaaSespiesub match($self, $b)
1229a76099aSespie{
1239a76099aSespie	-$self->compare($b->{version}) > 0 ? 1 : 0;
1249a76099aSespie}
1259a76099aSespie
1269a76099aSespiepackage OpenBSD::PkgSpec::version::ge;
1279a76099aSespieour @ISA = qw(OpenBSD::PkgSpec::versionspec);
128*039cbdaaSespiesub match($self, $b)
1299a76099aSespie{
1309a76099aSespie	-$self->compare($b->{version}) >= 0 ? 1 : 0;
1319a76099aSespie}
1329a76099aSespie
1339a76099aSespiepackage OpenBSD::PkgSpec::version::eq;
1349a76099aSespieour @ISA = qw(OpenBSD::PkgSpec::versionspec);
135*039cbdaaSespiesub match($self, $b)
1369a76099aSespie{
1379a76099aSespie	-$self->compare($b->{version}) == 0 ? 1 : 0;
1389a76099aSespie}
1399a76099aSespie
140*039cbdaaSespiesub is_exact($)
1419a76099aSespie{
1429a76099aSespie	return 1;
143e451b54dSespie}
144e451b54dSespie
145d348934bSespiepackage OpenBSD::PkgSpec::badspec;
146*039cbdaaSespiesub new($class)
147d348934bSespie{
148d348934bSespie	bless {}, $class;
149d348934bSespie}
150d348934bSespie
151*039cbdaaSespie# $self->match*($list)
152*039cbdaaSespiesub match_ref($, $)
153d348934bSespie{
154d348934bSespie	return ();
155d348934bSespie}
156d348934bSespie
157*039cbdaaSespiesub match_libs_ref($, $)
1588ac01a5dSespie{
1598ac01a5dSespie	return ();
1608ac01a5dSespie}
1618ac01a5dSespie
162*039cbdaaSespiesub match_locations($, $)
163d348934bSespie{
16467e90883Sespie	return [];
165d348934bSespie}
166d348934bSespie
167*039cbdaaSespiesub is_valid($)
168d348934bSespie{
169d348934bSespie	return 0;
170d348934bSespie}
171e451b54dSespie
172e451b54dSespiepackage OpenBSD::PkgSpec::SubPattern;
173e451b54dSespieuse OpenBSD::PackageName;
174e451b54dSespie
175*039cbdaaSespiesub parse($class, $p)
176e451b54dSespie{
177d348934bSespie	my $r = {};
178d348934bSespie
179415f1c34Sespie	# let's try really hard to find the stem and the flavors
1802dadf88dSespie	unless ($p =~ m/^
181a1e0fe66Sespie	    	([^%]+?) # stem part
1822dadf88dSespie		\-
1832dadf88dSespie		(
184a1e0fe66Sespie		    (?:\>|\>\=|\<\=|\<|\=)?\d[^-%]*  # optional op + version
1852dadf88dSespie		    |\* # or any version
1862dadf88dSespie		)
187d5381c5aSespie		(?:\-([^%]*))? # optional flavor part
1882dadf88dSespie	    $/x) {
189d348934bSespie		return undef;
190f5d79ea0Sespie	}
191d348934bSespie	($r->{stemspec}, $r->{vspec}, $r->{flavorspec}) = ($1, $2, $3);
192415f1c34Sespie
1932dadf88dSespie	$r->{flavorspec} //= '';
194d348934bSespie	$r->{stemspec} =~ s/\./\\\./go;
195d348934bSespie	$r->{stemspec} =~ s/\+/\\\+/go;
196d348934bSespie	$r->{stemspec} =~ s/\*/\.\*/go;
197d348934bSespie	$r->{stemspec} =~ s/\?/\./go;
198d348934bSespie	$r->{stemspec} =~ s/^(\\\.libs)\-/$1\\d*\-/go;
199d348934bSespie	return $r;
200e451b54dSespie}
201415f1c34Sespie
202*039cbdaaSespiesub add_version_constraints($class, $constraints, $vspec)
203e451b54dSespie{
204eca3935aSespie	# turn the vspec into a list of constraints.
205eca3935aSespie	if ($vspec eq '*') {
206eca3935aSespie		# non constraint
207eca3935aSespie	} else {
208eca3935aSespie		for my $c (split /\,/, $vspec) {
209e451b54dSespie			push(@$constraints,
210e451b54dSespie			    OpenBSD::PkgSpec::versionspec->new($c));
211e451b54dSespie		}
212eca3935aSespie	}
213eca3935aSespie}
214f5d79ea0Sespie
215*039cbdaaSespiesub add_flavor_constraints($class, $constraints, $flavorspec)
216e451b54dSespie{
217e451b54dSespie	# and likewise for flavors
218e451b54dSespie	if ($flavorspec eq '') {
219e451b54dSespie		# non constraint
220e451b54dSespie	} else {
221e451b54dSespie		push(@$constraints,
222e451b54dSespie		    OpenBSD::PkgSpec::flavorspec->new($flavorspec));
223e451b54dSespie	}
224e451b54dSespie}
225e451b54dSespie
226*039cbdaaSespiesub new($class, $p, $with_partial)
227e451b54dSespie{
228d348934bSespie	my $r = $class->parse($p);
229d348934bSespie	if (defined $r) {
230d348934bSespie		my $stemspec = $r->{stemspec};
231e451b54dSespie		my $constraints = [];
232d348934bSespie		$class->add_version_constraints($constraints, $r->{vspec});
233d348934bSespie		$class->add_flavor_constraints($constraints, $r->{flavorspec});
234e451b54dSespie
235d348934bSespie		my $o = bless {
2368ac01a5dSespie			libstem => qr{^\.libs\d*\-$stemspec\-\d.*$},
237e451b54dSespie		    }, $class;
238d24f1704Sespie
239d24f1704Sespie		if ($with_partial) {
2405f772b88Sespie			$o->{fuzzystem} = qr{^(?:partial\-)*$stemspec\-\d.*$};
241d24f1704Sespie		} else {
242d24f1704Sespie			$o->{fuzzystem} = qr{^$stemspec\-\d.*$};
243d24f1704Sespie		}
2448f642ffbSespie		if (@$constraints != 0) {
2458f642ffbSespie			$o->{constraints} = $constraints;
2468f642ffbSespie		}
247d348934bSespie		if (defined $r->{e}) {
248d348934bSespie			$o->{e} = 1;
249d348934bSespie		}
250d348934bSespie	   	return $o;
251d348934bSespie	} else {
252d348934bSespie		return OpenBSD::PkgSpec::badspec->new;
253d348934bSespie	}
254e451b54dSespie}
255e451b54dSespie
256*039cbdaaSespiesub match_ref($o, $list)
257e451b54dSespie{
258f5d79ea0Sespie	my @result = ();
259f5d79ea0Sespie	# Now, have to extract the version number, and the flavor...
260e451b54dSespieLOOP1:
2611f9ffadcSespie	for my $s (grep(/$o->{fuzzystem}/, @$list)) {
262eca3935aSespie		my $name = OpenBSD::PackageName->from_string($s);
2638f642ffbSespie		if (defined $o->{constraints}) {
264e451b54dSespie			for my $c (@{$o->{constraints}}) {
265e451b54dSespie				next LOOP1 unless $c->match($name);
266f5d79ea0Sespie			}
2678f642ffbSespie		}
268cfda5a0cSespie		if (wantarray) {
269e451b54dSespie			push(@result, $s);
270cfda5a0cSespie		} else {
271cfda5a0cSespie			return 1;
272cfda5a0cSespie		}
273f5d79ea0Sespie	}
274f5d79ea0Sespie
2759d478369Sespie	if (wantarray) {
276f5d79ea0Sespie		return @result;
2779d478369Sespie	} else {
2789d478369Sespie		return 0;
2799d478369Sespie	}
280f5d79ea0Sespie}
281f5d79ea0Sespie
282*039cbdaaSespiesub match_libs_ref($o, $list)
2838ac01a5dSespie{
2848ac01a5dSespie	return grep(/$o->{libstem}/, @$list);
2858ac01a5dSespie}
2868ac01a5dSespie
2878ac01a5dSespie
288*039cbdaaSespiesub match_locations($o, $list)
289e451b54dSespie{
290e451b54dSespie	my $result = [];
291e451b54dSespie	# Now, have to extract the version number, and the flavor...
292e451b54dSespieLOOP2:
2935ac93686Sespie	for my $s (grep { $_->name =~ m/$o->{fuzzystem}/} @$list) {
294e451b54dSespie		my $name = $s->pkgname;
2958f642ffbSespie		if (defined $o->{constraints}) {
296e451b54dSespie			for my $c (@{$o->{constraints}}) {
297e451b54dSespie				next LOOP2 unless $c->match($name);
298e451b54dSespie			}
2998f642ffbSespie		}
300e451b54dSespie		push(@$result, $s);
301e451b54dSespie	}
302e451b54dSespie
303e451b54dSespie	return $result;
304e451b54dSespie}
305e451b54dSespie
306*039cbdaaSespiesub is_valid($)
307d348934bSespie{
3084f16f0f9Sespie	return 1;
309d348934bSespie}
310d348934bSespie
311e451b54dSespiepackage OpenBSD::PkgSpec;
312*039cbdaaSespiesub subpattern_class($)
313e451b54dSespie{ "OpenBSD::PkgSpec::SubPattern" }
314*039cbdaaSespiesub new($class, $pattern, $with_partial = 0)
315e451b54dSespie{
316d24f1704Sespie	my @l = map { $class->subpattern_class->new($_, $with_partial) }
317e451b54dSespie		(split /\|/o, $pattern);
3185ac93686Sespie	if (@l == 1) {
3195ac93686Sespie		return $l[0];
3205ac93686Sespie	} else {
3215ac93686Sespie		return bless \@l, $class;
3225ac93686Sespie	}
323e451b54dSespie}
324e451b54dSespie
325*039cbdaaSespiesub match_ref($self, $r)
326e451b54dSespie{
327cfda5a0cSespie	if (wantarray) {
328e451b54dSespie		my @l = ();
329e451b54dSespie		for my $subpattern (@$self) {
330e451b54dSespie			push(@l, $subpattern->match_ref($r));
331e451b54dSespie		}
332e451b54dSespie		return @l;
333cfda5a0cSespie	} else {
334cfda5a0cSespie		for my $subpattern (@$self) {
335cfda5a0cSespie			if ($subpattern->match_ref($r)) {
336cfda5a0cSespie				return 1;
337cfda5a0cSespie			}
338cfda5a0cSespie		}
339cfda5a0cSespie		return 0;
340cfda5a0cSespie	}
341e451b54dSespie}
342e451b54dSespie
343*039cbdaaSespiesub match_libs_ref($self, $r)
3448ac01a5dSespie{
3458ac01a5dSespie	if (wantarray) {
3468ac01a5dSespie		my @l = ();
3478ac01a5dSespie		for my $subpattern (@$self) {
3488ac01a5dSespie			push(@l, $subpattern->match_libs_ref($r));
3498ac01a5dSespie		}
3508ac01a5dSespie		return @l;
3518ac01a5dSespie	} else {
3528ac01a5dSespie		for my $subpattern (@$self) {
3538ac01a5dSespie			if ($subpattern->match_libs_ref($r)) {
3548ac01a5dSespie				return 1;
3558ac01a5dSespie			}
3568ac01a5dSespie		}
3578ac01a5dSespie		return 0;
3588ac01a5dSespie	}
3598ac01a5dSespie}
3608ac01a5dSespie
361*039cbdaaSespiesub match_locations($self, $r)
362e451b54dSespie{
363e451b54dSespie	my $l = [];
364e451b54dSespie	for my $subpattern (@$self) {
365e451b54dSespie		push(@$l, @{$subpattern->match_locations($r)});
366e451b54dSespie	}
367e451b54dSespie	return $l;
368e451b54dSespie}
369e451b54dSespie
370*039cbdaaSespiesub is_valid($self)
371d348934bSespie{
372d348934bSespie	for my $subpattern (@$self) {
373d348934bSespie		return 0 unless $subpattern->is_valid;
374d348934bSespie	}
375d348934bSespie	return 1;
376d348934bSespie}
377d348934bSespie
378e451b54dSespiepackage OpenBSD::PkgSpec::SubPattern::Exact;
379e451b54dSespieour @ISA = qw(OpenBSD::PkgSpec::SubPattern);
380e451b54dSespie
381*039cbdaaSespiesub add_version_constraints($class, $constraints, $vspec)
382e451b54dSespie{
38341e3defdSespie	return if $vspec eq '*'; # XXX
384e451b54dSespie	my $v = OpenBSD::PkgSpec::versionspec->new($vspec);
385e47637b9Sespie	die "not a good exact spec" if !$v->is_exact;
386e47637b9Sespie	delete $v->{p};
387e451b54dSespie	push(@$constraints, $v);
388e451b54dSespie}
389e451b54dSespie
390*039cbdaaSespiesub add_flavor_constraints($class, $constraints, $flavorspec)
391e451b54dSespie{
392e451b54dSespie	push(@$constraints, OpenBSD::PkgSpec::exactflavor->new($flavorspec));
393e451b54dSespie}
394e451b54dSespie
395e451b54dSespiepackage OpenBSD::PkgSpec::Exact;
396e451b54dSespieour @ISA = qw(OpenBSD::PkgSpec);
397e451b54dSespie
398*039cbdaaSespiesub subpattern_class($)
399e451b54dSespie{ "OpenBSD::PkgSpec::SubPattern::Exact" }
400e451b54dSespie
401f5d79ea0Sespie1;
402