was Re: [Fwd: CPAN Upload: J/JP/JPEACOCK/version-0.36.tar.gz]
John Peacock [Sun, 1 Feb 2004 21:10:07 +0000 (16:10 -0500)]
Message-ID: <401DB17F.5060808@rowman.com>

p4raw-id: //depot/perl@22264

embed.fnc
embed.h
lib/version.pm
lib/version.t
pod/perlapi.pod
proto.h
t/comp/use.t
t/op/universal.t
universal.c
util.c

index 396f5b7..972d34d 100644 (file)
--- a/embed.fnc
+++ b/embed.fnc
@@ -535,7 +535,7 @@ Ap  |OP*    |newWHILEOP     |I32 flags|I32 debuggable|LOOP* loop \
 
 Ap     |PERL_SI*|new_stackinfo|I32 stitems|I32 cxitems
 Ap     |char*  |scan_vstring   |char *vstr|SV *sv
-Apd    |char*  |scan_version   |char *vstr|SV *sv
+Apd    |char*  |scan_version   |char *vstr|SV *sv|bool qv
 Apd    |SV*    |new_version    |SV *ver
 Apd    |SV*    |upg_version    |SV *ver
 Apd    |SV*    |vnumify        |SV *vs
diff --git a/embed.h b/embed.h
index dd5a05d..984bc66 100644 (file)
--- a/embed.h
+++ b/embed.h
 #define newWHILEOP(a,b,c,d,e,f,g)      Perl_newWHILEOP(aTHX_ a,b,c,d,e,f,g)
 #define new_stackinfo(a,b)     Perl_new_stackinfo(aTHX_ a,b)
 #define scan_vstring(a,b)      Perl_scan_vstring(aTHX_ a,b)
-#define scan_version(a,b)      Perl_scan_version(aTHX_ a,b)
+#define scan_version(a,b,c)    Perl_scan_version(aTHX_ a,b,c)
 #define new_version(a)         Perl_new_version(aTHX_ a)
 #define upg_version(a)         Perl_upg_version(aTHX_ a)
 #define vnumify(a)             Perl_vnumify(aTHX_ a)
index 520c781..f4cf944 100644 (file)
@@ -4,12 +4,15 @@ package version;
 use 5.005_03;
 use strict;
 
+require Exporter;
 require DynaLoader;
-use vars qw(@ISA $VERSION $CLASS);
+use vars qw(@ISA $VERSION $CLASS @EXPORT);
 
-@ISA = qw(DynaLoader);
+@ISA = qw(Exporter DynaLoader);
 
-$VERSION = 0.29; # stop using CVS and switch to subversion
+@EXPORT = qw(qv);
+
+$VERSION = 0.36; # stop using CVS and switch to subversion
 
 $CLASS = 'version';
 
@@ -31,14 +34,17 @@ version - Perl extension for Version Objects
   $version = new version "12.2.1"; # must be quoted!
   print $version;              # 12.2.1
   print $version->numify;      # 12.002001
-  if ( $version gt  "v12.2" )  # true
+  if ( $version gt  "12.2" )   # true
 
-  $vstring = new version qw(v1.2); # must be quoted!
+  $vstring = new version qw(1.2); # must be quoted!
   print $vstring;              # 1.2
 
   $alphaver = new version "1.2_3"; # must be quoted!
   print $alphaver;             # 1.2_3
   print $alphaver->is_alpha();  # true
+  
+  $ver = qv(1.2);               # 1.2.0
+  $ver = qv("1.2");             # 1.2.0
 
   $perlver = new version 5.005_03; # must not be quoted!
   print $perlver;              # 5.5.30
@@ -47,7 +53,7 @@ version - Perl extension for Version Objects
 
 Overloaded version objects for all versions of Perl.  This module
 implements all of the features of version objects which will be part
-of Perl 5.10.0 except automatic v-string handling.  See L<"Quoting">.
+of Perl 5.10.0 except automatic version object creation.
 
 =head2 What IS a version
 
@@ -66,11 +72,13 @@ There are actually two distinct ways to initialize versions:
 Any initial parameter which "looks like a number", see L<Numeric
 Versions>.
 
-=item * V-String Versions
+=item * Quoted Versions
 
-Any initial parameter which contains more than one decimal point,
-contains an embedded underscore, or has a leading 'v' see L<V-String
-Versions>.
+Any initial parameter which contains more than one decimal point
+or contains an embedded underscore, see L<Quoted Versions>.  The
+most recent development version of Perl (5.9.x) and the next major
+release (5.10.0) will automatically create version objects for bare
+numbers containing more than one decimal point.
 
 =back
 
@@ -79,9 +87,10 @@ the default stringification will always be in a reduced form, i.e.:
 
   $v  = new version 1.002003;  # 1.2.3
   $v2 = new version  "1.2.3";  # 1.2.3
-  $v3 = new version   v1.2.3;  # 1.2.3 for Perl > v5.8.0
-  $v4 = new version    1.2.3;  # 1.2.3 for Perl > v5.8.0
+  $v3 = new version   1.2.3;   # 1.2.3 for Perl > 5.8.0
 
+Note that the default stringification will display at least three sub
+terms (to ensure that appropriate round-trip processing is possible).
 Please see L<"Quoting"> for more details on how Perl will parse various
 input values.
 
@@ -94,9 +103,37 @@ contains a numeric, decimal, or underscore character.  So, for example:
 However, see L<New Operator> for one case where non-numeric text is
 acceptable when initializing version objects.
 
+=head2 What about v-strings?
+
+Beginning with Perl 5.6.0, an alternate method to code arbitrary strings
+of bytes was introduced, called v-strings.  They were intended to be an
+easy way to enter, for example, Unicode strings (which contain two bytes
+per character).  Some programs have used them to encode printer control
+characters (e.g. CRLF).  They were also intended to be used for $VERSION.
+Their use has been problematic from the start and they will be phased out
+beginning in Perl 5.10.0.
+
+There are two ways to enter v-strings: a bare number with two or more
+decimal places, or a bare number with one or more decimal places and a 
+leading 'v' character (also bare).  For example:
+
+  $vs1 = 1.2.3; # encoded as \1\2\3
+  $vs2 = v1.2;  # encoded as \1\2
+
+The first of those two syntaxes is destined to be the default way to create
+a version object in 5.10.0, whereas the second will issue a mandatory
+deprecation warning beginning at the same time.
+
+Consequently, the use of v-strings to initialize version objects with
+this module is only possible with Perl 5.8.1 (which will contain special
+code to enable it).  Their use is B<strongly> discouraged in all 
+circumstances(especially the leading 'v' style), since the meaning will
+change depending on which Perl you are running.  It is better to use
+L<"Quoted Versions"> to ensure the proper interpretation.
+
 =head2 Numeric Versions
 
-These correspond to historical versions of Perl itself prior to v5.6.0,
+These correspond to historical versions of Perl itself prior to 5.6.0,
 as well as all other modules which follow the Camel rules for the
 $VERSION scalar.  A numeric version is initialized with what looks like
 a floating point number.  Leading zeros B<are> significant and trailing
