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