167d43fbcSespie# ex:ts=8 sw=4: 2*039cbdaaSespie# $OpenBSD: PackageName.pm,v 1.58 2023/06/13 09:07:17 espie Exp $ 36f05d20cSespie# 46fcf288dSespie# Copyright (c) 2003-2010 Marc Espie <espie@openbsd.org> 56f05d20cSespie# 667d43fbcSespie# Permission to use, copy, modify, and distribute this software for any 767d43fbcSespie# purpose with or without fee is hereby granted, provided that the above 867d43fbcSespie# copyright notice and this permission notice appear in all copies. 96f05d20cSespie# 1067d43fbcSespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1167d43fbcSespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1267d43fbcSespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1367d43fbcSespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1467d43fbcSespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1567d43fbcSespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1667d43fbcSespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 176f05d20cSespie 18*039cbdaaSespieuse v5.36; 190fbefeddSespie 206f05d20cSespiepackage OpenBSD::PackageName; 216f05d20cSespie 22*039cbdaaSespiesub url2pkgname($name) 235c18493bSespie{ 245c18493bSespie $name =~ s|.*/||; 255c18493bSespie $name =~ s|\.tgz$||; 265c18493bSespie 275c18493bSespie return $name; 285c18493bSespie} 295c18493bSespie 30f56c88b3Sbernd# see packages-specs(7) 31*039cbdaaSespiesub splitname($n) 326f05d20cSespie{ 33b62674ebSespie if ($n =~ /^(.*?)\-(\d.*)$/o) { 34a7ceec02Sespie my $stem = $1; 35a7ceec02Sespie my $rest = $2; 3603258ab2Sespie my @all = split /\-/o, $rest; 376f05d20cSespie return ($stem, @all); 386f05d20cSespie } else { 39b62674ebSespie return ($n); 406f05d20cSespie } 416f05d20cSespie} 426f05d20cSespie 431f9ffadcSespiemy $cached = {}; 441f9ffadcSespie 45*039cbdaaSespiesub from_string($class, $s) 464a9f97dfSespie{ 47b62674ebSespie return $cached->{$s} //= $class->new_from_string($s); 48516945adSespie} 491f9ffadcSespie 50*039cbdaaSespiesub new_from_string($class, $n) 511f9ffadcSespie{ 52b62674ebSespie if ($n =~ /^(.*?)\-(\d.*)$/o) { 53a7ceec02Sespie my $stem = $1; 54a7ceec02Sespie my $rest = $2; 5503258ab2Sespie my @all = split /\-/o, $rest; 564a9f97dfSespie my $version = OpenBSD::PackageName::version->from_string(shift @all); 574a9f97dfSespie return bless { 584a9f97dfSespie stem => $stem, 594a9f97dfSespie version => $version, 601f9ffadcSespie flavors => { map {($_, 1)} @all }, 614a9f97dfSespie }, "OpenBSD::PackageName::Name"; 624a9f97dfSespie } else { 634a9f97dfSespie return bless { 64b62674ebSespie stem => $n, 654a9f97dfSespie }, "OpenBSD::PackageName::Stem"; 664a9f97dfSespie } 674a9f97dfSespie} 684a9f97dfSespie 69*039cbdaaSespiesub splitstem($s) 70f5d79ea0Sespie{ 71b62674ebSespie if ($s =~ /^(.*?)\-\d/o) { 72a7ceec02Sespie return $1; 734a9f97dfSespie } else { 74b62674ebSespie return $s; 754a9f97dfSespie } 76f5d79ea0Sespie} 77f5d79ea0Sespie 78*039cbdaaSespiesub pkg2stem($pkg) 79699c12dfSespie{ 80*039cbdaaSespie my $s = splitstem($pkg); 81699c12dfSespie $s =~ tr/A-Z/a-z/; 82699c12dfSespie return $s; 83699c12dfSespie 84699c12dfSespie} 85*039cbdaaSespiesub is_stem($s) 86d18f6f74Sespie{ 87b62674ebSespie if ($s =~ m/\-\d/o || $s eq '-') { 88d18f6f74Sespie return 0; 89d18f6f74Sespie } else { 90d18f6f74Sespie return 1; 91d18f6f74Sespie } 92d18f6f74Sespie} 93d18f6f74Sespie 94*039cbdaaSespiesub compile_stemlist(@p) 959b6421f6Sespie{ 969b6421f6Sespie my $hash = {}; 97*039cbdaaSespie for my $n (@p) { 98699c12dfSespie $hash->{pkg2stem($n)}{$n} = 1; 999b6421f6Sespie } 1009b6421f6Sespie bless $hash, "OpenBSD::PackageLocator::_compiled_stemlist"; 1019b6421f6Sespie} 1029b6421f6Sespie 103*039cbdaaSespiesub avail2stems(@p) 104646f71ecSespie{ 105*039cbdaaSespie return compile_stemlist(@p); 106646f71ecSespie} 107646f71ecSespie 1089b6421f6Sespiepackage OpenBSD::PackageLocator::_compiled_stemlist; 1099b6421f6Sespie 110*039cbdaaSespiesub find($self, $stem) 1119b6421f6Sespie{ 112b1986bc3Sespie $stem =~ tr/A-Z/a-z/; 113b28823c4Sespie return keys %{$self->{$stem}}; 1149b6421f6Sespie} 1159b6421f6Sespie 116*039cbdaaSespiesub add($self, $pkgname) 117efa23318Sespie{ 118699c12dfSespie $self->{OpenBSD::PackageName::pkg2stem($pkgname)}{$pkgname} = 1; 119efa23318Sespie} 120efa23318Sespie 121*039cbdaaSespiesub delete($self, $pkgname) 122efa23318Sespie{ 123699c12dfSespie my $stem = OpenBSD::PackageName::pkg2stem($pkgname); 124699c12dfSespie delete $self->{$stem}{$pkgname}; 125efa23318Sespie if(keys %{$self->{$stem}} == 0) { 126efa23318Sespie delete $self->{$stem}; 127efa23318Sespie } 128efa23318Sespie} 129efa23318Sespie 130*039cbdaaSespiesub find_partial($self, $partial) 13134a4c4c6Sespie{ 13234a4c4c6Sespie my @result = (); 13334a4c4c6Sespie while (my ($stem, $pkgs) = each %$self) { 13434a4c4c6Sespie next unless $stem =~ /\Q$partial\E/i; 13534a4c4c6Sespie push(@result, keys %$pkgs); 13634a4c4c6Sespie } 13734a4c4c6Sespie return @result; 13834a4c4c6Sespie} 13934a4c4c6Sespie 140516945adSespiepackage OpenBSD::PackageName::dewey; 1414a9f97dfSespie 142516945adSespiemy $cache = {}; 143516945adSespie 144*039cbdaaSespiesub from_string($class, $string) 1454a9f97dfSespie{ 146e5e97783Sespie my $o = bless { deweys => [ split(/\./o, $string) ], 147e5e97783Sespie suffix => '', suffix_value => 0}, $class; 148044f8b13Sjca if ($o->{deweys}->[-1] =~ m/^(\d+)(rc|alpha|beta|pre|pl)(\d*)$/) { 1494a9f97dfSespie $o->{deweys}->[-1] = $1; 150e5e97783Sespie $o->{suffix} = $2; 151e5e97783Sespie $o->{suffix_value} = $3; 1524a9f97dfSespie } 153516945adSespie return $o; 1544a9f97dfSespie} 1554a9f97dfSespie 156*039cbdaaSespiesub make($class, $string) 1574a9f97dfSespie{ 1587eb3bd1aSespie return $cache->{$string} //= $class->from_string($string); 1594a9f97dfSespie} 1604a9f97dfSespie 161*039cbdaaSespiesub to_string($self) 1624a9f97dfSespie{ 163fcba77b1Sespie my $r = join('.', @{$self->{deweys}}); 164e5e97783Sespie if ($self->{suffix}) { 165e5e97783Sespie $r .= $self->{suffix} . $self->{suffix_value}; 1664a9f97dfSespie } 167516945adSespie return $r; 1680e2c7b07Sespie} 1690e2c7b07Sespie 170*039cbdaaSespiesub suffix_compare($a, $b) 171e5e97783Sespie{ 172e5e97783Sespie if ($a->{suffix} eq $b->{suffix}) { 173e5e97783Sespie return $a->{suffix_value} <=> $b->{suffix_value}; 174e5e97783Sespie } 175e5e97783Sespie if ($a->{suffix} eq 'pl') { 176e5e97783Sespie return 1; 177e5e97783Sespie } 178e5e97783Sespie if ($b->{suffix} eq 'pl') { 179e5e97783Sespie return -1; 180e5e97783Sespie } 181e5e97783Sespie 182e5e97783Sespie if ($a->{suffix} gt $b->{suffix}) { 183e5e97783Sespie return -suffix_compare($b, $a); 184e5e97783Sespie } 185044f8b13Sjca # order is '', alpha, beta, pre, rc 186e5e97783Sespie # we know that a < b, 187e5e97783Sespie if ($a->{suffix} eq '') { 188e5e97783Sespie return 1; 189e5e97783Sespie } 190044f8b13Sjca if ($a->{suffix} eq 'alpha') { 191044f8b13Sjca return -1; 192044f8b13Sjca } 193e5e97783Sespie if ($a->{suffix} eq 'beta') { 194e5e97783Sespie return -1; 195e5e97783Sespie } 196e5e97783Sespie # refuse to compare pre vs. rc 197e5e97783Sespie return 0; 198e5e97783Sespie} 199e5e97783Sespie 200*039cbdaaSespiesub compare($a, $b) 2014a9f97dfSespie{ 2024a9f97dfSespie # Try a diff in dewey numbers first 2034a9f97dfSespie for (my $i = 0; ; $i++) { 2044a9f97dfSespie if (!defined $a->{deweys}->[$i]) { 2054a9f97dfSespie if (!defined $b->{deweys}->[$i]) { 2061daa8d72Sespie last; 2074a9f97dfSespie } else { 2084a9f97dfSespie return -1; 2094a9f97dfSespie } 2104a9f97dfSespie } 2114a9f97dfSespie if (!defined $b->{deweys}->[$i]) { 2124a9f97dfSespie return 1; 2134a9f97dfSespie } 2144a9f97dfSespie my $r = dewey_compare($a->{deweys}->[$i], 2154a9f97dfSespie $b->{deweys}->[$i]); 2164a9f97dfSespie return $r if $r != 0; 2174a9f97dfSespie } 218e5e97783Sespie return suffix_compare($a, $b); 2194a9f97dfSespie} 2204a9f97dfSespie 221*039cbdaaSespiesub dewey_compare($a, $b) 2224a9f97dfSespie{ 2234a9f97dfSespie # numerical comparison 224cc24e6f2Sespie if ($a =~ m/^\d+$/o and $b =~ m/^\d+$/o) { 2254a9f97dfSespie return $a <=> $b; 2264a9f97dfSespie } 2274a9f97dfSespie # added lowercase letter 228cc24e6f2Sespie if ("$a.$b" =~ m/^(\d+)([a-z]?)\.(\d+)([a-z]?)$/o) { 2294a9f97dfSespie my ($an, $al, $bn, $bl) = ($1, $2, $3, $4); 2304a9f97dfSespie if ($an != $bn) { 2314a9f97dfSespie return $an <=> $bn; 2324a9f97dfSespie } else { 2334a9f97dfSespie return $al cmp $bl; 2344a9f97dfSespie } 2354a9f97dfSespie } 2364a9f97dfSespie return $a cmp $b; 2374a9f97dfSespie} 2384a9f97dfSespie 239516945adSespiepackage OpenBSD::PackageName::version; 240516945adSespie 241*039cbdaaSespiesub p($self) 242516945adSespie{ 243516945adSespie return defined $self->{p} ? $self->{p} : -1; 244516945adSespie} 245516945adSespie 246*039cbdaaSespiesub v($self) 247516945adSespie{ 248516945adSespie return defined $self->{v} ? $self->{v} : -1; 249516945adSespie} 250516945adSespie 251*039cbdaaSespiesub from_string($class, $string) 252516945adSespie{ 253516945adSespie my $o = bless {}, $class; 254516945adSespie if ($string =~ m/^(.*)v(\d+)$/o) { 255516945adSespie $o->{v} = $2; 256516945adSespie $string = $1; 257516945adSespie } 258516945adSespie if ($string =~ m/^(.*)p(\d+)$/o) { 259516945adSespie $o->{p} = $2; 260516945adSespie $string = $1; 261516945adSespie } 262516945adSespie $o->{dewey} = OpenBSD::PackageName::dewey->make($string); 263516945adSespie 264516945adSespie return $o; 265516945adSespie} 266516945adSespie 267*039cbdaaSespiesub to_string($o) 268516945adSespie{ 269516945adSespie my $string = $o->{dewey}->to_string; 270516945adSespie if (defined $o->{p}) { 271516945adSespie $string .= 'p'.$o->{p}; 272516945adSespie } 273516945adSespie if (defined $o->{v}) { 274516945adSespie $string .= 'v'.$o->{v}; 275516945adSespie } 276516945adSespie return $string; 277516945adSespie} 278516945adSespie 279*039cbdaaSespiesub pnum_compare($a, $b) 280516945adSespie{ 281516945adSespie return $a->p <=> $b->p; 282516945adSespie} 283516945adSespie 284*039cbdaaSespiesub compare($a, $b) 285516945adSespie{ 286516945adSespie # Simple case: epoch number 287516945adSespie if ($a->v != $b->v) { 288516945adSespie return $a->v <=> $b->v; 289516945adSespie } 290516945adSespie # Simple case: only p number differs 291516945adSespie if ($a->{dewey} eq $b->{dewey}) { 292516945adSespie return $a->pnum_compare($b); 293516945adSespie } 294516945adSespie 295516945adSespie return $a->{dewey}->compare($b->{dewey}); 296516945adSespie} 297516945adSespie 298*039cbdaaSespiesub has_issues($self) 2996fcf288dSespie{ 3006fcf288dSespie if ($self->{dewey}{deweys}[-1] =~ m/v\d+$/ && defined $self->{p}) { 3016fcf288dSespie return ("correct order is pNvM"); 3026fcf288dSespie } else { 3036fcf288dSespie return (); 3046fcf288dSespie } 3056fcf288dSespie} 3066fcf288dSespie 3074a9f97dfSespiepackage OpenBSD::PackageName::Stem; 308*039cbdaaSespiesub to_string($o) 3094a9f97dfSespie{ 3104a9f97dfSespie return $o->{stem}; 3114a9f97dfSespie} 3124a9f97dfSespie 313*039cbdaaSespiesub to_pattern($o) 3144a9f97dfSespie{ 3154a9f97dfSespie return $o->{stem}.'-*'; 3164a9f97dfSespie} 3174a9f97dfSespie 318*039cbdaaSespiesub has_issues($self) 3196fcf288dSespie{ 3206fcf288dSespie return ("is a stem"); 3216fcf288dSespie} 3226fcf288dSespie 32341787c8eSespiepackage OpenBSD::PackageName::Name; 324*039cbdaaSespiesub flavor_string($o) 325e451b54dSespie{ 326e451b54dSespie return join('-', sort keys %{$o->{flavors}}); 327e451b54dSespie} 328e451b54dSespie 329*039cbdaaSespiesub to_string($o) 33041787c8eSespie{ 331e451b54dSespie return join('-', $o->{stem}, $o->{version}->to_string, 332fcba77b1Sespie sort keys %{$o->{flavors}}); 33341787c8eSespie} 33441787c8eSespie 335*039cbdaaSespiesub to_pattern($o) 33641787c8eSespie{ 337e451b54dSespie return join('-', $o->{stem}, '*', $o->flavor_string); 33841787c8eSespie} 33941787c8eSespie 340*039cbdaaSespiesub compare($a, $b) 341e1eeb5e0Sespie{ 342e1eeb5e0Sespie if ($a->{stem} ne $b->{stem} || $a->flavor_string ne $b->flavor_string) { 343e1eeb5e0Sespie return undef; 344e1eeb5e0Sespie } 345e1eeb5e0Sespie return $a->{version}->compare($b->{version}); 346e1eeb5e0Sespie} 347e1eeb5e0Sespie 348*039cbdaaSespiesub has_issues($self) 3496fcf288dSespie{ 3506fcf288dSespie return ((map {"flavor $_ can't start with digit"} 3516fcf288dSespie grep { /^\d/ } keys %{$self->{flavors}}), 3526fcf288dSespie $self->{version}->has_issues); 3536fcf288dSespie} 3546fcf288dSespie 3556f05d20cSespie1; 356