@@ -110,42 +147,40 @@ will have trailing zeros added to make up the difference.  For example:
   $v = new version     1.002;    # 1.2
   $v = new version    1.0023;    # 1.2.300
   $v = new version   1.00203;    # 1.2.30
-  $v = new version  1.002_03;    # 1.2.30   See L<"Quoting">
+  $v = new version  1.002_03;    # 1.2.30   See "Quoting"
   $v = new version  1.002003;    # 1.2.3
 
 All of the preceeding examples except the second to last are true
 whether or not the input value is quoted.  The important feature is that
 the input value contains only a single decimal.
 
-=head2 V-String Versions
+=head2 Quoted Versions
 
 These are the newest form of versions, and correspond to Perl's own
-version style beginning with v5.6.0.  Starting with Perl v5.10.0,
-this is likely to be the preferred form.  This method requires that
-the input parameter be quoted, although Perl > v5.9.0 can use bare
-v-strings as a special form of quoting.
-
-Unlike L<Numeric Versions>, V-String Versions must either have more than
-a single decimal point, e.g. "5.6.1" B<or> must be prefaced by a "v"
-like this "v5.6" (much like v-string notation).  In fact, with the
-newest Perl v-strings themselves can be used to initialize version
-objects.  Also unlike L<Numeric Versions>, leading zeros are B<not>
-significant, and trailing zeros must be explicitely specified (i.e.
-will not be automatically added).  In addition, the subversions are
-not enforced to be three decimal places.
+version style beginning with 5.6.0.  Starting with Perl 5.10.0,
+and most likely Perl 6, this is likely to be the preferred form.  This
+method requires that the input parameter be quoted, although Perl's after 
+5.9.0 can use bare numbers with multiple decimal places as a special form
+of quoting.
+
+Unlike L<Numeric Versions>, Quoted Versions may have more than
+a single decimal point, e.g. "5.6.1" but must be quoted like this "5.6" in
+order to prevent the Numeric Version interpretation.  Also unlike
+L<Numeric Versions>, leading zeros are B<not> significant, and trailing
+zeros must be explicitely specified (i.e. will not be automatically added).
+In addition, the subversions are not enforced to be three decimal places.
 
 So, for example:
 
-  $v = new version    "v1.2";    # 1.2
-  $v = new version  "v1.002";    # 1.2
+  $v = new version   "1.002";    # 1.2
   $v = new version   "1.2.3";    # 1.2.3
-  $v = new version  "v1.2.3";    # 1.2.3
-  $v = new version "v1.0003";    # 1.3
+  $v = new version   "1.2.3";    # 1.2.3
+  $v = new version  "1.0003";    # 1.3
 
-In additional to conventional versions, V-String Versions can be
+In addition to conventional versions, Quoted Versions can be
 used to create L<Alpha Versions>.
 
-In general, V-String Versions permit the greatest amount of freedom
+In general, Quoted Versions permit the greatest amount of freedom
 to specify a version, whereas Numeric Versions enforce a certain
 uniformity.  See also L<New Operator> for an additional method of
 initializing version objects.
@@ -165,8 +200,6 @@ version objects.  One way to increment versions when programming is to
 use the CVS variable $Revision, which is automatically incremented by
 CVS every time the file is committed to the repository.
 
-=back
-
 In order to facilitate this feature, the following
 code can be employed:
 
@@ -175,12 +208,32 @@ code can be employed:
 and the version object will be created as if the following code
 were used:
 
-  $VERSION = new version "v2.7";
+  $VERSION = new version "2.7";
 
 In other words, the version will be automatically parsed out of the
 string, and it will be quoted to preserve the meaning CVS normally
 carries for versions.
 
+=back
+
+=over 4
+
+=item * qv()
+
+An alternate way to create a new version object is through the exported
+qv() sub.  This is not strictly like other q? operators (like qq, qw),
+in that the only delimiters supported are parentheses (or spaces).  It is
+the best way to initialize a short version without triggering the floating
+point interpretation.  For example:
+
+  $v1 = qv(1.2);         # 1.2.0
+  $v2 = qv("1.2");       # also 1.2.0
+
+As you can see, either a bare number or a quoted string can be used, and
+either will yield the same version number.
+
+=back
+
 For the subsequent examples, the following two objects will be used:
 
   $ver  = new version "1.2.3"; # see "Quoting" below
@@ -193,11 +246,25 @@ For the subsequent examples, the following two objects will be used:
 Any time a version object is used as a string, a stringified
 representation is returned in reduced form (no extraneous zeros):
 
-=back
-
   print $ver->stringify;      # prints 1.2.3
   print $ver;                 # same thing
 
+In order to preserve the meaning of the processed version, the 
+default stringified representation will always contain at least
+three sub terms.  In other words, the following is guaranteed to
+always be true:
+
+  my $newver = version->new($ver->stringify);
+  if ($newver eq $ver ) # always true
+    {...}
+
+If the string representation "looked like a number" then there is
+a possibility that creating a new version object from that would use
+the Numeric Version interpretation,  If a version object contains only
+two terms internally, it will stringify with an explicit '.0' appended.
+
+=back
+
 =over 4
 
 =item * Numification
@@ -211,6 +278,13 @@ three decimal places.  So for example:
 
   print $ver->numify;         # prints 1.002003
 
+Unlike the stringification operator, there is never any need to append
+trailing zeros to preserve the correct version value.
+
+=back
+
+=over 4
+
 =item * Comparison operators
 
 Both cmp and <=> operators perform the same comparison between terms
@@ -218,7 +292,7 @@ Both cmp and <=> operators perform the same comparison between terms
 generates all of the other comparison operators based on those two.
 In addition to the obvious equalities listed below, appending a single
 trailing 0 term does not change the value of a version for comparison
-purposes.  In other words "v1.2" and "v1.2.0" are identical versions.
+purposes.  In other words "v1.2" and "1.2.0" will compare as identical.
 
 For example, the following relations hold:
 
@@ -229,21 +303,14 @@ For example, the following relations hold:
   $ver != 1.3     $ver ne "1.3"      true
   $ver == 1.2     $ver eq "1.2"      false
   $ver == 1.2.3   $ver eq "1.2.3"    see discussion below
-  $ver == v1.2.3  $ver eq "v1.2.3"   ditto
 
-In versions of Perl prior to the 5.9.0 development releases, it is not
-permitted to use bare v-strings in either form, due to the nature of Perl's
-parsing operation.  After that version (and in the stable 5.10.0 release),
-v-strings can be used with version objects without problem, see L<"Quoting">
-for more discussion of this topic.  In the case of the last two lines of
-the table above, only the string comparison will be true; the numerical
-comparison will test false.  However, you can do this:
+It is probably best to chose either the numeric notation or the string
+notation and stick with it, to reduce confusion.  Perl6 version objects
+B<may> only support numeric comparisons.  See also L<"Quoting">.
 
