File Coverage

blib/lib/Dpkg/Version.pm
Criterion Covered Total %
statement 162 164 98.7
branch 88 102 86.2
condition 56 65 86.1
subroutine 27 27 100.0
pod 14 14 100.0
total 347 372 93.2


line stmt bran cond sub pod time code
1             # Copyright © Colin Watson
2             # Copyright © Ian Jackson
3             # Copyright © 2007 Don Armstrong .
4             # Copyright © 2009 Raphaël Hertzog
5             #
6             # This program is free software; you can redistribute it and/or modify
7             # it under the terms of the GNU General Public License as published by
8             # the Free Software Foundation; either version 2 of the License, or
9             # (at your option) any later version.
10             #
11             # This program is distributed in the hope that it will be useful,
12             # but WITHOUT ANY WARRANTY; without even the implied warranty of
13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14             # GNU General Public License for more details.
15             #
16             # You should have received a copy of the GNU General Public License
17             # along with this program. If not, see .
18              
19             package Dpkg::Version;
20              
21 524     524   282350 use strict;
  524         1056  
  524         22439  
22 524     524   3140 use warnings;
  524         1050  
  524         16335  
23 524     524   3150 use warnings::register qw(semantic_change::overload::bool);
  524         1047  
  524         140417  
24              
25             our $VERSION = '1.03';
26             our @EXPORT = qw(
27             version_compare
28             version_compare_relation
29             version_normalize_relation
30             version_compare_string
31             version_compare_part
32             version_split_digits
33             version_check
34             REL_LT
35             REL_LE
36             REL_EQ
37             REL_GE
38             REL_GT
39             );
40              
41 524     524   3670 use Exporter qw(import);
  524         1048  
  524         20916  
42 524     524   3182 use Carp;
  524         1064  
  524         39775  
43              
44 524     524   3665 use Dpkg::Gettext;
  524         1063  
  524         28856  
45 524     524   3153 use Dpkg::ErrorHandling;
  524         531  
  524         52880  
46              
47             use constant {
48 524         106356 REL_LT => '<<',
49             REL_LE => '<=',
50             REL_EQ => '=',
51             REL_GE => '>=',
52             REL_GT => '>>',
53 524     524   3668 };
  524         1048  
54              
55             use overload
56             '<=>' => \&_comparison,
57             'cmp' => \&_comparison,
58 29682     29682   329349 '""' => sub { return $_[0]->as_string(); },
59 1242     1242   4230 'bool' => sub { return $_[0]->is_valid(); },
60 524     524   670958 'fallback' => 1;
  524         520050  
  524         5829  
