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   295672 use strict;
  524         1049  
  524         21400  
22 524     524   3147 use warnings;
  524         1046  
  524         15739  
23 524     524   3139 use warnings::register qw(semantic_change::overload::bool);
  524         1050  
  524         132603  
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   3185 use Exporter qw(import);
  524         1051  
  524         18872  
42 524     524   3142 use Carp;
  524         1566  
  524         33056  
43              
44 524     524   3664 use Dpkg::Gettext;
  524         1073  
  524         33997  
45 524     524   3160 use Dpkg::ErrorHandling;
  524         1057  
  524         48253  
46              
47             use constant {
48 524         103195 REL_LT => '<<',
49             REL_LE => '<=',
50             REL_EQ => '=',
51             REL_GE => '>=',
52             REL_GT => '>>',
53 524     524   4186 };
  524         5198  
54              
55             use overload
56             '<=>' => \&_comparison,
57             'cmp' => \&_comparison,
58 29682     29682   397588 '""' => sub { return $_[0]->as_string(); },
59 1242     1242   3697 'bool' => sub { return $_[0]->is_valid(); },
60 524     524   674728 'fallback' => 1;
  524         520353  
  524         5316  
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 21473191 my ($this, $ver, %opts) = @_;
95 674695   33     4466918 my $class = ref($this) || $this;
96 674695 100       1910812 $ver = "$ver" if ref($ver); # Try to stringify objects
97              
98 674695 100       2281942 if ($opts{check}) {
99 331717 50       1700462 return unless version_check($ver);
100             }
101              
102 674695         1384425 my $self = {};
103 674695 100       3220995 if ($ver =~ /^([^:]*):(.+)$/) {
104 82205         561233 $self->{epoch} = $1;
105 82205         483574 $ver = $2;
106             } else {
107 592490         1772909 $self->{epoch} = 0;
108 592490         1929246 $self->{no_epoch} = 1;
109             }
110 674695 100       3196752 if ($ver =~ /(.*)-(.*)$/) {
111 277881         1437594 $self->{version} = $1;
112 277881         1372855 $self->{revision} = $2;
113             } else {
114 396814         1257266 $self->{version} = $ver;
115 396814         1230080 $self->{revision} = 0;
116 396814         763840 $self->{no_revision} = 1;
117             }
118              
119 674695         2750254 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 15555 my $self = shift;
148 5945         13302 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 2655687 my $self = shift;
159 1616534         8557799 return $self->{epoch};
160             }
161              
162             sub version {
163 1569149     1569149 1 2989448 my $self = shift;
164 1569149         8635119 return $self->{version};
165             }
166              
167             sub revision {
168 875118     875118 1 1401366 my $self = shift;
169 875118         4472480 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 11374 my $self = shift;
180 2585         9823 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   1110701 my ($a, $b, $inverted) = @_;
193 303467 100 66     3367661 if (not ref($b) or not $b->isa('Dpkg::Version')) {
194 3683         6845 $b = Dpkg::Version->new($b);
195             }
196 303467 50       906939 ($a, $b) = ($b, $a) if $inverted;
197 303467         785116 my $r = version_compare_part($a->epoch(), $b->epoch());
198 303467 100       935779 return $r if $r;
199 280033         689478 $r = version_compare_part($a->version(), $b->version());
200 280033 100       1790359 return $r if $r;
201 101715         550412 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 185317 my ($self, %opts) = @_;
228 33906   66     249376 my $no_epoch = $opts{omit_epoch} || $self->{no_epoch};
229 33906   100     144845 my $no_revision = $opts{omit_revision} || $self->{no_revision};
230              
231 33906         81345 my $str = '';
232 33906 100       84660 $str .= $self->{epoch} . ':' unless $no_epoch;
233 33906         70785 $str .= $self->{version};
234 33906 100       87957 $str .= '-' . $self->{revision} unless $no_revision;
235 33906         351858 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 908969 my ($a, $b) = @_;
257 154462         3643834 my $va = Dpkg::Version->new($a, check => 1);
258 154462 50       582923 defined($va) || error(g_('%s is not a valid version'), "$a");
259 154462         480946 my $vb = Dpkg::Version->new($b, check => 1);
260 154462 50       630113 defined($vb) || error(g_('%s is not a valid version'), "$b");
261 154462         1621552 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 3161060 my ($a, $op, $b) = @_;
277 133919         1152766 my $res = version_compare($a, $b);
278              
279 133919 100       893790 if ($op eq REL_GT) {
    100          
    100          
    100          
    50          
280 21887         287172 return $res > 0;
281             } elsif ($op eq REL_GE) {
282 33156         418300 return $res >= 0;
283             } elsif ($op eq REL_EQ) {
284 22317         250745 return $res == 0;
285             } elsif ($op eq REL_LE) {
286 33884         357687 return $res <= 0;
287             } elsif ($op eq REL_LT) {
288 22675         311005 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 177003125 my $op = shift;
306              
307 133948 100 100     2911745 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     4948380 if ($op eq '>>' or $op eq 'gt') {
    100 100        
    100 100        
    100 100        
    50 100        
      100        
      66        
311 21891         236259 return REL_GT;
312             } elsif ($op eq '>=' or $op eq 'ge' or $op eq '>') {
313 33178         247915 return REL_GE;
314             } elsif ($op eq '=' or $op eq 'eq') {
315 22327         211419 return REL_EQ;
316             } elsif ($op eq '<=' or $op eq 'le' or $op eq '<') {
317 33888         291013 return REL_LE;
318             } elsif ($op eq '<<' or $op eq 'lt') {
319 22664         180421 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 1988030     1988030   2885673 my $x = shift;
340              
341 1988030 100       7551965 if ($x eq '~') {
    100          
    100          
342 93846         284807 return -1;
343             } elsif ($x =~ /^\d$/) {
344 50323         238593 return $x * 1 + 1;
345             } elsif ($x =~ /^[A-Za-z]$/) {
346 1467730         3264993 return ord($x);
347             } else {
348 376131         1167556 return ord($x) + 256;
349             }
350             }
351              
352             sub version_compare_string($$) {
353 326809     326809 1 1293930 my @a = map { _version_order($_) } split(//, shift);
  1033412         1778378  
354 326809         1149608 my @b = map { _version_order($_) } split(//, shift);
  954618         1377320  
355 326809         698017 while (1) {
356 1124324         1900068 my ($a, $b) = (shift @a, shift @b);
357 1124324 100 100     2965182 return 0 if not defined($a) and not defined($b);
358 922282   100     1633957 $a ||= 0; # Default order for "no character"
359 922282   100     1738603 $b ||= 0;
360 922282 100       1894091 return 1 if $a > $b;
361 864997 100       1657914 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 1528418 my @a = version_split_digits(shift);
378 685215         1536325 my @b = version_split_digits(shift);
379 685215         1253162 while (1) {
380 1621722         3484376 my ($a, $b) = (shift @a, shift @b);
381 1621722 100 100     5007825 return 0 if not defined($a) and not defined($b);
382 1154503   100     4335251 $a ||= 0; # Default value for lack of version
383 1154503   100     3094707 $b ||= 0;
384 1154503 100 100     7042552 if ($a =~ /^\d+$/ and $b =~ /^\d+$/) {
385             # Numerical comparison
386 827694         1696543 my $cmp = $a <=> $b;
387 827694 100       2016068 return $cmp if $cmp;
388             } else {
389             # String comparison
390 326809         1053109 my $cmp = version_compare_string($a, $b);
391 326809 100       1072312 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 2759288 my $version = shift;
406              
407 1370430         7507952 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 998133 my $version = shift;
422 337912         584576 my $str;
423 337912 50       991933 if (defined $version) {
424 337912         935684 $str = "$version";
425 337912 100       2427392 $version = Dpkg::Version->new($str) unless ref($version);
426             }
427 337912 100 66     2794693 if (not defined($str) or not length($str)) {
428 517         2068 my $msg = g_('version number cannot be empty');
429 517 50       1551 return (0, $msg) if wantarray;
430 517         2585 return 0;
431             }
432 337395 100 66     2689537 if (not defined $version->epoch() or not length $version->epoch()) {
433 517         11374 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     920459 if (not defined $version->version() or not length $version->version()) {
438 1034         4136 my $msg = g_('upstream version cannot be empty');
439 1034 50       3102 return (0, $msg) if wantarray;
440 1034         5687 return 0;
441             }
442 335844 100 66     1223393 if (not defined $version->revision() or not length $version->revision()) {
443 517         3619 my $msg = sprintf(g_('revision cannot be empty'));
444 517 50       3102 return (0, $msg) if wantarray;
445 517         2585 return 0;
446             }
447 335327 100       1251733 if ($version->version() =~ m/^[^\d]/) {
448 1034         4653 my $msg = g_('version number does not start with digit');
449 1034 50       3102 return (0, $msg) if wantarray;
450 1034         5687 return 0;
451             }
452 334293 100       1330208 if ($str =~ m/([^-+:.0-9a-zA-Z~])/o) {
453 517         3619 my $msg = sprintf g_("version number contains illegal character '%s'"), $1;
454 517 50       2585 return (0, $msg) if wantarray;
455 517         3102 return 0;
456             }
457 333776 100       726556 if ($version->epoch() !~ /^\d*$/) {
458 1034         4653 my $msg = sprintf(g_('epoch part of the version number ' .
459             "is not a number: '%s'"), $version->epoch());
460 1034 50       3619 return (0, $msg) if wantarray;
461 1034         13442 return 0;
462             }
463 332742 100       1097165 return (1, '') if wantarray;
464 332492         1379198 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;