From: Matt S Trout <mst@shadowcat.co.uk>
Date: Mon, 30 Jul 2012 19:56:55 +0000 (+0000)
Subject: initial import of version::fallback
X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=bff17d1b7ff09f3ba75bf91fa6437a44787dfd96;p=p5sagit%2Fversion-fallback.git

initial import of version::fallback
---

bff17d1b7ff09f3ba75bf91fa6437a44787dfd96
diff --git a/lib/version/fallback.pm b/lib/version/fallback.pm
new file mode 100644
index 0000000..9f90ac3
--- /dev/null
+++ b/lib/version/fallback.pm
@@ -0,0 +1,17 @@
+package version::fallback;
+
+use strict;
+use warnings;
+use base qw(version::fallback::vpp);
+
+our @EXPORT = qw(qv);
+
+our $VERSION = '0.99';
+
+sub import {
+  no strict 'refs';
+  no warnings 'redefine';
+  *{"${\caller}::qv"} = \&version::fallback::vpp::qv;
+}
+
+1;
diff --git a/lib/version/fallback/vpp.pm b/lib/version/fallback/vpp.pm
new file mode 100644
index 0000000..46d7b54
--- /dev/null
+++ b/lib/version/fallback/vpp.pm
@@ -0,0 +1,951 @@
+# ripped from vpp.pm, part of the perl licensed version-0.99 dist by JPEACOCK
+#
+# version::vpp renamed to version::fallback::vpp
+# charstar renamed to version::fallback::charstar
+# 'new charstar $s' changed to 'version::fallback::charstar->new($s)'
+
+package version::fallback::charstar;
+# a little helper class to emulate C char* semantics in Perl
+# so that prescan_version can use the same code as in C
+
+use overload (
+    '""'	=> \&thischar,
+    '0+'	=> \&thischar,
+    '++'	=> \&increment,
+    '--'	=> \&decrement,
+    '+'		=> \&plus,
+    '-'		=> \&minus,
+    '*'		=> \&multiply,
+    'cmp'	=> \&cmp,
+    '<=>'	=> \&spaceship,
+    'bool'	=> \&thischar,
+    '='		=> \&clone,
+);
+
+sub new {
+    my ($self, $string) = @_;
+    my $class = ref($self) || $self;
+
+    my $obj = {
+	string  => [split(//,$string)],
+	current => 0,
+    };
+    return bless $obj, $class;
+}
+
+sub thischar {
+    my ($self) = @_;
+    my $last = $#{$self->{string}};
+    my $curr = $self->{current};
+    if ($curr >= 0 && $curr <= $last) {
+	return $self->{string}->[$curr];
+    }
+    else {
+	return '';
+    }
+}
+
+sub increment {
+    my ($self) = @_;
+    $self->{current}++;
+}
+
+sub decrement {
+    my ($self) = @_;
+    $self->{current}--;
+}
+
+sub plus {
+    my ($self, $offset) = @_;
+    my $rself = $self->clone;
+    $rself->{current} += $offset;
+    return $rself;
+}
+
+sub minus {
+    my ($self, $offset) = @_;
+    my $rself = $self->clone;
+    $rself->{current} -= $offset;
+    return $rself;
+}
+
+sub multiply {
+    my ($left, $right, $swapped) = @_;
+    my $char = $left->thischar();
+    return $char * $right;
+}
+
+sub spaceship {
+    my ($left, $right, $swapped) = @_;
+    unless (ref($right)) { # not an object already
+	$right = $left->new($right);
+    }
+    return $left->{current} <=> $right->{current};
+}
+
+sub cmp {
+    my ($left, $right, $swapped) = @_;
+    unless (ref($right)) { # not an object already
+	if (length($right) == 1) { # comparing single character only
+	    return $left->thischar cmp $right;
+	}
+	$right = $left->new($right);
+    }
+    return $left->currstr cmp $right->currstr;
+}
+
+sub bool {
+    my ($self) = @_;
+    my $char = $self->thischar;
+    return ($char ne '');
+}
+
+sub clone {
+    my ($left, $right, $swapped) = @_;
+    $right = {
+	string  => [@{$left->{string}}],
+	current => $left->{current},
+    };
+    return bless $right, ref($left);
+}
+
+sub currstr {
+    my ($self, $s) = @_;
+    my $curr = $self->{current};
+    my $last = $#{$self->{string}};
+    if (defined($s) && $s->{current} < $last) {
+	$last = $s->{current};
+    }
+
+    my $string = join('', @{$self->{string}}[$curr..$last]);
+    return $string;
+}
+
+package version::fallback::vpp;
+use strict;
+
+use POSIX qw/locale_h/;
+use locale;
+use vars qw ($VERSION @ISA @REGEXS);
+$VERSION = 0.99;
+
+use overload (
+    '""'       => \&stringify,
+    '0+'       => \&numify,
+    'cmp'      => \&vcmp,
+    '<=>'      => \&vcmp,
+    'bool'     => \&vbool,
+    '+'        => \&vnoop,
+    '-'        => \&vnoop,
+    '*'        => \&vnoop,
+    '/'        => \&vnoop,
+    '+='        => \&vnoop,
+    '-='        => \&vnoop,
+    '*='        => \&vnoop,
+    '/='        => \&vnoop,
+    'abs'      => \&vnoop,
+);
+
+eval "use warnings";
+if ($@) {
+    eval '
+	package
+	warnings;
+	sub enabled {return $^W;}
+	1;
+    ';
+}
+
+my $VERSION_MAX = 0x7FFFFFFF;
+
+# implement prescan_version as closely to the C version as possible
+use constant TRUE  => 1;
+use constant FALSE => 0;
+
+sub isDIGIT {
+    my ($char) = shift->thischar();
+    return ($char =~ /\d/);
+}
+
+sub isALPHA {
+    my ($char) = shift->thischar();
+    return ($char =~ /[a-zA-Z]/);
+}
+
+sub isSPACE {
+    my ($char) = shift->thischar();
+    return ($char =~ /\s/);
+}
+
+sub BADVERSION {
+    my ($s, $errstr, $error) = @_;
+    if ($errstr) {
+	$$errstr = $error;
+    }
+    return $s;
+}
+
+sub prescan_version {
+    my ($s, $strict, $errstr, $sqv, $ssaw_decimal, $swidth, $salpha) = @_;
+    my $qv          = defined $sqv          ? $$sqv          : FALSE;
+    my $saw_decimal = defined $ssaw_decimal ? $$ssaw_decimal : 0;
+    my $width       = defined $swidth       ? $$swidth       : 3;
+    my $alpha       = defined $salpha       ? $$salpha       : FALSE;
+
+    my $d = $s;
+
+    if ($qv && isDIGIT($d)) {
+	goto dotted_decimal_version;
+    }
+
+    if ($d eq 'v') { # explicit v-string
+	$d++;
+	if (isDIGIT($d)) {
+	    $qv = TRUE;
+	}
+	else { # degenerate v-string
+	    # requires v1.2.3
+	    return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
+	}
+
+dotted_decimal_version:
+	if ($strict && $d eq '0' && isDIGIT($d+1)) {
+	    # no leading zeros allowed
+	    return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
+	}
+
+	while (isDIGIT($d)) { 	# integer part
+	    $d++;
+	}
+
+	if ($d eq '.')
+	{
+	    $saw_decimal++;
+	    $d++; 		# decimal point
+	}
+	else
+	{
+	    if ($strict) {
+		# require v1.2.3
+		return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
+	    }
+	    else {
+		goto version_prescan_finish;
+	    }
+	}
+
+	{
+	    my $i = 0;
+	    my $j = 0;
+	    while (isDIGIT($d)) {	# just keep reading
+		$i++;
+		while (isDIGIT($d)) {
+		    $d++; $j++;
+		    # maximum 3 digits between decimal
+		    if ($strict && $j > 3) {
+			return BADVERSION($s,$errstr,"Invalid version format (maximum 3 digits between decimals)");
+		    }
+		}
+		if ($d eq '_') {
+		    if ($strict) {
+			return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
+		    }
+		    if ( $alpha ) {
+			return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
+		    }
+		    $d++;
+		    $alpha = TRUE;
+		}
+		elsif ($d eq '.') {
+		    if ($alpha) {
+			return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
+		    }
+		    $saw_decimal++;
+		    $d++;
+		}
+		elsif (!isDIGIT($d)) {
+		    last;
+		}
+		$j = 0;
+	    }
+
+	    if ($strict && $i < 2) {
+		# requires v1.2.3
+		return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
+	    }
+	}
+    } 					# end if dotted-decimal
+    else
+    {					# decimal versions
+	# special $strict case for leading '.' or '0'
+	if ($strict) {
+	    if ($d eq '.') {
+		return BADVERSION($s,$errstr,"Invalid version format (0 before decimal required)");
+	    }
+	    if ($d eq '0' && isDIGIT($d+1)) {
+		return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
+	    }
+	}
+
+	# and we never support negative version numbers
+	if ($d eq '-') {
+	    return BADVERSION($s,$errstr,"Invalid version format (negative version number)");
+	}
+
+	# consume all of the integer part
+	while (isDIGIT($d)) {
+	    $d++;
+	}
+
+	# look for a fractional part
+	if ($d eq '.') {
+	    # we found it, so consume it
+	    $saw_decimal++;
+	    $d++;
+	}
+	elsif (!$d || $d eq ';' || isSPACE($d) || $d eq '}') {
+	    if ( $d == $s ) {
+		# found nothing
+		return BADVERSION($s,$errstr,"Invalid version format (version required)");
+	    }
+	    # found just an integer
+	    goto version_prescan_finish;
+	}
+	elsif ( $d == $s ) {
+	    # didn't find either integer or period
+	    return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
+	}
+	elsif ($d eq '_') {
+	    # underscore can't come after integer part
+	    if ($strict) {
+		return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
+	    }
+	    elsif (isDIGIT($d+1)) {
+		return BADVERSION($s,$errstr,"Invalid version format (alpha without decimal)");
+	    }
+	    else {
+		return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
+	    }
+	}
+	elsif ($d) {
+	    # anything else after integer part is just invalid data
+	    return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
+	}
+
+	# scan the fractional part after the decimal point
+	if ($d && !isDIGIT($d) && ($strict || ! ($d eq ';' || isSPACE($d) || $d eq '}') )) {
+		# $strict or lax-but-not-the-end
+		return BADVERSION($s,$errstr,"Invalid version format (fractional part required)");
+	}
+
+	while (isDIGIT($d)) {
+	    $d++;
+	    if ($d eq '.' && isDIGIT($d-1)) {
+		if ($alpha) {
+		    return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
+		}
+		if ($strict) {
+		    return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
+		}
+		$d = $s; # start all over again
+		$qv = TRUE;
+		goto dotted_decimal_version;
+	    }
+	    if ($d eq '_') {
+		if ($strict) {
+		    return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
+		}
+		if ( $alpha ) {
+		    return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
+		}
+		if ( ! isDIGIT($d+1) ) {
+		    return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
+		}
+		$d++;
+		$alpha = TRUE;
+	    }
+	}
+    }
+
+version_prescan_finish:
+    while (isSPACE($d)) {
+	$d++;
+    }
+
+    if ($d && !isDIGIT($d) && (! ($d eq ';' || $d eq '}') )) {
+	# trailing non-numeric data
+	return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
+    }
+
+    if (defined $sqv) {
+	$$sqv = $qv;
+    }
+    if (defined $swidth) {
+	$$swidth = $width;
+    }
+    if (defined $ssaw_decimal) {
+	$$ssaw_decimal = $saw_decimal;
+    }
+    if (defined $salpha) {
+	$$salpha = $alpha;
+    }
+    return $d;
+}
+
+sub scan_version {
+    my ($s, $rv, $qv) = @_;
+    my $start;
+    my $pos;
+    my $last;
+    my $errstr;
+    my $saw_decimal = 0;
+    my $width = 3;
+    my $alpha = FALSE;
+    my $vinf = FALSE;
+    my @av;
+
+    $s = version::fallback::charstar->new($s);
+
+    while (isSPACE($s)) { # leading whitespace is OK
+	$s++;
+    }
+
+    $last = prescan_version($s, FALSE, \$errstr, \$qv, \$saw_decimal,
+	\$width, \$alpha);
+
+    if ($errstr) {
+	# 'undef' is a special case and not an error
+	if ( $s ne 'undef') {
+	    use Carp;
+	    Carp::croak($errstr);
+	}
+    }
+
+    $start = $s;
+    if ($s eq 'v') {
+	$s++;
+    }
+    $pos = $s;
+
+    if ( $qv ) {
+	$$rv->{qv} = $qv;
+    }
+    if ( $alpha ) {
+	$$rv->{alpha} = $alpha;
+    }
+    if ( !$qv && $width < 3 ) {
+	$$rv->{width} = $width;
+    }
+
+    while (isDIGIT($pos)) {
+	$pos++;
+    }
+    if (!isALPHA($pos)) {
+	my $rev;
+
+	for (;;) {
+	    $rev = 0;
+	    {
+  		# this is atoi() that delimits on underscores
+  		my $end = $pos;
+  		my $mult = 1;
+		my $orev;
+
+		#  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 ( !$qv && $s > $start && $saw_decimal == 1 ) {
+		    $mult *= 100;
+ 		    while ( $s < $end ) {
+			$orev = $rev;
+ 			$rev += $s * $mult;
+ 			$mult /= 10;
+			if (   (abs($orev) > abs($rev))
+			    || (abs($rev) > $VERSION_MAX )) {
+			    warn("Integer overflow in version %d",
+					   $VERSION_MAX);
+			    $s = $end - 1;
+			    $rev = $VERSION_MAX;
+			    $vinf = 1;
+			}
+ 			$s++;
+			if ( $s eq '_' ) {
+			    $s++;
+			}
+ 		    }
+  		}
+ 		else {
+ 		    while (--$end >= $s) {
+			$orev = $rev;
+ 			$rev += $end * $mult;
+ 			$mult *= 10;
+			if (   (abs($orev) > abs($rev))
+			    || (abs($rev) > $VERSION_MAX )) {
+			    warn("Integer overflow in version");
+			    $end = $s - 1;
+			    $rev = $VERSION_MAX;
+			    $vinf = 1;
+			}
+ 		    }
+ 		}
+  	    }
+
+  	    # Append revision
+	    push @av, $rev;
+	    if ( $vinf ) {
+		$s = $last;
+		last;
+	    }
+	    elsif ( $pos eq '.' ) {
+		$s = ++$pos;
+	    }
+	    elsif ( $pos eq '_' && isDIGIT($pos+1) ) {
+		$s = ++$pos;
+	    }
+	    elsif ( $pos eq ',' && isDIGIT($pos+1) ) {
+		$s = ++$pos;
+	    }
+	    elsif ( isDIGIT($pos) ) {
+		$s = $pos;
+	    }
+	    else {
+		$s = $pos;
+		last;
+	    }
+	    if ( $qv ) {
+		while ( isDIGIT($pos) ) {
+		    $pos++;
+		}
+	    }
+	    else {
+		my $digits = 0;
+		while ( ( isDIGIT($pos) || $pos eq '_' ) && $digits < 3 ) {
+		    if ( $pos ne '_' ) {
+			$digits++;
+		    }
+		    $pos++;
+		}
+	    }
+	}
+    }
+    if ( $qv ) { # quoted versions always get at least three terms
+	my $len = $#av;
+	#  This for loop appears to trigger a compiler bug on OS X, as it
+	#  loops infinitely. Yes, len is negative. No, it makes no sense.
+	#  Compiler in question is:
+	#  gcc version 3.3 20030304 (Apple Computer, Inc. build 1640)
+	#  for ( len = 2 - len; len > 0; len-- )
+	#  av_push(MUTABLE_AV(sv), newSViv(0));
+	#
+	$len = 2 - $len;
+	while ($len-- > 0) {
+	    push @av, 0;
+	}
+    }
+
+    # need to save off the current version string for later
+    if ( $vinf ) {
+	$$rv->{original} = "v.Inf";
+	$$rv->{vinf} = 1;
+    }
+    elsif ( $s > $start ) {
+	$$rv->{original} = $start->currstr($s);
+	if ( $qv && $saw_decimal == 1 && $start ne 'v' ) {
+	    # need to insert a v to be consistent
+	    $$rv->{original} = 'v' . $$rv->{original};
+	}
+    }
+    else {
+	$$rv->{original} = '0';
+	push(@av, 0);
+    }
+
+    # And finally, store the AV in the hash
+    $$rv->{version} = \@av;
+
+    # fix RT#19517 - special case 'undef' as string
+    if ($s eq 'undef') {
+	$s += 5;
+    }
+
+    return $s;
+}
+
+sub new
+{
+	my ($class, $value) = @_;
+	my $self = bless ({}, ref ($class) || $class);
+	my $qv = FALSE;
+
+	if ( ref($value) && eval('$value->isa("version")') ) {
+	    # Can copy the elements directly
+	    $self->{version} = [ @{$value->{version} } ];
+	    $self->{qv} = 1 if $value->{qv};
+	    $self->{alpha} = 1 if $value->{alpha};
+	    $self->{original} = ''.$value->{original};
+	    return $self;
+	}
+
+	my $currlocale = setlocale(LC_ALL);
+
+	# if the current locale uses commas for decimal points, we
+	# just replace commas with decimal places, rather than changing
+	# locales
+	if ( localeconv()->{decimal_point} eq ',' ) {
+	    $value =~ tr/,/./;
+	}
+
+	if ( not defined $value or $value =~ /^undef$/ ) {
+	    # RT #19517 - special case for undef comparison
+	    # or someone forgot to pass a value
+	    push @{$self->{version}}, 0;
+	    $self->{original} = "0";
+	    return ($self);
+	}
+
+	if ( $#_ == 2 ) { # must be CVS-style
+	    $value = $_[2];
+	    $qv = TRUE;
+	}
+
+	$value = _un_vstring($value);
+
+	# exponential notation
+	if ( $value =~ /\d+.?\d*e[-+]?\d+/ ) {
+	    $value = sprintf("%.9f",$value);
+	    $value =~ s/(0+)$//; # trim trailing zeros
+	}
+
+	my $s = scan_version($value, \$self, $qv);
+
+	if ($s) { # must be something left over
+	    warn("Version string '%s' contains invalid data; "
+                       ."ignoring: '%s'", $value, $s);
+	}
+
+	return ($self);
+}
+
+*parse = \&new;
+
+sub numify
+{
+    my ($self) = @_;
+    unless (_verify($self)) {
+	require Carp;
+	Carp::croak("Invalid version object");
+    }
+    my $width = $self->{width} || 3;
+    my $alpha = $self->{alpha} || "";
+    my $len = $#{$self->{version}};
+    my $digit = $self->{version}[0];
+    my $string = sprintf("%d.", $digit );
+
+    for ( my $i = 1 ; $i < $len ; $i++ ) {
+	$digit = $self->{version}[$i];
+	if ( $width < 3 ) {
+	    my $denom = 10**(3-$width);
+	    my $quot = int($digit/$denom);
+	    my $rem = $digit - ($quot * $denom);
+	    $string .= sprintf("%0".$width."d_%d", $quot, $rem);
+	}
+	else {
+	    $string .= sprintf("%03d", $digit);
+	}
+    }
+
+    if ( $len > 0 ) {
+	$digit = $self->{version}[$len];
+	if ( $alpha && $width == 3 ) {
+	    $string .= "_";
+	}
+	$string .= sprintf("%0".$width."d", $digit);
+    }
+    else # $len = 0
+    {
+	$string .= sprintf("000");
+    }
+
+    return $string;
+}
+
+sub normal
+{
+    my ($self) = @_;
+    unless (_verify($self)) {
+	require Carp;
+	Carp::croak("Invalid version object");
+    }
+    my $alpha = $self->{alpha} || "";
+    my $len = $#{$self->{version}};
+    my $digit = $self->{version}[0];
+    my $string = sprintf("v%d", $digit );
+
+    for ( my $i = 1 ; $i < $len ; $i++ ) {
+	$digit = $self->{version}[$i];
+	$string .= sprintf(".%d", $digit);
+    }
+
+    if ( $len > 0 ) {
+	$digit = $self->{version}[$len];
+	if ( $alpha ) {
+	    $string .= sprintf("_%0d", $digit);
+	}
+	else {
+	    $string .= sprintf(".%0d", $digit);
+	}
+    }
+
+    if ( $len <= 2 ) {
+	for ( $len = 2 - $len; $len != 0; $len-- ) {
+	    $string .= sprintf(".%0d", 0);
+	}
+    }
+
+    return $string;
+}
+
+sub stringify
+{
+    my ($self) = @_;
+    unless (_verify($self)) {
+	require Carp;
+	Carp::croak("Invalid version object");
+    }
+    return exists $self->{original}
+    	? $self->{original}
+	: exists $self->{qv}
+	    ? $self->normal
+	    : $self->numify;
+}
+
+sub vcmp
+{
+    require UNIVERSAL;
+    my ($left,$right,$swap) = @_;
+    my $class = ref($left);
+    unless ( UNIVERSAL::isa($right, $class) ) {
+	$right = $class->new($right);
+    }
+
+    if ( $swap ) {
+	($left, $right) = ($right, $left);
+    }
+    unless (_verify($left)) {
+	require Carp;
+	Carp::croak("Invalid version object");
+    }
+    unless (_verify($right)) {
+	require Carp;
+	Carp::croak("Invalid version format");
+    }
+    my $l = $#{$left->{version}};
+    my $r = $#{$right->{version}};
+    my $m = $l < $r ? $l : $r;
+    my $lalpha = $left->is_alpha;
+    my $ralpha = $right->is_alpha;
+    my $retval = 0;
+    my $i = 0;
+    while ( $i <= $m && $retval == 0 ) {
+	$retval = $left->{version}[$i] <=> $right->{version}[$i];
+	$i++;
+    }
+
+    # tiebreaker for alpha with identical terms
+    if ( $retval == 0
+	&& $l == $r
+	&& $left->{version}[$m] == $right->{version}[$m]
+	&& ( $lalpha || $ralpha ) ) {
+
+	if ( $lalpha && !$ralpha ) {
+	    $retval = -1;
+	}
+	elsif ( $ralpha && !$lalpha) {
+	    $retval = +1;
+	}
+    }
+
+    # possible match except for trailing 0's
+    if ( $retval == 0 && $l != $r ) {
+	if ( $l < $r ) {
+	    while ( $i <= $r && $retval == 0 ) {
+		if ( $right->{version}[$i] != 0 ) {
+		    $retval = -1; # not a match after all
+		}
+		$i++;
+	    }
+	}
+	else {
+	    while ( $i <= $l && $retval == 0 ) {
+		if ( $left->{version}[$i] != 0 ) {
+		    $retval = +1; # not a match after all
+		}
+		$i++;
+	    }
+	}
+    }
+
+    return $retval;
+}
+
+sub vbool {
+    my ($self) = @_;
+    return vcmp($self,$self->new("0"),1);
+}
+
+sub vnoop {
+    require Carp;
+    Carp::croak("operation not supported with version object");
+}
+
+sub is_alpha {
+    my ($self) = @_;
+    return (exists $self->{alpha});
+}
+
+sub qv {
+    my $value = shift;
+    my $class = 'version';
+    if (@_) {
+	$class = ref($value) || $value;
+	$value = shift;
+    }
+
+    $value = _un_vstring($value);
+    $value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/;
+    my $obj = version->new($value);
+    return bless $obj, $class;
+}
+
+*declare = \&qv;
+
+sub is_qv {
+    my ($self) = @_;
+    return (exists $self->{qv});
+}
+
+
+sub _verify {
+    my ($self) = @_;
+    if ( ref($self)
+	&& eval { exists $self->{version} }
+	&& ref($self->{version}) eq 'ARRAY'
+	) {
+	return 1;
+    }
+    else {
+	return 0;
+    }
+}
+
+sub _is_non_alphanumeric {
+    my $s = shift;
+    $s = version::fallback::charstar->new($s);
+    while ($s) {
+	return 0 if isSPACE($s); # early out
+	return 1 unless (isALPHA($s) || isDIGIT($s) || $s =~ /[.-]/);
+	$s++;
+    }
+    return 0;
+}
+
+sub _un_vstring {
+    my $value = shift;
+    # may be a v-string
+    if ( length($value) >= 3 && $value !~ /[._]/
+	&& _is_non_alphanumeric($value)) {
+	my $tvalue;
+	if ( $] ge 5.008_001 ) {
+	    $tvalue = _find_magic_vstring($value);
+	    $value = $tvalue if length $tvalue;
+	}
+	elsif ( $] ge 5.006_000 ) {
+	    $tvalue = sprintf("v%vd",$value);
+	    if ( $tvalue =~ /^v\d+(\.\d+){2,}$/ ) {
+		# must be a v-string
+		$value = $tvalue;
+	    }
+	}
+    }
+    return $value;
+}
+
+sub _find_magic_vstring {
+    my $value = shift;
+    my $tvalue = '';
+    require B;
+    my $sv = B::svref_2object(\$value);
+    my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef;
+    while ( $magic ) {
+	if ( $magic->TYPE eq 'V' ) {
+	    $tvalue = $magic->PTR;
+	    $tvalue =~ s/^v?(.+)$/v$1/;
+	    last;
+	}
+	else {
+	    $magic = $magic->MOREMAGIC;
+	}
+    }
+    return $tvalue;
+}
+
+sub _VERSION {
+    my ($obj, $req) = @_;
+    my $class = ref($obj) || $obj;
+
+    no strict 'refs';
+    if ( exists $INC{"$class.pm"} and not %{"$class\::"} and $] >= 5.008) {
+	 # file but no package
+	require Carp;
+	Carp::croak( "$class defines neither package nor VERSION"
+	    ."--version check failed");
+    }
+
+    my $version = eval "\$$class\::VERSION";
+    if ( defined $version ) {
+	local $^W if $] <= 5.008;
+	$version = version::vpp->new($version);
+    }
+
+    if ( defined $req ) {
+	unless ( defined $version ) {
+	    require Carp;
+	    my $msg =  $] < 5.006
+	    ? "$class version $req required--this is only version "
+	    : "$class does not define \$$class\::VERSION"
+	      ."--version check failed";
+
+	    if ( $ENV{VERSION_DEBUG} ) {
+		Carp::confess($msg);
+	    }
+	    else {
+		Carp::croak($msg);
+	    }
+	}
+
+	$req = version::vpp->new($req);
+
+	if ( $req > $version ) {
+	    require Carp;
+	    if ( $req->is_qv ) {
+		Carp::croak(
+		    sprintf ("%s version %s required--".
+			"this is only version %s", $class,
+			$req->normal, $version->normal)
+		);
+	    }
+	    else {
+		Carp::croak(
+		    sprintf ("%s version %s required--".
+			"this is only version %s", $class,
+			$req->stringify, $version->stringify)
+		);
+	    }
+	}
+    }
+
+    return defined $version ? $version->stringify : undef;
+}
+
+1; #this line is important and will help the module return a true value