Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 28 Mar 2001 15:30:20 -0800
From:      bmah@freebsd.org (Bruce A. Mah)
To:        "Akinori MUSHA" <knu@iDaemons.org>
Cc:        "Bruce A. Mah" <bmah@freebsd.org>, freebsd-ports@freebsd.org, knu@freebsd.org
Subject:   Re: pkg_version comparison routine 
Message-ID:  <200103282330.f2SNUKX26511@bmah-freebsd-0.cisco.com>
In-Reply-To: <86y9tqc4tj.wl@archon.local.idaemons.org> 
References:  <200103280237.f2S2bUP02277@bmah-freebsd-0.cisco.com> <86y9tqc4tj.wl@archon.local.idaemons.org>

next in thread | previous in thread | raw e-mail | index | archive | help
--==_Exmh_1193653196P
Content-Type: text/plain; charset=us-ascii

If memory serves me right, "Akinori MUSHA" wrote:

> Actually, the PkgVersion module used in my portupgrade/portversion has
> a unit test suite to validate its comparison algorithm.  I bet you can
> just grasp it even if you're not familiar with Ruby at all:
> 
> http://www.idaemons.org/cgi-bin/cvsweb.cgi/~checkout~/pkgtools/tests/test_pkg
> version.rb?rev=.&content-type=text/plain&cvsroot=local

Interesting...much more systematic that the way that I (ahem) tested my 
code.

