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