-  $ver == "1.2.3" or $ver == "v1.2.3"  # both true
+=back
 
-even though you are doing a "numeric" comparison with a "string" value.
-It is probably best to chose either the numeric notation or the string
-notation and stick with it, to reduce confusion.  See also L<"Quoting">.
+=over 4
 
 =item * Logical Operators 
 
@@ -253,7 +320,7 @@ has been initialized, you can simply test it directly:
   $vobj = new version $something;
   if ( $vobj )   # true only if $something was non-blank
 
-You can also test whether a version object is a L<Alpha version>, for
+You can also test whether a version object is an L<Alpha version>, for
 example to prevent the use of some feature not present in the main
 release:
 
@@ -295,12 +362,12 @@ but other operations are not likely to be what you intend.  For example:
   $V2 = new version 100/9; # Integer overflow in decimal number
   print $V2;               # yields 11_1285418553
 
-Perl 5.9.0 and beyond will be able to automatically quote v-strings
-(which may become the recommended notation), but that is not possible in
-earlier versions of Perl.  In other words:
+Perl 5.8.1 and beyond will be able to automatically quote v-strings
+(although a warning will be issued under 5.9.x and 5.10.0), but that
+is not possible in earlier versions of Perl.  In other words:
 
   $version = new version "v2.5.4";  # legal in all versions of Perl
-  $newvers = new version v2.5.4;    # legal only in Perl > 5.9.0
+  $newvers = new version v2.5.4;    # legal only in Perl > 5.8.1
 
 
 =head2 Types of Versions Objects
@@ -324,7 +391,7 @@ This allows you to automatically increment your module version by
 using the Revision number from the primary file in a distribution, see
 L<ExtUtils::MakeMaker/"VERSION_FROM">.
 
-=item * alpha versions
+=item * Alpha versions
 
 For module authors using CPAN, the convention has been to note
 unstable releases with an underscore in the version string, see
@@ -352,7 +419,7 @@ comparisons.
 
 =head1 EXPORT
 
-None by default.
+qv - quoted version initialization operator
 
 =head1 AUTHOR
 
index 6f753bd..ecf9f46 100644 (file)
 #! /usr/local/perl -w
 # Before `make install' is performed this script should be runnable with
 # `make test'. After `make install' it should work as `perl test.pl'
-# $Revision: 2.4 $
 
 #########################
 
-use Test::More tests => 73;
-use_ok("version"); # If we made it this far, we are ok.
-
-my ($version, $new_version);
-#########################
-
-# Insert your test code below, the Test module is use()ed here so read
-# its man page ( perldoc Test ) for help writing this test script.
-
-# Test bare number processing
-diag "tests with bare numbers" unless $ENV{PERL_CORE};
-$version = new version 5.005_03;
-is ( "$version" , "5.5.30" , '5.005_03 eq 5.5.30' );
-$version = new version 1.23;
-is ( "$version" , "1.230" , '1.23 eq "1.230"' );
-
-# Test quoted number processing
-diag "tests with quoted numbers" unless $ENV{PERL_CORE};
-$version = new version "5.005_03";
-is ( "$version" , "5.5_3" , '"5.005_03" eq "5.5_3"' );
-$version = new version "v1.23";
-is ( "$version" , "1.23" , '"v1.23" eq "1.23"' );
-
-# Test stringify operator
-diag "tests with stringify" unless $ENV{PERL_CORE};
-$version = new version "5.005";
-is ( "$version" , "5.5" , '5.005 eq 5.5' );
-$version = new version "5.006.001";
-is ( "$version" , "5.6.1" , '5.006.001 eq 5.6.1' );
-$version = new version "1.2.3_4";
-is ( "$version" , "1.2.3_4" , 'alpha version 1.2.3_4 eq 1.2.3_4' );
-
-# test illegal formats
-diag "test illegal formats" unless $ENV{PERL_CORE};
-eval {my $version = new version "1.2_3_4";};
-like($@, qr/multiple underscores/,
-    "Invalid version format (multiple underscores)");
-
-eval {my $version = new version "1.2_3.4";};
-like($@, qr/underscores before decimal/,
-    "Invalid version format (underscores before decimal)");
-
-$version = new version "99 and 44/100 pure";
-ok ("$version" eq "99.0", '$version eq "99.0"');
-ok ($version->numify == 99.0, '$version->numify == 99.0');
-
-$version = new version "something";
-ok (defined $version, 'defined $version');
-
-# reset the test object to something reasonable
-$version = new version "1.2.3";
-
-# Test boolean operator
-ok ($version, 'boolean');
-
-# Test ref operator
-ok (ref($version) eq 'version','ref operator');
-
-# Test comparison operators with self
-diag "tests with self" unless $ENV{PERL_CORE};
-ok ( $version eq $version, '$version eq $version' );
-is ( $version cmp $version, 0, '$version cmp $version == 0' );
-ok ( $version == $version, '$version == $version' );
-
-# test first with non-object
-$version = new version "5.006.001";
-$new_version = "5.8.0";
-diag "tests with non-objects" unless $ENV{PERL_CORE};
-ok ( $version ne $new_version, '$version ne $new_version' );
-ok ( $version lt $new_version, '$version lt $new_version' );
-ok ( $new_version gt $version, '$new_version gt $version' );
-ok ( ref(\$new_version) eq 'SCALAR', 'no auto-upgrade');
-$new_version = "$version";
-ok ( $version eq $new_version, '$version eq $new_version' );
-ok ( $new_version eq $version, '$new_version eq $version' );
-
-# now test with existing object
-$new_version = new version "5.8.0";
-diag "tests with objects" unless $ENV{PERL_CORE};
-ok ( $version ne $new_version, '$version ne $new_version' );
-ok ( $version lt $new_version, '$version lt $new_version' );
-ok ( $new_version gt $version, '$new_version gt $version' );
-$new_version = new version "$version";
-ok ( $version eq $new_version, '$version eq $new_version' );
+use Test::More tests => 166;
 