> I think something similar to this can be written in Bourne shell using
> the `pkg_version -t' feature, so that we can always ensure the
> correctness of its behavior.

That's a good idea.  Maybe we can add a "make test" target to
pkg_version's Makefile or something like that.

> > The -t option below is a little different than what knu did originally; 
> > what this version does is to take two version strings and return "=", 
> > "<", or ">" on standard out.  (knu's version took a test and returned a 
> > truth value for the test.)  I've got a usage in mind where I think that 
> > this form of -t will be more useful.
> 
> Good idea.  That'd be more convenient for shell scripts.

What I was thinking about was an addition of a new type of dependency 
to bsd.port.mk, where rather than checking for existence of a file (and 
building a specific port if it didn't exist) we could check for the 
existence of a specific version of a port (installing if it doesn't 
exist and updating if it's too old).  This was inspired by a comment 
from sobomax the last time the Magic Port Upgrading script was 
discussed.

> > The following is a patch against the RELENG_4 branch, but it should
> > apply equally well to the HEAD.  Comments would be most welcome.
> 
> I'll check it out later.  Thanks for adopting my code! :)

I just realized that I don't handle the case of "1.0p1" vs "1.0p2" (some
digits, some letters, and some more digits).  This is technically
illegal, but my copy of the Ports Collection has 115 ports with version
numbers of this form.  New patch attached below.  Can anyone guess I was
looking at Scheme code immediately before fixing this?

(You know what really sucks about pkg_version is the whole world
gets to see what a Perl amateur I am, even after ten years of writing in
this @#$%^&* language.)

Cheers,

Bruce.

? pkg_version.ps
? pkg_version.diff
Index: pkg_version.1
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/version/pkg_version.1,v
retrieving revision 1.5.2.8
diff -c -r1.5.2.8 pkg_version.1
*** pkg_version.1	2001/02/20 23:32:50	1.5.2.8
--- pkg_version.1	2001/03/28 23:22:39
***************
*** 36,41 ****
--- 36,43 ----
  .Op Fl l Ar limchar
  .Op Fl L Ar limchar
  .Op Ar index
+ .Nm pkg_version
+ .Op Fl t Ar version1 version2
  .Sh DESCRIPTION
  The
  .Nm
***************
*** 144,149 ****
--- 146,161 ----
  to the shell, it is best to quote
  .Ar limchar
  with single quotes.
+ .It Fl t
+ Test a pair of version number strings and exit.
+ The output consists of one of the single characters
+ .Li =
+ (equal),
+ .Li \&<
+ (right-hand number greater), or
+ .Li \&>
+ (left-hand number greater) on standard output.
+ This flag is mostly useful for scripts or for testing.
  .It Fl v
  Enable verbose output.  Verbose output includes some English-text
  interpretations of the version number comparisons, as well as the
***************
*** 198,214 ****
  unusable state.
  .Pp
  .Dl % pkg_version -c > do_update
  .Sh AUTHORS
  .An Bruce A. Mah Aq bmah@FreeBSD.org
  .Sh CONTRIBUTORS
  .An Nik Clayton Aq nik@FreeBSD.org ,
  .An Dominic Mitchell Aq dom@palmerharvey.co.uk ,
  .An Mark Ovens Aq marko@FreeBSD.org ,
! .An Doug Barton Aq DougB@gorean.org
  .Sh BUGS
- .Pp
- Patch levels aren't handled
- very well (i.e. version numbers of the form 1.2p3 or 1.2pl3).
  .Pp
  The commands output feature is 
  .Bf Em 
--- 210,228 ----
  unusable state.
  .Pp
  .Dl % pkg_version -c > do_update
+ .Pp
+ The following command compares two package version strings:
+ .Pp
+ .Dl % pkg_version -t 1.5 1.5.1
  .Sh AUTHORS
  .An Bruce A. Mah Aq bmah@FreeBSD.org
  .Sh CONTRIBUTORS
  .An Nik Clayton Aq nik@FreeBSD.org ,
  .An Dominic Mitchell Aq dom@palmerharvey.co.uk ,
  .An Mark Ovens Aq marko@FreeBSD.org ,
! .An Doug Barton Aq DougB@gorean.org ,
! .An Akinori MUSHA Aq knu@FreeBSD.org
  .Sh BUGS
  .Pp
  The commands output feature is 
  .Bf Em 
Index: pkg_version.pl
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/version/pkg_version.pl,v
retrieving revision 1.4.2.7
diff -c -r1.4.2.7 pkg_version.pl
*** pkg_version.pl	2001/02/16 17:52:34	1.4.2.7
--- pkg_version.pl	2001/03/28 23:22:39
***************
*** 37,43 ****
  #
  # Configuration global variables
  #
- $Version = '0.1';
  $CurrentPackagesCommand = '/usr/sbin/pkg_info -aI';
  $CatProgram = "cat ";
  $FetchProgram = "fetch -o - ";
--- 37,42 ----
***************
*** 62,70 ****
  # This function returns -1, 0, or 1, in the same manner as <=> or cmp.
  #
  sub CompareNumbers {
!     local($v1, $v2);
!     $v1 = $_[0];
!     $v2 = $_[1];
  
      # Short-cut in case of equality
      if ($v1 eq $v2) {
--- 61,67 ----
  # This function returns -1, 0, or 1, in the same manner as <=> or cmp.
  #
  sub CompareNumbers {
!     my ($v1, $v2) = @_;
  
      # Short-cut in case of equality
      if ($v1 eq $v2) {
***************
*** 73,99 ****
  
      # Loop over different components (the parts separated by dots).
      # If any component differs, we have the basis for an inequality.
!     while (1) {
! 	($p1, $v1) = split(/\./, $v1, 2);
! 	($p2, $v2) = split(/\./, $v2, 2);
! 
! 	# If we\'re out of components, they\'re equal (this probably won\'t
! 	# happen, since the short-cut case above should get this).
! 	if (($p1 eq "") && ($p2 eq "")) {
! 	    return 0;
! 	}
! 	# Check for numeric inequality.  We assume here that (for example)
! 	# 3.09 < 3.10.
! 	elsif ($p1 != $p2) {
! 	    return $p1 <=> $p2;
  	}
! 	# Check for string inequality, given numeric equality.  This
! 	# handles version numbers of the form 3.4j < 3.4k.
! 	elsif ($p1 ne $p2) {
! 	    return $p1 cmp $p2;
  	}
      }
  
  }
  
  #
--- 70,157 ----
  
      # Loop over different components (the parts separated by dots).
      # If any component differs, we have the basis for an inequality.
!     my @s1 = split(/\./, $v1);
!     my @s2 = split(/\./, $v2);
!     my ($c1, $c2);
!     do {
! 	last unless @s1 || @s2;
! 	$c1 = shift @s1;
! 	$c2 = shift @s2;
!     } while ($c1 eq $c2);
! 
!     # Look at the first components of the arrays that are left.
!     # These will determine the result of the comparison.
!     # Note that if either version doesn't have any components left,
!     # it's implicitly treated as a "0".
! 
!     # Our next set of checks looks to see if either component has a
!     # leading letter (there should be at most one leading letter per
!     # component, so that "4.0b1" is allowed, but "4.0beta1" is not).
!     if ($c1 =~ /^\D/) {
! 	if ($c2 =~ /^\D/) {
! 
! 	    # Both have a leading letter, so do an alpha comparison
! 	    # on the letters.  This isn't ideal, since we're assuming
! 	    # that "1.0.b4" > "1.0.a2".  But it's about the best we can do, 
! 	    # without encoding some explicit policy.
! 	    my ($letter1, $letter2);
! 	    $letter1 = substr($c1, 0, 1);
! 	    $letter2 = substr($c2, 0, 1);
! 
! 	    if ($letter1 ne $letter2) {
! 		return $letter1 cmp $letter2;
! 	    }
! 	    else {
! 		# The letters matched equally.  Delete the leading
! 		# letters and invoke ourselves on the remainining
! 		# characters, which according to the Porters Handbook
! 		# must be digits, so for example, "1.0.a9" < "1.0.a10".
! 		substr($c1, 0, 1) = "";
! 		substr($c2, 0, 1) = "";
! 		return &CompareNumbers($c1, $c2);		
! 	    }
! 
  	}
! 	else {
! 	    # $c1 begins with a letter, but $c2 doesn't.  Let $c2
! 	    # win the comparison, so that "1.0.b1" < "1.0.1".
! 	    return -1;
  	}
      }
+     else {
+ 	if ($c2 =~ /^\D/) {
+ 	    # $c2 begins with a letter but $c1 doesn't.  Let $c1
+ 	    # win the comparison, as above.
+ 	    return 1;
+ 	}
+ 	else {
+ 	    # Neither component begins with a leading letter.
+ 	    # Check for numeric inequality.  We assume here that (for example)
+ 	    # "3.09" < "3.10", and that we aren't going to be asked to
+ 	    # decide between "3.010" and "3.10".
+ 	    if ($c1 != $c2) {
+ 		return $c1 <=> $c2;
+ 	    }
  
+ 	    # String comparison, given numeric equality.  This
+ 	    # handles comparisons of the form "3.4j" < "3.4k".  This form
+ 	    # technically isn't allowed by the Porter's Handbook, but a
+ 	    # number of ports in the FreeBSD Ports Collection as of this
+ 	    # writing use it (graphics/jpeg and graphics/xv).  So we need
+ 	    # to support it.
+ 	    #
+ 	    # What we actually do is to strip off the leading digits and
+ 	    # invoke ourselves on the remainder.  This allows us to handle
+ 	    # comparisons of the form "1.1p1" < "1.1p2".  Again, not
+ 	    # technically allowed by the Porters Handbook, but lots of ports
+ 	    # use it.
+ 	    else {
+ 		$c1 =~ s/\d+//;
+ 		$c2 =~ s/\d+//;
+ 		return &CompareNumbers($c1, $c2);
+ 	    }
+ 	}
+     }
  }
  
  #
***************
*** 194,203 ****
  #
  sub PrintHelp {
      print <<"EOF"
! pkg_version $Version
! Bruce A. Mah <bmah\@freebsd.org>
! 
! Usage: pkg_version [-c] [-d debug] [-h] [-v] [index]
  -c              Show commands to update installed packages
  -d debug	Debugging output (debug controls level of output)
  -h		Help (this message)
--- 252,259 ----
  #
  sub PrintHelp {
      print <<"EOF"
! Usage:	pkg_version [-c] [-d debug] [-h] [-l limchar] [-L limchar] [-v] [index]
! 	pkg_version [-d debug] -t v1 v2
  -c              Show commands to update installed packages
  -d debug	Debugging output (debug controls level of output)
  -h		Help (this message)
***************
*** 206,218 ****
  -v		Verbose output
  index		URL or filename of index file
  		(Default is $IndexFile)
  EOF
  }
  
  #
  # Parse command-line arguments, deal with them
  #
! if (!getopts('cdhl:L:v') || ($opt_h)) {
      &PrintHelp();
      exit;
  }
--- 262,276 ----
  -v		Verbose output
  index		URL or filename of index file
  		(Default is $IndexFile)
+ 
+ -t v1 v2	Test two version strings
  EOF
  }
  
  #
  # Parse command-line arguments, deal with them
  #
! if (!getopts('cdhl:L:tv') || ($opt_h)) {
      &PrintHelp();
      exit;
  }
***************
*** 231,241 ****
  if ($opt_L) {
      $PreventFlag = $opt_L;
  }
  if ($opt_v) {
      $VerboseFlag = 1;
  }
  if ($#ARGV >= 0) {
!     $IndexFile = $ARGV[0];
  }
  
  # Determine what command to use to retrieve the index file.
--- 289,322 ----
  if ($opt_L) {
      $PreventFlag = $opt_L;
  }
+ if ($opt_t) {
+     $TestFlag = 1;
+ }
  if ($opt_v) {
      $VerboseFlag = 1;
  }
  if ($#ARGV >= 0) {
!     if ($TestFlag) {
! 	($test1, $test2) = @ARGV;
!     }
!     else {
! 	$IndexFile = $ARGV[0];
!     }
! }
! 
! # Handle test flag now
! if ($TestFlag) {
!     my $cmp = CompareVersions($test1, $test2);
!     if ($cmp < 0) {
! 	print "<\n";
!     }
!     elsif ($cmp == 0) {
! 	print "=\n";
!     }
!     else {
! 	print ">\n";
!     }
!     exit(0);
  }
  
  # Determine what command to use to retrieve the index file.




--==_Exmh_1193653196P
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.4 (FreeBSD)
Comment: Exmh version 2.2 06/23/2000

iD8DBQE6wnQM2MoxcVugUsMRAtrXAKCXcY1yEcTxcUE0AWSowU0D/1EsugCg1OA9
H/SqxT+WDG2CbjnpmruTAcc=
=yvfh
-----END PGP SIGNATURE-----

--==_Exmh_1193653196P--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-ports" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200103282330.f2SNUKX26511>