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