-# Test Numeric Comparison operators
-# test first with non-object
-$new_version = "5.8.0";
-diag "numeric tests with non-objects" unless $ENV{PERL_CORE};
-ok ( $version == $version, '$version == $version' );
-ok ( $version < $new_version, '$version < $new_version' );
-ok ( $new_version > $version, '$new_version > $version' );
-ok ( $version != $new_version, '$version != $new_version' );
-
-# now test with existing object
-$new_version = new version $new_version;
-diag "numeric tests with objects" unless $ENV{PERL_CORE};
-ok ( $version < $new_version, '$version < $new_version' );
-ok ( $new_version > $version, '$new_version > $version' );
-ok ( $version != $new_version, '$version != $new_version' );
-
-# now test with actual numbers
-diag "numeric tests with numbers" unless $ENV{PERL_CORE};
-ok ( $version->numify() == 5.006001, '$version->numify() == 5.006001' );
-ok ( $version->numify() <= 5.006001, '$version->numify() <= 5.006001' );
-ok ( $version->numify() < 5.008, '$version->numify() < 5.008' );
-#ok ( $version->numify() > v5.005_02, '$version->numify() > 5.005_02' );
-
-# test with long decimals
-diag "Tests with extended decimal versions" unless $ENV{PERL_CORE};
-$version = new version 1.002003;
-ok ( $version eq "1.2.3", '$version eq "1.2.3"');
-ok ( $version->numify == 1.002003, '$version->numify == 1.002003');
-$version = new version "2002.09.30.1";
-ok ( $version eq "2002.9.30.1",'$version eq 2002.9.30.1');
-ok ( $version->numify == 2002.009030001,
-    '$version->numify == 2002.009030001');
-
-# now test with alpha version form with string
-$version = new version "1.2.3";
-$new_version = "1.2.3_4";
-diag "tests with alpha-style non-objects" unless $ENV{PERL_CORE};
-ok ( $version lt $new_version, '$version lt $new_version' );
-ok ( $new_version gt $version, '$new_version gt $version' );
-ok ( $version ne $new_version, '$version ne $new_version' );
-
-$version = new version "1.2.4";
-diag "numeric tests with alpha-style non-objects" unless $ENV{PERL_CORE};
-ok ( $version > $new_version, '$version > $new_version' );
-ok ( $new_version < $version, '$new_version < $version' );
-ok ( $version != $new_version, '$version != $new_version' );
-
-# now test with alpha version form with object
-$version = new version "1.2.3";
-$new_version = new version "1.2.3_4";
-diag "tests with alpha-style objects" unless $ENV{PERL_CORE};
-ok ( $version < $new_version, '$version < $new_version' );
-ok ( $new_version > $version, '$new_version > $version' );
-ok ( $version != $new_version, '$version != $new_version' );
-ok ( !$version->is_alpha, '!$version->is_alpha');
-ok ( $new_version->is_alpha, '$new_version->is_alpha');
-
-$version = new version "1.2.4";
-diag "tests with alpha-style objects" unless $ENV{PERL_CORE};
-ok ( $version > $new_version, '$version > $new_version' );
-ok ( $new_version < $version, '$new_version < $version' );
-ok ( $version != $new_version, '$version != $new_version' );
-
-$version = new version "1.2.4";
-$new_version = new version "1.2_4";
-diag "tests with alpha-style objects with same subversion" unless $ENV{PERL_CORE};
-ok ( $version > $new_version, '$version > $new_version' );
-ok ( $new_version < $version, '$new_version < $version' );
-ok ( $version != $new_version, '$version != $new_version' );
-
-diag "test implicit [in]equality" unless $ENV{PERL_CORE};
-$version = new version "v1.2";
-$new_version = new version "1.2.0";
-ok ( $version == $new_version, '$version == $new_version' );
-$new_version = new version "1.2_0";
-ok ( $version == $new_version, '$version == $new_version' );
-$new_version = new version "1.2.1";
-ok ( $version < $new_version, '$version < $new_version' );
-$new_version = new version "1.2_1";
-ok ( $version < $new_version, '$version < $new_version' );
-$new_version = new version "1.1.999";
-ok ( $version > $new_version, '$version > $new_version' );
-
-# that which is not expressly permitted is forbidden
-diag "forbidden operations" unless $ENV{PERL_CORE};
-ok ( !eval { $version++ }, "noop ++" );
-ok ( !eval { $version-- }, "noop --" );
-ok ( !eval { $version/1 }, "noop /" );
-ok ( !eval { $version*3 }, "noop *" );
-ok ( !eval { abs($version) }, "noop abs" );
-
-# test reformed UNIVERSAL::VERSION
-diag "Replacement UNIVERSAL::VERSION tests" unless $ENV{PERL_CORE};
-
-# we know this file is here since we require it ourselves
-$version = new version $Test::More::VERSION;
-eval "use Test::More $version";
-unlike($@, qr/Test::More version $version required/,
-       'Replacement eval works with exact version');
-
-$version = new version $Test::More::VERSION+0.01; # this should fail even with old UNIVERSAL::VERSION
-eval "use Test::More $version";
-like($@, qr/Test::More version $version required/,
-       'Replacement eval works with incremented version');
-
-chop($version); # shorten by 1 digit, should still succeed
-eval "use Test::More $version";
-unlike($@, qr/Test::More version $version required/,
-       'Replacement eval works with single digit');
-
-$version += 0.1; # this would fail with old UNIVERSAL::VERSION
-eval "use Test::More $version";
-unlike($@, qr/Test::More version $version required/,
-       'Replacement eval works with incremented digit');
+diag "Tests with base class" unless $ENV{PERL_CORE};
 
