xref: /openbsd/usr.sbin/pkg_add/OpenBSD/LibSpec.pm (revision d803f986)
1# ex:ts=8 sw=4:
2# $OpenBSD: LibSpec.pm,v 1.21 2023/10/08 12:45:31 espie Exp $
3#
4# Copyright (c) 2010 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::LibObject;
21
22sub systemlibraryclass($self)
23{
24	return ref($self);
25}
26
27sub key($self)
28{
29	if (defined $self->{dir}) {
30		return "$self->{dir}/$self->{stem}";
31	} else {
32		return $self->{stem};
33	}
34}
35
36sub major($self)
37{
38	return $self->{major};
39}
40
41sub minor($self)
42{
43	return $self->{minor};
44}
45
46sub version($self)
47{
48	return ".".$self->major.".".$self->minor;
49}
50
51sub is_static($) { 0 }
52
53sub is_valid($) { 1 }
54
55sub stem($self)
56{
57	return $self->{stem};
58}
59
60sub badclass($self)
61{
62	"OpenBSD::BadLib";
63}
64
65sub lookup($spec, $repo, $base)
66{
67	my $approx = $spec->lookup_stem($repo);
68	if (!defined $approx) {
69		return undef;
70	}
71	my $r = [];
72	for my $c (@$approx) {
73		if ($spec->match($c, $base)) {
74			push(@$r, $c);
75		}
76	}
77	return $r;
78}
79
80sub compare($a, $b)
81{
82	if ($a->key ne $b->key) {
83		return $a->key cmp $b->key;
84	}
85	if ($a->major != $b->major) {
86		return $a->major <=> $b->major;
87	}
88	return $a->minor <=> $b->minor;
89}
90
91package OpenBSD::BadLib;
92our @ISA=qw(OpenBSD::LibObject);
93
94sub to_string($self)
95{
96	return $$self;
97}
98
99sub new($class, $string)
100{
101	bless \$string, $class;
102}
103
104sub is_valid($)
105{
106	return 0;
107}
108
109sub lookup_stem($, $)
110{
111	return undef;
112}
113
114# $spec->match($library, $base)
115sub match($, $, $)
116{
117	return 0;
118}
119
120package OpenBSD::LibRepo;
121
122sub new($class)
123{
124	bless {}, $class;
125}
126
127sub register($repo, $lib, $origin)
128{
129	$lib->set_origin($origin);
130	push @{$repo->{$lib->stem}}, $lib;
131}
132
133sub find_best($repo, $stem)
134{
135	my $best;
136
137	if (exists $repo->{$stem}) {
138		for my $lib (@{$repo->{$stem}}) {
139			if (!defined $best || $lib->is_better($best)) {
140				$best = $lib;
141			}
142		}
143	}
144	return $best;
145}
146
147package OpenBSD::Library;
148our @ISA = qw(OpenBSD::LibObject);
149
150sub systemlibraryclass($)
151{
152	"OpenBSD::Library::System";
153}
154
155sub from_string($class, $filename)
156{
157	if (my ($dir, $stem, $major, $minor) = $filename =~ m/^(.*)\/lib([^\/]+)\.so\.(\d+)\.(\d+)$/o) {
158		bless { dir => $dir, stem => $stem, major => $major,
159		    minor => $minor }, $class;
160	} else {
161		return $class->badclass->new($filename);
162	}
163}
164
165sub to_string($self)
166{
167	return "$self->{dir}/lib$self->{stem}.so.$self->{major}.$self->{minor}";
168}
169
170sub set_origin($self, $origin)
171{
172	$self->{origin} = $origin;
173	if ($origin eq 'system') {
174		bless $self, $self->systemlibraryclass;
175	}
176	return $self;
177}
178
179sub origin($self)
180{
181	return $self->{origin};
182}
183
184sub no_match_dispatch($library, $spec, $base)
185{
186	return $spec->no_match_shared($library, $base);
187}
188
189sub is_better($self, $other)
190{
191	if ($other->is_static) {
192		return 1;
193	}
194	if ($self->major > $other->major) {
195		return 1;
196	}
197	if ($self->major == $other->major && $self->minor > $other->minor) {
198		return 1;
199    	}
200	return 0;
201}
202
203# could be used for better reporting
204# is used for regression testing
205package OpenBSD::Library::System;
206our @ISA = qw(OpenBSD::Library);
207
208package OpenBSD::LibSpec;
209our @ISA = qw(OpenBSD::LibObject);
210
211sub new($class, $dir, $stem, $major, $minor)
212{
213	bless {
214		dir => $dir, stem => $stem,
215		major => $major, minor => $minor
216	    }, $class;
217}
218
219my $cached = {};
220
221sub from_string($class, $s)
222{
223	return $cached->{$s} //= $class->new_from_string($s);
224}
225
226sub new_with_stem($class, $stem, $major, $minor)
227{
228	if ($stem =~ m/^(.*)\/([^\/]+)$/o) {
229		return $class->new($1, $2, $major, $minor);
230	} else {
231		return $class->new(undef, $stem, $major, $minor);
232	}
233}
234
235sub new_from_string($class, $string)
236{
237	if (my ($stem, $major, $minor) = $string =~ m/^(.*)\.(\d+)\.(\d+)$/o) {
238		return $class->new_with_stem($stem, $major, $minor);
239	} else {
240		return $class->badclass->new($string);
241	}
242}
243
244sub to_string($self)
245{
246	return join('.', $self->key, $self->major, $self->minor);
247
248}
249
250sub lookup_stem($spec, $repo)
251{
252	my $result = $repo->{$spec->stem};
253	if (!defined $result) {
254		return undef;
255	} else {
256		return $result;
257	}
258}
259
260sub no_match_major($spec, $library)
261{
262	return $spec->major != $library->major;
263}
264
265sub no_match_name($spec, $library, $base)
266{
267	if (defined $spec->{dir}) {
268		if ("$base/$spec->{dir}" eq $library->{dir}) {
269			return undef;
270		}
271	} else {
272		for my $d ($base, OpenBSD::Paths->library_dirs) {
273			if ("$d/lib" eq $library->{dir}) {
274				return undef;
275			}
276		}
277	}
278	return "bad directory";
279}
280
281sub no_match_shared($spec, $library, $base)
282{
283	if ($spec->no_match_major($library)) {
284		return "bad major";
285	}
286	if ($spec->major == $library->major &&
287	    $spec->minor > $library->minor) {
288		return "minor is too small";
289	}
290	return $spec->no_match_name($library, $base);
291}
292
293# classic double dispatch pattern
294sub no_match($spec, $library, $base)
295{
296	return $library->no_match_dispatch($spec, $base);
297}
298
299sub match($spec, $library, $base)
300{
301	return !$spec->no_match($library, $base);
302}
303
3041;
305