Version.pm 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. # Copyright © Colin Watson <cjwatson@debian.org>
  2. # Copyright © Ian Jackson <iwj@debian.org>
  3. # Copyright © 2007 Don Armstrong <don@donarmstrong.com>.
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. # You should have received a copy of the GNU General Public License along
  13. # with this program; if not, write to the Free Software Foundation, Inc.,
  14. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. package Dpkg::Version;
  16. use strict;
  17. use warnings;
  18. use Dpkg::ErrorHandling;
  19. use Dpkg::Gettext;
  20. use Exporter;
  21. our @ISA = qw(Exporter);
  22. our @EXPORT_OK = qw(vercmp compare_versions check_version parseversion);
  23. =head1 NAME
  24. Dpkg::Version - pure-Perl dpkg-style version comparison
  25. =head1 DESCRIPTION
  26. The Dpkg::Version module provides pure-Perl routines to compare
  27. dpkg-style version numbers, as used in Debian packages. If you have the
  28. libapt-pkg Perl bindings available (Debian package libapt-pkg-perl), they
  29. may offer better performance.
  30. =head1 METHODS
  31. =over 8
  32. =cut
  33. sub parseversion ($)
  34. {
  35. my $ver = shift;
  36. my %verhash;
  37. if ($ver =~ /:/)
  38. {
  39. $ver =~ /^(\d+):(.+)/ or die "bad version number '$ver'";
  40. $verhash{epoch} = $1;
  41. $ver = $2;
  42. }
  43. else
  44. {
  45. $verhash{epoch} = 0;
  46. }
  47. if ($ver =~ /(.+)-(.*)$/)
  48. {
  49. $verhash{version} = $1;
  50. $verhash{revision} = $2;
  51. }
  52. else
  53. {
  54. $verhash{version} = $ver;
  55. $verhash{revision} = 0;
  56. }
  57. return %verhash;
  58. }
  59. # verrevcmp
  60. # This function is almost exactly equivalent
  61. # to dpkg's verrevcmp function, including the
  62. # order subroutine which it uses.
  63. sub verrevcmp($$)
  64. {
  65. sub order{
  66. my ($x) = @_;
  67. ##define order(x) ((x) == '~' ? -1 \
  68. # : cisdigit((x)) ? 0 \
  69. # : !(x) ? 0 \
  70. # : cisalpha((x)) ? (x) \
  71. # : (x) + 256)
  72. # This comparison is out of dpkg's order to avoid
  73. # comparing things to undef and triggering warnings.
  74. if (not defined $x or not length $x) {
  75. return 0;
  76. }
  77. elsif ($x eq '~') {
  78. return -1;
  79. }
  80. elsif ($x =~ /^\d$/) {
  81. return 0;
  82. }
  83. elsif ($x =~ /^[A-Za-z]$/) {
  84. return ord($x);
  85. }
  86. else {
  87. return ord($x) + 256;
  88. }
  89. }
  90. my ($val, $ref) = @_;
  91. $val = "" if not defined $val;
  92. $ref = "" if not defined $ref;
  93. my @val = split //,$val;
  94. my @ref = split //,$ref;
  95. my $vc = shift @val;
  96. my $rc = shift @ref;
  97. while (defined $vc or defined $rc) {
  98. my $first_diff = 0;
  99. while ((defined $vc and $vc !~ /^\d$/) or
  100. (defined $rc and $rc !~ /^\d$/)) {
  101. my $vo = order($vc); my $ro = order($rc);
  102. # Unlike dpkg's verrevcmp, we only return 1 or -1 here.
  103. return (($vo - $ro > 0) ? 1 : -1) if $vo != $ro;
  104. $vc = shift @val; $rc = shift @ref;
  105. }
  106. while (defined $vc and $vc eq '0') {
  107. $vc = shift @val;
  108. }
  109. while (defined $rc and $rc eq '0') {
  110. $rc = shift @ref;
  111. }
  112. while (defined $vc and $vc =~ /^\d$/ and
  113. defined $rc and $rc =~ /^\d$/) {
  114. $first_diff = ord($vc) - ord($rc) if !$first_diff;
  115. $vc = shift @val; $rc = shift @ref;
  116. }
  117. return 1 if defined $vc and $vc =~ /^\d$/;
  118. return -1 if defined $rc and $rc =~ /^\d$/;
  119. return (($first_diff > 0) ? 1 : -1) if $first_diff;
  120. }
  121. return 0;
  122. }
  123. =item vercmp
  124. Compare the two arguments as dpkg-style version numbers. Returns -1 if the
  125. first argument represents a lower version number than the second, 1 if the
  126. first argument represents a higher version number than the second, and 0 if
  127. the two arguments represent equal version numbers.
  128. =cut
  129. sub vercmp ($$)
  130. {
  131. my %version = parseversion $_[0];
  132. my %refversion = parseversion $_[1];
  133. return 1 if $version{epoch} > $refversion{epoch};
  134. return -1 if $version{epoch} < $refversion{epoch};
  135. my $r = verrevcmp($version{version}, $refversion{version});
  136. return $r if $r;
  137. return verrevcmp($version{revision}, $refversion{revision});
  138. }
  139. =item compare_versions
  140. Emulates dpkg --compare-versions. Takes two versions as arguments
  141. one and three and one operator as argument two. Supports the following
  142. operators: 'gt', 'ge', 'eq', 'le', 'lt', and '>>', '>=', '=', '<=', '<<'.
  143. Returns a true value if the specified condition is true, a false value
  144. otherwise.
  145. =cut
  146. sub compare_versions ($$$)
  147. {
  148. my $rel = $_[1];
  149. my $res = vercmp($_[0], $_[2]);
  150. warning("operator %s is deprecated in compare_versions(): use %s or %s",
  151. $rel, "$rel$rel", "$rel=") if ($rel eq '>' or $rel eq '<');
  152. if ($rel eq 'gt' or $rel eq '>>') {
  153. return $res > 0;
  154. } elsif ($rel eq 'ge' or $rel eq '>=' or $rel eq '>') {
  155. return $res >= 0;
  156. } elsif ($rel eq 'eq' or $rel eq '=') {
  157. return $res == 0;
  158. } elsif ($rel eq 'le' or $rel eq '<=' or $rel eq '<') {
  159. return $res <= 0;
  160. } elsif ($rel eq 'lt' or $rel eq '<<') {
  161. return $res < 0;
  162. } else {
  163. die "bad relation '$rel'";
  164. }
  165. }
  166. =item check_version($version, $die)
  167. If $die is false (or unset), returns true if the version is valid and
  168. false if it contains illegal characters. If $die is true, it dies with
  169. an error message if it contains illegal characters, otherwise it returns
  170. true.
  171. =cut
  172. sub check_version ($;$) {
  173. my ($version, $die) = @_;
  174. $version ||= "";
  175. if ($version =~ m/[^-+:.0-9a-zA-Z~]/o) {
  176. error(_g("version number contains illegal character `%s'"), $&) if $die;
  177. return 0;
  178. }
  179. return 1;
  180. }
  181. =back
  182. =head1 AUTHOR
  183. Don Armstrong <don@donarmstrong.com> and Colin Watson
  184. E<lt>cjwatson@debian.orgE<gt>, based on the implementation in
  185. C<dpkg/lib/vercmp.c> by Ian Jackson and others.
  186. =cut
  187. 1;