+use_ok("version"); # If we made it this far, we are ok.
+BaseTests("version");
+
+diag "Tests with empty derived class" unless $ENV{PERL_CORE};
+
+package version::Empty;
+use vars qw($VERSION @ISA);
+use Exporter;
+use version 0.30;
+@ISA = qw(Exporter version);
+$VERSION = 0.01;
+
+package main;
+my $testobj = new version::Empty 1.002_003;
+isa_ok( $testobj, "version::Empty" );
+ok( $testobj->numify == 1.002003, "Numified correctly" );
+ok( $testobj->stringify eq "1.2.3", "Stringified correctly" );
+
+my $verobj = new version "1.2.4";
+ok( $verobj > $testobj, "Comparison vs parent class" );
+ok( $verobj gt $testobj, "Comparison vs parent class" );
+BaseTests("version::Empty");
+
+sub BaseTests {
+
+       my $CLASS = shift;
+       
+       # Insert your test code below, the Test module is use()ed here so read
+       # its man page ( perldoc Test ) for help writing this test script.
+       
+       # Test bare number processing
+       diag "tests with bare numbers" unless $ENV{PERL_CORE};
+       $version = $CLASS->new(5.005_03);
+       is ( "$version" , "5.5.30" , '5.005_03 eq 5.5.30' );
+       $version = $CLASS->new(1.23);
+       is ( "$version" , "1.230.0" , '1.23 eq "1.230.0"' );
+       
+       # Test quoted number processing
+       diag "tests with quoted numbers" unless $ENV{PERL_CORE};
+       $version = $CLASS->new("5.005_03");
+       is ( "$version" , "5.5_3" , '"5.005_03" eq "5.5_3"' );
+       $version = $CLASS->new("v1.23");
+       is ( "$version" , "1.23.0" , '"v1.23" eq "1.23.0"' );
+       
+       # Test stringify operator
+       diag "tests with stringify" unless $ENV{PERL_CORE};
+       $version = $CLASS->new("5.005");
+       is ( "$version" , "5.5.0" , '5.005 eq 5.5' );
+       $version = $CLASS->new("5.006.001");
+       is ( "$version" , "5.6.1" , '5.006.001 eq 5.6.1' );
+       $version = $CLASS->new("1.2.3_4");
+       is ( "$version" , "1.2.3_4" , 'alpha version 1.2.3_4 eq 1.2.3_4' );
+       
+       # test illegal formats
+       diag "test illegal formats" unless $ENV{PERL_CORE};
+       eval {my $version = $CLASS->new("1.2_3_4")};
+       like($@, qr/multiple underscores/,
+           "Invalid version format (multiple underscores)");
+       
+       eval {my $version = $CLASS->new("1.2_3.4")};
+       like($@, qr/underscores before decimal/,
+           "Invalid version format (underscores before decimal)");
+       
+       $version = $CLASS->new("99 and 44/100 pure");
+       ok ("$version" eq "99.0.0", '$version eq "99.0.0"');
+       ok ($version->numify == 99.0, '$version->numify == 99.0');
+       
+       $version = $CLASS->new("something");
+       ok (defined $version, 'defined $version');
+       
+       # reset the test object to something reasonable
+       $version = $CLASS->new("1.2.3");
+       
+       # Test boolean operator
+       ok ($version, 'boolean');
+       
+       # Test class membership
+       isa_ok ( $version, "version" );
+       
+       # Test comparison operators with self
+       diag "tests with self" unless $ENV{PERL_CORE};
+       ok ( $version eq $version, '$version eq $version' );
+       is ( $version cmp $version, 0, '$version cmp $version == 0' );
+       ok ( $version == $version, '$version == $version' );
+       
+       # test first with non-object
+       $version = $CLASS->new("5.006.001");
+       $new_version = "5.8.0";
+       diag "tests with non-objects" unless $ENV{PERL_CORE};
+       ok ( $version ne $new_version, '$version ne $new_version' );
+       ok ( $version lt $new_version, '$version lt $new_version' );
+       ok ( $new_version gt $version, '$new_version gt $version' );
+       ok ( ref(\$new_version) eq 'SCALAR', 'no auto-upgrade');
+       $new_version = "$version";
+       ok ( $version eq $new_version, '$version eq $new_version' );
+       ok ( $new_version eq $version, '$new_version eq $version' );
+       
+       # now test with existing object
+       $new_version = $CLASS->new("5.8.0");
+       diag "tests with objects" unless $ENV{PERL_CORE};
+       ok ( $version ne $new_version, '$version ne $new_version' );
+       ok ( $version lt $new_version, '$version lt $new_version' );
+       ok ( $new_version gt $version, '$new_version gt $version' );
+       $new_version = $CLASS->new("$version");
+       ok ( $version eq $new_version, '$version eq $new_version' );
+       
+       # Test Numeric Comparison operators
+       # test first with non-object
+       $new_version = "5.8.0";
+       diag "numeric tests with non-objects" unless $ENV{PERL_CORE};
+       ok ( $version == $version, '$version == $version' );
+       ok ( $version < $new_version, '$version < $new_version' );
+       ok ( $new_version > $version, '$new_version > $version' );
+       ok ( $version != $new_version, '$version != $new_version' );
+       
+       # now test with existing object
+       $new_version = $CLASS->new($new_version);
+       diag "numeric tests with objects" unless $ENV{PERL_CORE};
+       ok ( $version < $new_version, '$version < $new_version' );
+       ok ( $new_version > $version, '$new_version > $version' );
+       ok ( $version != $new_version, '$version != $new_version' );
+       
+       # now test with actual numbers
+       diag "numeric tests with numbers" unless $ENV{PERL_CORE};
+       ok ( $version->numify() == 5.006001, '$version->numify() == 5.006001' );
+       ok ( $version->numify() <= 5.006001, '$version->numify() <= 5.006001' );
+       ok ( $version->numify() < 5.008, '$version->numify() < 5.008' );
+       #ok ( $version->numify() > v5.005_02, '$version->numify() > 5.005_02' );
+       
+       # test with long decimals
+       diag "Tests with extended decimal versions" unless $ENV{PERL_CORE};
+       $version = $CLASS->new(1.002003);
+       ok ( $version eq "1.2.3", '$version eq "1.2.3"');
+       ok ( $version->numify == 1.002003, '$version->numify == 1.002003');
+       $version = $CLASS->new("2002.09.30.1");
+       ok ( $version eq "2002.9.30.1",'$version eq 2002.9.30.1');
+       ok ( $version->numify == 2002.009030001,
+           '$version->numify == 2002.009030001');
+       
+       # now test with alpha version form with string
+       $version = $CLASS->new("1.2.3");
+       $new_version = "1.2.3_4";
+       diag "tests with alpha-style non-objects" unless $ENV{PERL_CORE};
+       ok ( $version lt $new_version, '$version lt $new_version' );
+       ok ( $new_version gt $version, '$new_version gt $version' );
+       ok ( $version ne $new_version, '$version ne $new_version' );
+       
+       $version = $CLASS->new("1.2.4");
+       diag "numeric tests with alpha-style non-objects" unless $ENV{PERL_CORE};
+       ok ( $version > $new_version, '$version > $new_version' );
+       ok ( $new_version < $version, '$new_version < $version' );
+       ok ( $version != $new_version, '$version != $new_version' );
+       
+       # now test with alpha version form with object
+       $version = $CLASS->new("1.2.3");
+       $new_version = $CLASS->new("1.2.3_4");
+       diag "tests with alpha-style objects" unless $ENV{PERL_CORE};
+       ok ( $version < $new_version, '$version < $new_version' );
+       ok ( $new_version > $version, '$new_version > $version' );
+       ok ( $version != $new_version, '$version != $new_version' );
+       ok ( !$version->is_alpha, '!$version->is_alpha');
+       ok ( $new_version->is_alpha, '$new_version->is_alpha');
+       
+       $version = $CLASS->new("1.2.4");
+       diag "tests with alpha-style objects" unless $ENV{PERL_CORE};
+       ok ( $version > $new_version, '$version > $new_version' );
+       ok ( $new_version < $version, '$new_version < $version' );
+       ok ( $version != $new_version, '$version != $new_version' );
+       
+       $version = $CLASS->new("1.2.4");
+       $new_version = $CLASS->new("1.2_4");
+       diag "tests with alpha-style objects with same subversion" unless $ENV{PERL_CORE};
+       ok ( $version > $new_version, '$version > $new_version' );
+       ok ( $new_version < $version, '$new_version < $version' );
+       ok ( $version != $new_version, '$version != $new_version' );
+       
+       diag "test implicit [in]equality" unless $ENV{PERL_CORE};
+       $version = $CLASS->new("v1.2");
+       $new_version = $CLASS->new("1.2.0");
+       ok ( $version == $new_version, '$version == $new_version' );
+       $new_version = $CLASS->new("1.2_0");
+       ok ( $version == $new_version, '$version == $new_version' );
+       $new_version = $CLASS->new("1.2.1");
+       ok ( $version < $new_version, '$version < $new_version' );
+       $new_version = $CLASS->new("1.2_1");
+       ok ( $version < $new_version, '$version < $new_version' );
+       $new_version = $CLASS->new("1.1.999");
+       ok ( $version > $new_version, '$version > $new_version' );
+       
+       # that which is not expressly permitted is forbidden
+       diag "forbidden operations" unless $ENV{PERL_CORE};
+       ok ( !eval { ++$version }, "noop ++" );
+       ok ( !eval { --$version }, "noop --" );
+       ok ( !eval { $version/1 }, "noop /" );
+       ok ( !eval { $version*3 }, "noop *" );
+       ok ( !eval { abs($version) }, "noop abs" );
+
+       # test the qv() sub
+       diag "testing qv" unless $ENV{PERL_CORE};
+       $version = qv("1.2");
+       ok ( $version eq "1.2.0", 'qv("1.2") eq "1.2.0"' );
+       $version = qv(1.2);
+       ok ( $version eq "1.2.0", 'qv(1.2) eq "1.2.0"' );
+
+       # test the CVS revision mode
+       diag "testing CVS Revision" unless $ENV{PERL_CORE};
+       $version = new version qw$Revision: 1.2$;
+       ok ( $version eq "1.2.0", 'qw$Revision: 1.2$ eq 1.2.0' );
+       
+       # test reformed UNIVERSAL::VERSION
+       diag "Replacement UNIVERSAL::VERSION tests" unless $ENV{PERL_CORE};
+       
+       # we know this file is here since we require it ourselves
+       $version = $CLASS->new( $Test::More::VERSION );
+       eval "use Test::More $version";
+       unlike($@, qr/Test::More version $version required/,
+               'Replacement eval works with exact version');
+       
+       $version = $CLASS->new( $Test::More::VERSION+0.01 ); # this should fail even with old UNIVERSAL::VERSION
+       my $testeval = "use Test::More ".
+       (       $]<5.6  ? $version->numify() #why is this a problem???
+                       : $version );
+       eval $testeval;
+       like($@, qr/Test::More version $version required/,
+               'Replacement eval works with incremented version');
+       
+       $version =~ s/...$//; #convert to string and remove trailing '.0'
+       chop($version); # shorten by 1 digit, should still succeed
+       eval "use Test::More $version";
+       unlike($@, qr/Test::More version $version required/,
+               'Replacement eval works with single digit');
+       
+       $version += 0.1; # this would fail with old UNIVERSAL::VERSION
+       eval "use Test::More $version";
+       unlike($@, qr/Test::More version $version required/,
+               'Replacement eval works with incremented digit');
+       
+SKIP:  {
+           skip 'Cannot test v-strings with Perl < 5.8.1', 5
+                   if $] < 5.008_001; 
+           diag "Tests with v-strings" unless $ENV{PERL_CORE};
+           $version = $CLASS->new(1.2.3);
+           ok("$version" eq "1.2.3", '"$version" eq 1.2.3');
+           $version = $CLASS->new(1.0.0);
+           $new_version = $CLASS->new(1);
+           ok($version == $new_version, '$version == $new_version');
+           ok($version eq $new_version, '$version eq $new_version');
+           ok("$version" eq "$new_version", '"$version" eq "$new_version"');
+           $version = qv(1.2.3);
+           ok("$version" eq "1.2.3", 'v-string initialized qv()');
+       }
+}
index 5c0bee4..d8f7efa 100644 (file)
@@ -1772,15 +1772,17 @@ an RV.
 
 Function must be called with an already existing SV like
 