61              
62             =encoding utf8
63              
64             =head1 NAME
65              
66             Dpkg::Version - handling and comparing dpkg-style version numbers
67              
68             =head1 DESCRIPTION
69              
70             The Dpkg::Version module provides pure-Perl routines to compare
71             dpkg-style version numbers (as used in Debian packages) and also
72             an object oriented interface overriding perl operators
73             to do the right thing when you compare Dpkg::Version object between
74             them.
75              
76             =head1 METHODS
77              
78             =over 4
79              
80             =item $v = Dpkg::Version->new($version, %opts)
81              
82             Create a new Dpkg::Version object corresponding to the version indicated in
83             the string (scalar) $version. By default it will accepts any string
84             and consider it as a valid version. If you pass the option "check => 1",
85             it will return undef if the version is invalid (see version_check for
86             details).
87              
88             You can always call $v->is_valid() later on to verify that the version is
89             valid.
90              
91             =cut
92              
93             sub new {
94 674703     674703 1 22431679 my ($this, $ver, %opts) = @_;
95 674703   33     4192525 my $class = ref($this) || $this;
96 674703 100       1610105 $ver = "$ver" if ref($ver); # Try to stringify objects
97              
98 674703 100       2298469 if ($opts{check}) {
99 331721 50       2007821 return unless version_check($ver);
100             }
101              
102 674703         1645982 my $self = {};
103 674703 100       3173666 if ($ver =~ /^([^:]*):(.+)$/) {
104 82205         529383 $self->{epoch} = $1;
105 82205         459801 $ver = $2;
106             } else {
107 592498         1890766 $self->{epoch} = 0;
108 592498         1837126 $self->{no_epoch} = 1;
109             }
110 674703 100       3448657 if ($ver =~ /(.*)-(.*)$/) {
111 277881         1388836 $self->{version} = $1;
112 277881         1393498 $self->{revision} = $2;
113             } else {
114 396822         1398043 $self->{version} = $ver;
115 396822         1177642 $self->{revision} = 0;
116 396822         774770 $self->{no_revision} = 1;
117             }
118              
119 674703         2945011 return bless $self, $class;
120             }
121              
122             =item boolean evaluation
123              
124             When the Dpkg::Version object is used in a boolean evaluation (for example
125             in "if ($v)" or "$v ? \"$v\" : 'default'") it returns true if the version
126             stored is valid ($v->is_valid()) and false otherwise.
127              
128             B: Between dpkg 1.15.7.2 and 1.19.1 this overload used to return
129             $v->as_string() if $v->is_valid(), a breaking change in behavior that caused
130             "0" versions to be evaluated as false. To catch any possibly intended code
131             that relied on those semantics, this overload will emit a warning with
132             category "Dpkg::Version::semantic_change::overload::bool" until dpkg 1.20.x.
133             Once fixed, or for already valid code the warning can be quiesced with
134              
135             no if $Dpkg::Version::VERSION ge '1.02',
136             warnings => qw(Dpkg::Version::semantic_change::overload::bool);
137              
138             added after the C.
139              
140             =item $v->is_valid()
141              
142             Returns true if the version is valid, false otherwise.
143              
144             =cut
145              
146             sub is_valid {
147 5945     5945 1 14630 my $self = shift;
148 5945         12860 return scalar version_check($self);
149             }
150              
151             =item $v->epoch(), $v->version(), $v->revision()
152              
153             Returns the corresponding part of the full version string.
154              
155             =cut
156              
157             sub epoch {
158 1616550     1616550 1 2637578 my $self = shift;
159 1616550         8780418 return $self->{epoch};
160             }
161              
162             sub version {
163 1569165     1569165 1 3177876 my $self = shift;
164 1569165         9031486 return $self->{version};
165             }
166              
167             sub revision {
168 875126     875126 1 1637877 my $self = shift;
169 875126         5178284 return $self->{revision};
170             }
171              
172             =item $v->is_native()
173              
174             Returns true if the version is native, false if it has a revision.
175              
176             =cut
177              
178             sub is_native {
179 2585     2585 1 12925 my $self = shift;
180 2585         10857 return $self->{no_revision};
181             }
182              
183             =item $v1 <=> $v2, $v1 < $v2, $v1 <= $v2, $v1 > $v2, $v1 >= $v2
184              
185             Numerical comparison of various versions numbers. One of the two operands
186             needs to be a Dpkg::Version, the other one can be anything provided that
187             its string representation is a version number.
188              
189             =cut
190              
191             sub _comparison {
192 303469     303469   1130659 my ($a, $b, $inverted) = @_;
193 303469 100 66     3469944 if (not ref($b) or not $b->isa('Dpkg::Version')) {
194 3683         7879 $b = Dpkg::Version->new($b);
195             }
196 303469 50       1057104 ($a, $b) = ($b, $a) if $inverted;
197 303469         764806 my $r = version_compare_part($a->epoch(), $b->epoch());
198 303469 100       990210 return $r if $r;
199 280035         790154 $r = version_compare_part($a->version(), $b->version());
200 280035 100       2674321 return $r if $r;
201 101715         517977 return version_compare_part($a->revision(), $b->revision());
202             }
203              
204             =item "$v", $v->as_string(), $v->as_string(%options)
205              
206             Accepts an optional option hash reference, affecting the string conversion.
207              
208             Options:
209              
210             =over 8
211              
212             =item omit_epoch (defaults to 0)
213              
214             Omit the epoch, if present, in the output string.
215              
216             =item omit_revision (defaults to 0)
217              
218             Omit the revision, if present, in the output string.
219              
220             =back
221              
222             Returns the string representation of the version number.
223              
224             =cut
225              
226             sub as_string {
227 33906     33906 1 185854 my ($self, %opts) = @_;
228 33906   66     298201 my $no_epoch = $opts{omit_epoch} || $self->{no_epoch};
229 33906   100     136769 my $no_revision = $opts{omit_revision} || $self->{no_revision};
230              
231 33906         82784 my $str = '';
232 33906 100       102111 $str .= $self->{epoch} . ':' unless $no_epoch;
233 33906         85718 $str .= $self->{version};
234 33906 100       83534 $str .= '-' . $self->{revision} unless $no_revision;
235 33906         316705 return $str;
236             }
237              
238             =back
239              
240             =head1 FUNCTIONS
241              
242             All the functions are exported by default.
243              
244             =over 4
245              
246             =item version_compare($a, $b)
247              
248             Returns -1 if $a is earlier than $b, 0 if they are equal and 1 if $a
249             is later than $b.
250              
251             If $a or $b are not valid version numbers, it dies with an error.
252              
253             =cut
254              
255             sub version_compare($$) {
256 154464     154464 1 760937 my ($a, $b) = @_;
257 154464         3288660 my $va = Dpkg::Version->new($a, check => 1);
258 154464 50       602689 defined($va) || error(g_('%s is not a valid version'), "$a");
259 154464         462079 my $vb = Dpkg::Version->new($b, check => 1);
260 154464 50       599691 defined($vb) || error(g_('%s is not a valid version'), "$b");
261 154464         1426331 return $va <=> $vb;
262             }
263              
264             =item version_compare_relation($a, $rel, $b)
265              
266             Returns the result (0 or 1) of the given comparison operation. This
267             function is implemented on top of version_compare().
268              
269             Allowed values for $rel are the exported constants REL_GT, REL_GE,
270             REL_EQ, REL_LE, REL_LT. Use version_normalize_relation() if you
271             have an input string containing the operator.
272              
273             =cut
274              
275             sub version_compare_relation($$$) {
276 133919     133919 1 3024129 my ($a, $op, $b) = @_;
277 133919         1002252 my $res = version_compare($a, $b);
278              
279 133919 100       936432 if ($op eq REL_GT) {
    100          
    100          
    100          
    50          
280 21887         256353 return $res > 0;
281             } elsif ($op eq REL_GE) {
282 33156         340704 return $res >= 0;
283             } elsif ($op eq REL_EQ) {
284 22317         248118 return $res == 0;
285             } elsif ($op eq REL_LE) {
286 33884         381632 return $res <= 0;
287             } elsif ($op eq REL_LT) {
288 22675         238487 return $res < 0;
289             } else {
290 0         0 croak "unsupported relation for version_compare_relation(): '$op'";
291             }
292             }
293              
294             =item $rel = version_normalize_relation($rel_string)
295              
296             Returns the normalized constant of the relation $rel (a value
297             among REL_GT, REL_GE, REL_EQ, REL_LE and REL_LT). Supported
298             relations names in input are: "gt", "ge", "eq", "le", "lt", ">>", ">=",
299             "=", "<=", "<<". ">" and "<" are also supported but should not be used as
300             they are obsolete aliases of ">=" and "<=".
301              
302             =cut
303              
304             sub version_normalize_relation($) {
305 133948     133948 1 187021987 my $op = shift;
306              
307 133948 100 100     2743215 warning('relation %s is deprecated: use %s or %s',
308             $op, "$op$op", "$op=") if ($op eq '>' or $op eq '<');
309              
310 133948 100 100     5457328 if ($op eq '>>' or $op eq 'gt') {
    100 100        
    100 100        
    100 100        
    50 100        
      100        
      66        
311 21891         267376 return REL_GT;
312             } elsif ($op eq '>=' or $op eq 'ge' or $op eq '>') {
313 33178         189593 return REL_GE;
314             } elsif ($op eq '=' or $op eq 'eq') {
315 22327         142305 return REL_EQ;
316             } elsif ($op eq '<=' or $op eq 'le' or $op eq '<') {
317 33888         262860 return REL_LE;
318             } elsif ($op eq '<<' or $op eq 'lt') {
319 22664         193050 return REL_LT;
320             } else {
321 0         0 croak "bad relation '$op'";
322             }
323             }
324              
325             =item version_compare_string($a, $b)
326              
327             String comparison function used for comparing non-numerical parts of version
328             numbers. Returns -1 if $a is earlier than $b, 0 if they are equal and 1 if $a
329             is later than $b.
330              
331             The "~" character always sort lower than anything else. Digits sort lower
332             than non-digits. Among remaining characters alphabetic characters (A-Z, a-z)
333             sort lower than the other ones. Within each range, the ASCII decimal value
334             of the character is used to sort between characters.
335              
336             =cut
337              
338             sub _version_order {
339 1988034     1988034   2990299 my $x = shift;
340              
341 1988034 100       7171533 if ($x eq '~') {
    100          
    100          
342 93846         261436 return -1;
343             } elsif ($x =~ /^\d$/) {
344 50323         223343 return $x * 1 + 1;
345             } elsif ($x =~ /^[A-Za-z]$/) {
346 1467730         3290613 return ord($x);
347             } else {
348 376135         1134971 return ord($x) + 256;
349             }
350             }
351              
352             sub version_compare_string($$) {
353 326811     326811 1 1253429 my @a = map { _version_order($_) } split(//, shift);
  1033414         1814936  
354 326811         1081745 my @b = map { _version_order($_) } split(//, shift);
  954620         1467859  
355 326811         669246 while (1) {
356 1124328         1981596 my ($a, $b) = (shift @a, shift @b);
357 1124328 100 100     3070780 return 0 if not defined($a) and not defined($b);
358 922284   100     1818872 $a ||= 0; # Default order for "no character"
359 922284   100     1914653 $b ||= 0;
360 922284 100       1819091 return 1 if $a > $b;
361 864997 100       1640388 return -1 if $a < $b;
362             }
363             }
364              
365             =item version_compare_part($a, $b)
366              
367             Compare two corresponding sub-parts of a version number (either upstream
368             version or debian revision).
369              
370             Each parameter is split by version_split_digits() and resulting items
371             are compared together. As soon as a difference happens, it returns -1 if
372             $a is earlier than $b, 0 if they are equal and 1 if $a is later than $b.
373              
374             =cut
375              
376             sub version_compare_part($$) {
377 685219     685219 1 1687723 my @a = version_split_digits(shift);
378 685219         1584758 my @b = version_split_digits(shift);
379 685219         1375783 while (1) {
380 1621732         3534500 my ($a, $b) = (shift @a, shift @b);
381 1621732 100 100     5142891 return 0 if not defined($a) and not defined($b);
382 1154511   100     4279743 $a ||= 0; # Default value for lack of version
383 1154511   100     3914027 $b ||= 0;
384 1154511 100 100     6531463 if ($a =~ /^\d+$/ and $b =~ /^\d+$/) {
385             # Numerical comparison
386 827700         1684868 my $cmp = $a <=> $b;
387 827700 100       2025949 return $cmp if $cmp;
388             } else {
389             # String comparison
390 326811         1156602 my $cmp = version_compare_string($a, $b);
391 326811 100       1080386 return $cmp if $cmp;
392             }
393             }
394             }
395              
396             =item @items = version_split_digits($version)
397              
398             Splits a string in items that are each entirely composed either
399             of digits or of non-digits. For instance for "1.024~beta1+svn234" it would
400             return ("1", ".", "024", "~beta", "1", "+svn", "234").
401              
402             =cut
403              
404             sub version_split_digits($) {
405 1370438     1370438 1 2505499 my $version = shift;
406              
407 1370438         8287189 return split /(?<=\d)(?=\D)|(?<=\D)(?=\d)/, $version;
408             }
409              
410             =item ($ok, $msg) = version_check($version)
411              
412             =item $ok = version_check($version)
413              
414             Checks the validity of $version as a version number. Returns 1 in $ok
415             if the version is valid, 0 otherwise. In the latter case, $msg
416             contains a description of the problem with the $version scalar.
417              
418             =cut
419              
420             sub version_check($) {
421 337916     337916 1 1355889 my $version = shift;
422 337916         764681 my $str;
423 337916 50       978488 if (defined $version) {
424 337916         2001710 $str = "$version";
425 337916 100       1559826 $version = Dpkg::Version->new($str) unless ref($version);
426             }
427 337916 100 66     3689362 if (not defined($str) or not length($str)) {
428 517         2585 my $msg = g_('version number cannot be empty');
429 517 50       2585 return (0, $msg) if wantarray;
430 517         2585 return 0;
431             }
432 337399 100 66     1943525 if (not defined $version->epoch() or not length $version->epoch()) {
433 517         11891 my $msg = sprintf(g_('epoch part of the version number cannot be empty'));
434 517 50       3102 return (0, $msg) if wantarray;
435 517         3102 return 0;
436             }
437 336882 100 66     1151892 if (not defined $version->version() or not length $version->version()) {
438 1034         4136 my $msg = g_('upstream version cannot be empty');
439 1034 50       4136 return (0, $msg) if wantarray;
440 1034         6204 return 0;
441             }
442 335848 100 66     1287865 if (not defined $version->revision() or not length $version->revision()) {
443 517         2585 my $msg = sprintf(g_('revision cannot be empty'));
444 517 50       2068 return (0, $msg) if wantarray;
445 517         3102 return 0;
446             }
447 335331 100       1141977 if ($version->version() =~ m/^[^\d]/) {
448 1034         3619 my $msg = g_('version number does not start with digit');
449 1034 50       3619 return (0, $msg) if wantarray;
450 1034         13442 return 0;
451             }
452 334297 100       1614111 if ($str =~ m/([^-+:.0-9a-zA-Z~])/o) {
453 517         2585 my $msg = sprintf g_("version number contains illegal character '%s'"), $1;
454 517 50       3102 return (0, $msg) if wantarray;
455 517         2068 return 0;
456             }
457 333780 100       763033 if ($version->epoch() !~ /^\d*$/) {
458 1034         3619 my $msg = sprintf(g_('epoch part of the version number ' .
459             "is not a number: '%s'"), $version->epoch());
460 1034 50       3102 return (0, $msg) if wantarray;
461 1034         5687 return 0;
462             }
463 332746 100       996020 return (1, '') if wantarray;
464 332496         1277476 return 1;
465             }
466              
467             =back
468              
469             =head1 CHANGES
470              
471             =head2 Version 1.03 (dpkg 1.20.0)
472              
473             Remove deprecation warning from semantic change in 1.02.
474              
475             =head2 Version 1.02 (dpkg 1.19.1)
476              
477             Semantic change: bool evaluation semantics restored to their original behavior.
478              
479             =head2 Version 1.01 (dpkg 1.17.0)
480              
481             New argument: Accept an options argument in $v->as_string().
482              
483             New method: $v->is_native().
484              
485             =head2 Version 1.00 (dpkg 1.15.6)
486              
487             Mark the module as public.
488              
489             =cut
490              
491             1;