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   278185 use strict;
  524         1567  
  524         21406  
22 524     524   3141 use warnings;
  524         1068  
  524         15734  
23 524     524   2625 use warnings::register qw(semantic_change::overload::bool);
  524         1052  
  524         134779  
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   4190 use Exporter qw(import);
  524         1571  
  524         21099  
42 524     524   3147 use Carp;
  524         1050  
  524         32589  
43              
44 524     524   3667 use Dpkg::Gettext;
  524         570  
  524         33029  
45 524     524   3673 use Dpkg::ErrorHandling;
  524         1049  
  524         45684  
46              
47             use constant {
48 524         101201 REL_LT => '<<',
49             REL_LE => '<=',
50             REL_EQ => '=',
51             REL_GE => '>=',
52             REL_GT => '>>',
53 524     524   3680 };
  524         1047  
54              
55             use overload
56             '<=>' => \&_comparison,
57             'cmp' => \&_comparison,
58 29682     29682   425371 '""' => sub { return $_[0]->as_string(); },
59 1242     1242   4224 'bool' => sub { return $_[0]->is_valid(); },
60 524     524   668856 'fallback' => 1;
  524         513801  
  524         5830  
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 674695     674695 1 21227703 my ($this, $ver, %opts) = @_;
95 674695   33     4369011 my $class = ref($this) || $this;
96 674695 100       1587033 $ver = "$ver" if ref($ver); # Try to stringify objects
97              
98 674695 100       2307497 if ($opts{check}) {
99 331717 50       2072757 return unless version_check($ver);
100             }
101              
102 674695         1571279 my $self = {};
103 674695 100       3457490 if ($ver =~ /^([^:]*):(.+)$/) {
104 82205         570465 $self->{epoch} = $1;
105 82205         485889 $ver = $2;
106             } else {
107 592490         1931340 $self->{epoch} = 0;
108 592490         1180120 $self->{no_epoch} = 1;
109             }
110 674695 100       3636887 if ($ver =~ /(.*)-(.*)$/) {
111 277881         1429766 $self->{version} = $1;
112 277881         1266527 $self->{revision} = $2;
113             } else {
114 396814         1443183 $self->{version} = $ver;
115 396814         792591 $self->{revision} = 0;
116 396814         716186 $self->{no_revision} = 1;
117             }
118              
119 674695         3043858 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 15566 my $self = shift;
148 5945         13404 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 1616534     1616534 1 2687888 my $self = shift;
159 1616534         8165368 return $self->{epoch};
160             }
161              
162             sub version {
163 1569149     1569149 1 3162227 my $self = shift;
164 1569149         9109547 return $self->{version};
165             }
166              
167             sub revision {
168 875118     875118 1 1636441 my $self = shift;
169 875118         4903729 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 9823 my $self = shift;
180 2585         10340 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 303467     303467   1784260 my ($a, $b, $inverted) = @_;
193 303467 100 66     3457335 if (not ref($b) or not $b->isa('Dpkg::Version')) {
194 3683         8406 $b = Dpkg::Version->new($b);
195             }
196 303467 50       898104 ($a, $b) = ($b, $a) if $inverted;
197 303467         829551 my $r = version_compare_part($a->epoch(), $b->epoch());
198 303467 100       1203980 return $r if $r;
199 280033         698287 $r = version_compare_part($a->version(), $b->version());
200 280033 100       2420007 return $r if $r;
201 101715         541437 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 146443 my ($self, %opts) = @_;
228 33906   66     245963 my $no_epoch = $opts{omit_epoch} || $self->{no_epoch};
229 33906   100     136438 my $no_revision = $opts{omit_revision} || $self->{no_revision};
230              
231 33906         86721 my $str = '';
232 33906 100       88342 $str .= $self->{epoch} . ':' unless $no_epoch;
233 33906         79261 $str .= $self->{version};
234 33906 100       110143 $str .= '-' . $self->{revision} unless $no_revision;
235 33906         435729 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 154462     154462 1 782931 my ($a, $b) = @_;
257 154462         3622850 my $va = Dpkg::Version->new($a, check => 1);
258 154462 50       677741 defined($va) || error(g_('%s is not a valid version'), "$a");
259 154462         653120 my $vb = Dpkg::Version->new($b, check => 1);
260 154462 50       800229 defined($vb) || error(g_('%s is not a valid version'), "$b");
261 154462         2463425 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 3118858 my ($a, $op, $b) = @_;
277 133919         1097005 my $res = version_compare($a, $b);
278              
279 133919 100       988197 if ($op eq REL_GT) {
    100          
    100          
    100          
    50          
280 21887         330166 return $res > 0;
281             } elsif ($op eq REL_GE) {
282 33156         393239 return $res >= 0;
283             } elsif ($op eq REL_EQ) {
284 22317         234075 return $res == 0;
285             } elsif ($op eq REL_LE) {
286 33884         336944 return $res <= 0;
287             } elsif ($op eq REL_LT) {
288 22675         310102 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 185539815 my $op = shift;
306              
307 133948 100 100     3434110 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     4990069 if ($op eq '>>' or $op eq 'gt') {
    100 100        
    100 100        
    100 100        
    50 100        
      100        
      66        
311 21891         214997 return REL_GT;
312             } elsif ($op eq '>=' or $op eq 'ge' or $op eq '>') {
313 33178         293806 return REL_GE;
314             } elsif ($op eq '=' or $op eq 'eq') {
315 22327         242722 return REL_EQ;
316             } elsif ($op eq '<=' or $op eq 'le' or $op eq '<') {
317 33888         302225 return REL_LE;
318             } elsif ($op eq '<<' or $op eq 'lt') {
319 22664         204232 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   3180247 my $x = shift;
340              
341 1988034 100       8139643 if ($x eq '~') {
    100          
    100          
342 93846         268548 return -1;
343             } elsif ($x =~ /^\d$/) {
344 50323         250436 return $x * 1 + 1;
345             } elsif ($x =~ /^[A-Za-z]$/) {
346 1467730         3710293 return ord($x);
347             } else {
348 376135         1286681 return ord($x) + 256;
349             }
350             }
351              
352             sub version_compare_string($$) {
353 326811     326811 1 1350241 my @a = map { _version_order($_) } split(//, shift);
  1033414         2115006  
354 326811         1288823 my @b = map { _version_order($_) } split(//, shift);
  954620         1686300  
355 326811         738870 while (1) {
356 1124328         2215565 my ($a, $b) = (shift @a, shift @b);
357 1124328 100 100     3424617 return 0 if not defined($a) and not defined($b);
358 922284   100     1820253 $a ||= 0; # Default order for "no character"
359 922284   100     1943546 $b ||= 0;
360 922284 100       1994028 return 1 if $a > $b;
361 864997 100       1712382 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 685215     685215 1 2253888 my @a = version_split_digits(shift);
378 685215         1503315 my @b = version_split_digits(shift);
379 685215         1218977 while (1) {
380 1621726         3569237 my ($a, $b) = (shift @a, shift @b);
381 1621726 100 100     5320582 return 0 if not defined($a) and not defined($b);
382 1154507   100     4822721 $a ||= 0; # Default value for lack of version
383 1154507   100     4052913 $b ||= 0;
384 1154507 100 100     6139747 if ($a =~ /^\d+$/ and $b =~ /^\d+$/) {
385             # Numerical comparison
386 827696         1667193 my $cmp = $a <=> $b;
387 827696 100       2168040 return $cmp if $cmp;
388             } else {
389             # String comparison
390 326811         1039336 my $cmp = version_compare_string($a, $b);
391 326811 100       1036991 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 1370430     1370430 1 2755766 my $version = shift;
406              
407 1370430         7778727 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 337912     337912 1 1522104 my $version = shift;
422 337912         696894 my $str;
423 337912 50       1075099 if (defined $version) {
424 337912         1073430 $str = "$version";
425 337912 100       3102949 $version = Dpkg::Version->new($str) unless ref($version);
426             }
427 337912 100 66     3711858 if (not defined($str) or not length($str)) {
428 517         2068 my $msg = g_('version number cannot be empty');
429 517 50       2068 return (0, $msg) if wantarray;
430 517         2585 return 0;
431             }
432 337395 100 66     1844144 if (not defined $version->epoch() or not length $version->epoch()) {
433 517         12408 my $msg = sprintf(g_('epoch part of the version number cannot be empty'));
434 517 50       2585 return (0, $msg) if wantarray;
435 517         3619 return 0;
436             }
437 336878 100 66     1166074 if (not defined $version->version() or not length $version->version()) {
438 1034         3102 my $msg = g_('upstream version cannot be empty');
439 1034 50       3102 return (0, $msg) if wantarray;
440 1034         6204 return 0;
441             }
442 335844 100 66     1403674 if (not defined $version->revision() or not length $version->revision()) {
443 517         2068 my $msg = sprintf(g_('revision cannot be empty'));
444 517 50       2585 return (0, $msg) if wantarray;
445 517         3102 return 0;
446             }
447 335327 100       1172779 if ($version->version() =~ m/^[^\d]/) {
448 1034         4653 my $msg = g_('version number does not start with digit');
449 1034 50       3619 return (0, $msg) if wantarray;
450 1034         6204 return 0;
451             }
452 334293 100       1514427 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         2585 return 0;
456             }
457 333776 100       747293 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         13959 return 0;
462             }
463 332742 100       1014529 return (1, '') if wantarray;
464 332492         1434369 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;