-    sv = NEWSV(92,0);
-    s = scan_version(s,sv);
+    sv = newSV(0);
+    s = scan_version(s,SV *sv, bool qv);
 
 Performs some preprocessing to the string to ensure that
 it has the correct characteristics of a version.  Flags the
 object if it contains an underscore (which denotes this
-is a beta version).
+is a alpha version).  The boolean qv denotes that the version
+should be interpreted as if it had multiple decimals, even if
+it doesn't.
 
-       char*   scan_version(char *vstr, SV *sv)
+       char*   scan_version(char *vstr, SV *sv, bool qv)
 
 =for hackers
 Found in file util.c
diff --git a/proto.h b/proto.h
index ee315bf..8f3a2e0 100644 (file)
--- a/proto.h
+++ b/proto.h
@@ -514,7 +514,7 @@ PERL_CALLCONV OP*   Perl_newWHILEOP(pTHX_ I32 flags, I32 debuggable, LOOP* loop, I
 
 PERL_CALLCONV PERL_SI* Perl_new_stackinfo(pTHX_ I32 stitems, I32 cxitems);
 PERL_CALLCONV char*    Perl_scan_vstring(pTHX_ char *vstr, SV *sv);
-PERL_CALLCONV char*    Perl_scan_version(pTHX_ char *vstr, SV *sv);
+PERL_CALLCONV char*    Perl_scan_version(pTHX_ char *vstr, SV *sv, bool qv);
 PERL_CALLCONV SV*      Perl_new_version(pTHX_ SV *ver);
 PERL_CALLCONV SV*      Perl_upg_version(pTHX_ SV *ver);
 PERL_CALLCONV SV*      Perl_vnumify(pTHX_ SV *vs);
index fa4dc18..0e3c22d 100755 (executable)
@@ -111,7 +111,7 @@ print "ok ",$i++,"\n";
     print "ok ",$i++,"\n";
 
     eval "use lib v100.105";
-    unless ($@ =~ /lib version 100\.105 required--this is only version 35\.3/) {
+    unless ($@ =~ /lib version 100\.105\.0 required--this is only version 35\.360\.0/) {
        print "not ";
     }
     print "ok ",$i++,"\n";
@@ -121,7 +121,7 @@ print "ok ",$i++,"\n";
     print "ok ",$i++,"\n";
 
     eval "use lib 100.105";
-    unless ($@ =~ /lib version 100\.105 required--this is only version 35\.3/) {
+    unless ($@ =~ /lib version 100\.105\.0 required--this is only version 35\.360\.0/) {
        print "not ";
     }
     print "ok ",$i++,"\n";
@@ -132,7 +132,7 @@ print "ok ",$i++,"\n";
     print "ok ",$i++,"\n";
 
     eval "use lib v100.105";
-    unless ($@ =~ /lib version 100\.105 required--this is only version 35\.36/) {
+    unless ($@ =~ /lib version 100\.105\.0 required--this is only version 35\.360\.0/) {
        print "not ";
     }
     print "ok ",$i++,"\n";
@@ -142,7 +142,7 @@ print "ok ",$i++,"\n";
     print "ok ",$i++,"\n";
 
     eval "use lib 100.105";
-    unless ($@ =~ /lib version 100\.105 required--this is only version 35\.36/) {
+    unless ($@ =~ /lib version 100\.105\.0 required--this is only version 35\.360\.0/) {
        print "not ";
     }
     print "ok ",$i++,"\n";
@@ -153,7 +153,7 @@ print "ok ",$i++,"\n";
     print "ok ",$i++,"\n";
 
     eval "use lib v100.105";
-    unless ($@ =~ /lib version 100\.105 required--this is only version 35\.36/) {
+    unless ($@ =~ /lib version 100\.105\.0 required--this is only version 35\.36\.0/) {
        print "not ";
     }
     print "ok ",$i++,"\n";
@@ -163,7 +163,7 @@ print "ok ",$i++,"\n";
     print "ok ",$i++,"\n";
 
     eval "use lib 100.105";
-    unless ($@ =~ /lib version 100\.105 required--this is only version 35\.36/) {
+    unless ($@ =~ /lib version 100\.105\.0 required--this is only version 35\.36\.0/) {
        print "not ";
     }
     print "ok ",$i++,"\n";
index ebc22d1..4587c3f 100755 (executable)
@@ -121,7 +121,7 @@ test ! $a->can("export_tags");      # a method in Exporter
 test (eval { $a->VERSION }) == 2.718;
 
 test ! (eval { $a->VERSION(2.719) }) &&
-         $@ =~ /^Alice version 2.71(?:9|8999\d+) required--this is only version 2.718 at /;
+         $@ =~ /^Alice version 2\.719\.0 required--this is only version 2\.718\.0 at /;
 
 test (eval { $a->VERSION(2.718) }) && ! $@;
 
index a6c1c41..b84e554 100644 (file)
@@ -174,6 +174,7 @@ XS(XS_version_vcmp);
 XS(XS_version_boolean);
 XS(XS_version_noop);
 XS(XS_version_is_alpha);
+XS(XS_version_qv);
 XS(XS_utf8_is_utf8);
 XS(XS_utf8_valid);
 XS(XS_utf8_encode);
@@ -217,6 +218,7 @@ Perl_boot_core_UNIVERSAL(pTHX)
        newXS("version::(nomethod", XS_version_noop, file);
        newXS("version::noop", XS_version_noop, file);
        newXS("version::is_alpha", XS_version_is_alpha, file);
+       newXS("version::qv", XS_version_qv, file);
     }
     newXS("utf8::is_utf8", XS_utf8_is_utf8, file);
     newXS("utf8::valid", XS_utf8_valid, file);
@@ -332,6 +334,8 @@ XS(XS_UNIVERSAL_VERSION)
         SV *nsv = sv_newmortal();
         sv_setsv(nsv, sv);
         sv = nsv;
+       if ( !sv_derived_from(sv, "version"))
+           upg_version(sv);
         undef = Nullch;
     }
     else {
@@ -355,13 +359,16 @@ XS(XS_UNIVERSAL_VERSION)
                             "%s defines neither package nor VERSION--version check failed", str);
             }
        }
-       if ( !sv_derived_from(sv, "version"))
-           sv = new_version(sv);
 
-       if ( !sv_derived_from(req, "version"))
-           req = new_version(req);
+       if ( !sv_derived_from(req, "version")) {
+           /* req may very well be R/O, so create a new object */
+           SV *nsv = sv_newmortal();
+           sv_setsv(nsv, req);
+           req = nsv;
+           upg_version(req);
+       }
 
-       if ( vcmp( SvRV(req), SvRV(sv) ) > 0 )
+       if ( vcmp( req, sv ) > 0 )
            Perl_croak(aTHX_
                "%s version %"SVf" required--this is only version %"SVf,
                HvNAME(pkg), req, sv);
@@ -379,15 +386,20 @@ XS(XS_version_new)
        Perl_croak(aTHX_ "Usage: version::new(class, version)");
     SP -= items;
     {
-/*     char *  class = (char *)SvPV_nolen(ST(0)); */
-        SV *version = ST(1);
+       char *  class = (char *)SvPV_nolen(ST(0));
+        SV *vs = ST(1);
+       SV *rv;
        if (items == 3 )
        {
-           char *vs = savepvn(SvPVX(ST(2)),SvCUR(ST(2)));
-           version = Perl_newSVpvf(aTHX_ "v%s",vs);
+           vs = sv_newmortal(); 
+           Perl_sv_setpvf(aTHX_ vs,"v%s",SvPV_nolen(ST(2)));
        }
 
-       PUSHs(new_version(version));
+       rv = new_version(vs);
+       if ( strcmp(class,"version") != 0 ) /* inherited new() */
+           sv_bless(rv, gv_stashpv(class,TRUE));
+
+       PUSHs(sv_2mortal(rv));
        PUTBACK;
        return;
     }
@@ -409,9 +421,7 @@ XS(XS_version_stringify)
          else
               Perl_croak(aTHX_ "lobj is not of type version");
 
-         {
-              PUSHs(vstringify(lobj));
-         }
+         PUSHs(sv_2mortal(vstringify(lobj)));
 
          PUTBACK;
          return;
@@ -434,9 +444,7 @@ XS(XS_version_numify)
          else
               Perl_croak(aTHX_ "lobj is not of type version");
 
-         {
-              PUSHs(vnumify(lobj));
-         }
+         PUSHs(sv_2mortal(vnumify(lobj)));
 
          PUTBACK;
          return;
@@ -480,7 +488,7 @@ XS(XS_version_vcmp)
                    rs = newSViv(vcmp(lobj,rvs));
               }
 
-              PUSHs(rs);
+              PUSHs(sv_2mortal(rs));
          }
 
          PUTBACK;
@@ -507,7 +515,7 @@ XS(XS_version_boolean)
          {
               SV       *rs;
               rs = newSViv( vcmp(lobj,new_version(newSVpvn("0",1))) );
-              PUSHs(rs);
+              PUSHs(sv_2mortal(rs));
          }
 
          PUTBACK;
@@ -566,6 +574,43 @@ XS(XS_version_is_alpha)
     }
 }
 
