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