1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
|
# Copyright (C) 1998,2002 Julian Gilbey <jdg@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# For a copy of the GNU General Public License write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# The functions in this Perl module are versort and deb_versort. They
# each take as input an array of elements of the form [version, data, ...]
# and sort them into decreasing order according to dpkg's
# understanding of version sorting. The output is a sorted array. In
# versort, "version" is assumed to be an upstream version number only,
# whereas in deb_versort, "version" is assumed to be a Debian version
# number, possibly including an epoch and/or a Debian revision.
#
# The returned array has the greatest version as the 0th array element.
package Devscripts::Versort;
sub versort (@)
{
my @namever_pairs = @_;
my @sorted = sort { _vercmp($$a[0], $$b[0]) } @namever_pairs;
return reverse @sorted;
}
sub _vercmp {
my ($v1, $v2) = @_;
return 0 if $v1 eq $v2;
# assume dpkg works - not really worth checking every single call here
return -1 if system("dpkg", "--compare-versions", $v1, "lt", $v2) == 0;
return 1;
}
1;
__END__
# This was the old version. It didn't handle ~, incidentally
# The sorting order of upstream version numbers is described in
# chapter 4 of the Debian Policy Manual:
#
# The strings are compared from left to right.
#
# First the initial part of each string consisting entirely of non-digit
# characters is determined. These two parts (one of which may be empty)
# are compared lexically. If a difference is found it is returned. The
# lexical comparison is a comparison of ASCII values modified so that
# all the letters sort earlier than all the non-letters.
#
# Then the initial part of the remainder of each string which consists
# entirely of digit characters is determined. The numerical values of
# these two parts are compared, and any difference found is returned as
# the result of the comparison. For these purposes an empty string
# (which can only occur at the end of one or both version strings being
# compared) counts as zero.
#
# These two steps are repeated (chopping initial non-digit strings and
# initial digit strings off from the start) until a difference is found
# or both strings are exhausted.
#
# The program works like this. In order that letters (defined by the
# isalpha() function) sort before non-letters, we initially modify all
# the strings as follows: every letter x is replaced by ax, every
# non-alphanumeric x is replaced by bx, and digits are left
# untouched. In this way, the letters will all sort before
# non-letters in alphabetical sorting. At the end, we simply undo the
# changes. We use "no locale" since the dpkg program clears the locale
# before comparing things (or at least it should do), so we should do
# the same in order to match it. We must also use [A-Za-z] rather than
# \w to distinguish between letters and digits.
#
# We read all of the strings into an array. We then split each string
# by blocks of digits, and sort this array of arrays. In this way, we
# need only perform the splitting etc. once. We also note that some
# of our strings might end in a digit, in which case the split array
# would not have the null non-digit string at the end. We make our
# lives easier by insisting that they do. Thus we will always have an
# array of the form ("\D*","\d+","\D+","\d+",...,"\d+","\D*"), that
# is, it has odd length, always ending with a non-digit string.
no locale;
sub versort (@)
{
my @namever_pairs = @_;
foreach my $pair (@namever_pairs) {
my $ver = $$pair[0];
$ver =~ s/([A-Za-z])/a$1/g;
$ver =~ s/([^A-Za-z0-9])/b$1/g;
my @split_ver = split /(\d+)/, $ver, -1;
unshift @$pair, \@split_ver;
}
@namever_pairs = sort _vercmp @namever_pairs;
foreach my $pair (@namever_pairs) {
shift @$pair;
}
return reverse @namever_pairs;
}
# The following subroutine compares two split strings, passed within
# references to anonymous arrays, $a and $b. We remember that we must
# not alter the things $a and $b refer to. We also remember that the
# arrays @{$$a[0]} and @{$$b[0]} will always have an odd length as
# explained above.
sub _vercmp {
$vera=$$a[0];
$verb=$$b[0];
$lengtha = @$vera;
$lengthb = @$verb;
$i=0;
for (;;) {
$nondiga = $vera->[$i];
$nondigb = $verb->[$i];
if ($nondiga lt $nondigb) { return -1; }
if ($nondiga gt $nondigb) { return +1; }
$i++;
if ($lengtha == $i) { # Nothing left in array @$vera
if ($lengthb == $i) { return 0; } # @$vera = @$verb
else { return -1; } # @$vera is an initial part of @$verb
}
elsif ($lengthb == $i) { return +1; } # vice versa
# Now for the next term, which is a numeric part
if ( $vera->[$i] < $verb->[$i] ) { return -1; }
if ( $vera->[$i] > $verb->[$i] ) { return +1; }
$i++;
}
}
# Now the Debian variants
sub deb_versort (@)
{
my @namever_pairs = @_;
foreach my $pair (@namever_pairs) {
my ($ver, $epoch, $rev);
$ver = $$pair[0];
if ($ver =~ s/^(\d+)://) { $epoch = $1; } else { $epoch = 0; }
if ($ver =~ s/-([^-]+)$//) { $rev = $1; } else { $rev = ''; }
$ver =~ s/([A-Za-z])/a$1/g;
$ver =~ s/([^A-Za-z0-9])/b$1/g;
$rev =~ s/([A-Za-z])/a$1/g;
$rev =~ s/([^A-Za-z0-9])/b$1/g;
my @split_ver = split /(\d+)/, $ver, -1;
my @split_rev = split /(\d+)/, $rev, -1;
unshift @$pair, $epoch, \@split_ver, \@split_rev;
}
@namever_pairs = sort _deb_vercmp @namever_pairs;
# Undo the unshifts
foreach my $pair (@namever_pairs) {
shift @$pair;
shift @$pair;
shift @$pair;
}
return reverse @namever_pairs;
}
# The following subroutine compares two Debian version numbers in
# split strings format, passed within references to anonymous arrays,
# $a and $b, as above. We remember that we must not alter the things
# $a and $b refer to. We also remember that the arrays @{$$a[1,2]}
# and @{$$b[1,2]} (using sloppy notation ;-) will always have an odd
# length as explained above.
sub _deb_vercmp {
$epocha=$$a[0];
$epochb=$$b[0];
$vera=$$a[1];
$verb=$$b[1];
$reva=$$a[2];
$revb=$$b[2];
# epochs first
if ( $epocha < $epochb ) { return -1; }
if ( $epocha > $epochb ) { return +1; }
# if we're still going, the epochs are the same, so we now handle
# the upstream version numbers
$lengtha = @$vera;
$lengthb = @$verb;
$i=0;
for (;;) {
$nondiga = $vera->[$i];
$nondigb = $verb->[$i];
if ($nondiga lt $nondigb) { return -1; }
if ($nondiga gt $nondigb) { return +1; }
$i++;
if ($lengtha == $i) { # Nothing left in array @$vera
if ($lengthb == $i) { last; } # @$vera = @$verb
else { return -1; } # @$vera is an initial part of @$verb
}
elsif ($lengthb == $i) { return +1; } # vice versa
# Now for the next term, which is a numeric part
if ( $vera->[$i] < $verb->[$i] ) { return -1; }
if ( $vera->[$i] > $verb->[$i] ) { return +1; }
$i++;
}
# if we're still going, the upstream version numbers are the same,
# so we now handle the Debian revision numbers
$lengtha = @$reva;
$lengthb = @$revb;
if ($lengtha == 0 && $lengthb == 0) {
return 0; # both lack Debian versions - #236344
}
$i=0;
for (;;) {
$nondiga = $reva->[$i];
$nondigb = $revb->[$i];
if ($nondiga lt $nondigb) { return -1; }
if ($nondiga gt $nondigb) { return +1; }
$i++;
if ($lengtha == $i) { # Nothing left in array @$reva
if ($lengthb == $i) { return 0; } # @$reva = @$revb
else { return -1; } # @$reva is an initial part of @$revb
}
elsif ($lengthb == $i) { return +1; } # vice versa
# Now for the next term, which is a numeric part
if ( $reva->[$i] < $revb->[$i] ) { return -1; }
if ( $reva->[$i] > $revb->[$i] ) { return +1; }
$i++;
}
}
1;
|