+XS(XS_version_qv)
+{
+    dXSARGS;
+    if (items != 1)
+       Perl_croak(aTHX_ "Usage: version::qv(ver)");
+    SP -= items;
+    {
+       SV *    ver = ST(0);
+       if ( !SvVOK(ver) ) /* only need to do with if not already v-string */
+       {
+           SV *vs = sv_newmortal();
+           char *version;
+           if ( SvNOK(ver) ) /* may get too much accuracy */
+           {
+               char tbuf[64];
+               sprintf(tbuf,"%.9"NVgf, SvNVX(ver));
+               version = savepv(tbuf);
+           }
+           else
+           {
+               version = savepv(SvPV_nolen(ver));
+           }
+           (void)scan_version(version,vs,TRUE);
+           Safefree(version);
+
+           PUSHs(vs);
+       }
+       else
+       {
+           PUSHs(sv_2mortal(new_version(ver)));
+       }
+
+       PUTBACK;
+       return;
+    }
+}
+
 XS(XS_utf8_is_utf8)
 {
      dXSARGS;
diff --git a/util.c b/util.c
index b20cd8c..0927477 100644 (file)
--- a/util.c
+++ b/util.c
@@ -3663,19 +3663,21 @@ an RV.
 
 Function must be called with an already existing SV like
 
-    sv = NEWSV(92,0);
-    s = scan_version(s,sv);
+    sv = newSV(0);
+    s = scan_version(s,SV *sv, bool qv);
 
 Performs some preprocessing to the string to ensure that
 it has the correct characteristics of a version.  Flags the
 object if it contains an underscore (which denotes this
-is a beta version).
+is a alpha version).  The boolean qv denotes that the version
+should be interpreted as if it had multiple decimals, even if
+it doesn't.
 
 =cut
 */
 
 char *
-Perl_scan_version(pTHX_ char *s, SV *rv)
+Perl_scan_version(pTHX_ char *s, SV *rv, bool qv)
 {
     const char *start = s;
     char *pos = s;
@@ -3703,7 +3705,10 @@ Perl_scan_version(pTHX_ char *s, SV *rv)
     }
     pos = s;
 
-    if (*pos == 'v') pos++;  /* get past 'v' */
+    if (*pos == 'v') {
+       pos++;  /* get past 'v' */
+       qv = 1; /* force quoted version processing */
+    }
     while (isDIGIT(*pos))
        pos++;
     if (!isALPHA(*pos)) {
@@ -3719,13 +3724,13 @@ Perl_scan_version(pTHX_ char *s, SV *rv)
                I32 mult = 1;
                I32 orev;
                if ( s < pos && s > start && *(s-1) == '_' ) {
-                       mult *= -1;     /* beta version */
+                       mult *= -1;     /* alpha version */
                }
                /* the following if() will only be true after the decimal
                 * point of a version originally created with a bare
                 * floating point number, i.e. not quoted in any way
                 */
-               if ( s > start+1 && saw_period == 1 && !saw_under ) {
+               if ( !qv && s > start+1 && saw_period == 1 && !saw_under ) {
                    mult = 100;
                    while ( s < end ) {
                        orev = rev;
@@ -3784,24 +3789,21 @@ SV *
 Perl_new_version(pTHX_ SV *ver)
 {
     SV *rv = newSV(0);
-    char *version;
-    if ( SvNOK(ver) ) /* may get too much accuracy */ 
-    {
-       char tbuf[64];
-       sprintf(tbuf,"%.9"NVgf, SvNVX(ver));
-       version = savepv(tbuf);
-    }
 #ifdef SvVOK
-    else if ( SvVOK(ver) ) { /* already a v-string */
+    if ( SvVOK(ver) ) { /* already a v-string */
+       char *version;
        MAGIC* mg = mg_find(ver,PERL_MAGIC_vstring);
        version = savepvn( (const char*)mg->mg_ptr,mg->mg_len );
+       sv_setpv(rv,version);
+       Safefree(version);
     }
+    else {
 #endif
-    else /* must be a string or something like a string */
-    {
-       version = (char *)SvPV(ver,PL_na);
+    sv_setsv(rv,ver); /* make a duplicate */
+#ifdef SvVOK
     }
-    version = scan_version(version,rv);
+#endif
+    upg_version(rv);
     return rv;
 }
 
@@ -3820,14 +3822,29 @@ Returns a pointer to the upgraded SV.
 SV *
 Perl_upg_version(pTHX_ SV *ver)
 {
-    char *version = savepvn(SvPVX(ver),SvCUR(ver));
+    char *version;
+    bool qv = 0;
+
+    if ( SvNOK(ver) ) /* may get too much accuracy */ 
+    {
+       char tbuf[64];
+       sprintf(tbuf,"%.9"NVgf, SvNVX(ver));
+       version = savepv(tbuf);
+    }
 #ifdef SvVOK
-    if ( SvVOK(ver) ) { /* already a v-string */
+    else if ( SvVOK(ver) ) { /* already a v-string */
        MAGIC* mg = mg_find(ver,PERL_MAGIC_vstring);
        version = savepvn( (const char*)mg->mg_ptr,mg->mg_len );
+       qv = 1;
     }
 #endif
-    version = scan_version(version,ver);
+    else /* must be a string or something like a string */
+    {
+       STRLEN n_a;
+       version = savepv(SvPV(ver,n_a));
+    }
+    (void)scan_version(version, ver, qv);
+    Safefree(version);
     return ver;
 }
 
@@ -3850,7 +3867,7 @@ SV *
 Perl_vnumify(pTHX_ SV *vs)
 {
     I32 i, len, digit;
-    SV *sv = NEWSV(92,0);
+    SV *sv = newSV(0);
     if ( SvROK(vs) )
        vs = SvRV(vs);
     len = av_len((AV *)vs);
@@ -3890,7 +3907,7 @@ SV *
 Perl_vstringify(pTHX_ SV *vs)
 {
     I32 i, len, digit;
-    SV *sv = NEWSV(92,0);
+    SV *sv = newSV(0);
     if ( SvROK(vs) )
        vs = SvRV(vs);
     len = av_len((AV *)vs);
@@ -3909,8 +3926,12 @@ Perl_vstringify(pTHX_ SV *vs)
        else
            Perl_sv_catpvf(aTHX_ sv,".%"IVdf,(IV)digit);
     }
-    if ( len == 0 )
-        Perl_sv_catpv(aTHX_ sv,".0");
+    
+    if ( len <= 2 ) { /* short version, must be at least three */
+       for ( len = 2 - len; len != 0; len-- )
+           Perl_sv_catpv(aTHX_ sv,".0");
+    }
+
     return sv;
 } 
 
@@ -3940,23 +3961,36 @@ Perl_vcmp(pTHX_ SV *lsv, SV *rsv)
     {
        I32 left  = SvIV(*av_fetch((AV *)lsv,i,0));
        I32 right = SvIV(*av_fetch((AV *)rsv,i,0));
-       bool lbeta = left  < 0 ? 1 : 0;
-       bool rbeta = right < 0 ? 1 : 0;
-       left  = PERL_ABS(left);
-       right = PERL_ABS(right);
-       if ( left < right || (left == right && lbeta && !rbeta) )
+       bool lalpha = left  < 0 ? 1 : 0;
+       bool ralpha = right < 0 ? 1 : 0;
+       left  = abs(left);
+       right = abs(right);
+       if ( left < right || (left == right && lalpha && !ralpha) )
            retval = -1;
-       if ( left > right || (left == right && rbeta && !lbeta) )
+       if ( left > right || (left == right && ralpha && !lalpha) )
            retval = +1;
        i++;
     }
 
-    if ( l != r && retval == 0 ) /* possible match except for trailing 0 */
+    if ( l != r && retval == 0 ) /* possible match except for trailing 0's */
     {
-       if ( !( l < r && r-l == 1 && SvIV(*av_fetch((AV *)rsv,r,0)) == 0 ) &&
-            !( l-r == 1 && SvIV(*av_fetch((AV *)lsv,l,0)) == 0 ) )
+       if ( l < r )
        {
-           retval = l < r ? -1 : +1; /* not a match after all */
+           while ( i <= r && retval == 0 )
+           {
+               if ( SvIV(*av_fetch((AV *)rsv,i,0)) != 0 )
+                   retval = -1; /* not a match after all */
+               i++;
+           }
+       }
+       else
+       {
+           while ( i <= l && retval == 0 )
+           {
+               if ( SvIV(*av_fetch((AV *)lsv,i,0)) != 0 )
+                   retval = +1; /* not a match after all */
+               i++;
+           }
        }
     }
     return retval;