#ifdef PERL_CORE
# include "vutil.h"
#endif
#define VERSION_MAX 0x7FFFFFFF
#ifndef STRLENs
# define STRLENs(s) (sizeof("" s "") - 1)
#endif
#ifndef POSIX_SETLOCALE_LOCK
# ifdef gwLOCALE_LOCK
# define POSIX_SETLOCALE_LOCK gwLOCALE_LOCK
# define POSIX_SETLOCALE_UNLOCK gwLOCALE_UNLOCK
# else
# define POSIX_SETLOCALE_LOCK NOOP
# define POSIX_SETLOCALE_UNLOCK NOOP
# endif
#endif
#ifndef DISABLE_LC_NUMERIC_CHANGES
# ifdef LOCK_LC_NUMERIC_STANDARD
# define DISABLE_LC_NUMERIC_CHANGES() LOCK_LC_NUMERIC_STANDARD()
# define REENABLE_LC_NUMERIC_CHANGES() UNLOCK_LC_NUMERIC_STANDARD()
# else
# define DISABLE_LC_NUMERIC_CHANGES() NOOP
# define REENABLE_LC_NUMERIC_CHANGES() NOOP
# endif
#endif
const
char
*
#ifdef VUTIL_REPLACE_CORE
Perl_prescan_version2(pTHX_
const
char
*s,
bool
strict,
#else
Perl_prescan_version(pTHX_
const
char
*s,
bool
strict,
#endif
const
char
**errstr,
bool
*sqv,
int
*ssaw_decimal,
int
*swidth,
bool
*salpha) {
bool
qv = (sqv ? *sqv : FALSE);
int
width = 3;
int
saw_decimal = 0;
bool
alpha = FALSE;
const
char
*d = s;
PERL_ARGS_ASSERT_PRESCAN_VERSION;
PERL_UNUSED_CONTEXT;
if
(qv && isDIGIT(*d))
goto
dotted_decimal_version;
if
(*d ==
'v'
) {
d++;
if
(isDIGIT(*d)) {
qv = TRUE;
}
else
{
BADVERSION(s,errstr,
"Invalid version format (dotted-decimal versions require at least three parts)"
);
}
dotted_decimal_version:
if
(strict && d[0] ==
'0'
&& isDIGIT(d[1])) {
BADVERSION(s,errstr,
"Invalid version format (no leading zeros)"
);
}
while
(isDIGIT(*d))
d++;
if
(*d ==
'.'
)
{
saw_decimal++;
d++;
}
else
{
if
(strict) {
BADVERSION(s,errstr,
"Invalid version format (dotted-decimal versions require at least three parts)"
);
}
else
{
goto
version_prescan_finish;
}
}
{
int
i = 0;
int
j = 0;
while
(isDIGIT(*d)) {
i++;
while
(isDIGIT(*d)) {
d++; j++;
if
(strict && j > 3) {
BADVERSION(s,errstr,
"Invalid version format (maximum 3 digits between decimals)"
);
}
}
if
(*d ==
'_'
) {
if
(strict) {
BADVERSION(s,errstr,
"Invalid version format (no underscores)"
);
}
if
( alpha ) {
BADVERSION(s,errstr,
"Invalid version format (multiple underscores)"
);
}
d++;
alpha = TRUE;
}
else
if
(*d ==
'.'
) {
if
(alpha) {
BADVERSION(s,errstr,
"Invalid version format (underscores before decimal)"
);
}
saw_decimal++;
d++;
}
else
if
(!isDIGIT(*d)) {
break
;
}
j = 0;
}
if
(strict && i < 2) {
BADVERSION(s,errstr,
"Invalid version format (dotted-decimal versions require at least three parts)"
);
}
}
}
else
{
int
j = 0;
if
(strict) {
if
(*d ==
'.'
) {
BADVERSION(s,errstr,
"Invalid version format (0 before decimal required)"
);
}
if
(*d ==
'0'
&& isDIGIT(d[1])) {
BADVERSION(s,errstr,
"Invalid version format (no leading zeros)"
);
}
}
if
( *d ==
'-'
) {
BADVERSION(s,errstr,
"Invalid version format (negative version number)"
);
}
while
(isDIGIT(*d))
d++;
if
(*d ==
'.'
) {
saw_decimal++;
d++;
}
else
if
(!*d || *d ==
';'
|| isSPACE(*d) || *d ==
'{'
|| *d ==
'}'
) {
if
( d == s ) {
BADVERSION(s,errstr,
"Invalid version format (version required)"
);
}
goto
version_prescan_finish;
}
else
if
( d == s ) {
BADVERSION(s,errstr,
"Invalid version format (non-numeric data)"
);
}
else
if
(*d ==
'_'
) {
if
(strict) {
BADVERSION(s,errstr,
"Invalid version format (no underscores)"
);
}
else
if
(isDIGIT(d[1])) {
BADVERSION(s,errstr,
"Invalid version format (alpha without decimal)"
);
}
else
{
BADVERSION(s,errstr,
"Invalid version format (misplaced underscore)"
);
}
}
else
{
BADVERSION(s,errstr,
"Invalid version format (non-numeric data)"
);
}
if
(!isDIGIT(*d) && (strict || ! (!*d || *d ==
';'
|| isSPACE(*d) || *d ==
'{'
|| *d ==
'}'
) )) {
BADVERSION(s,errstr,
"Invalid version format (fractional part required)"
);
}
while
(isDIGIT(*d)) {
d++; j++;
if
(*d ==
'.'
&& isDIGIT(d[-1])) {
if
(alpha) {
BADVERSION(s,errstr,
"Invalid version format (underscores before decimal)"
);
}
if
(strict) {
BADVERSION(s,errstr,
"Invalid version format (dotted-decimal versions must begin with 'v')"
);
}
d = (
char
*)s;
qv = TRUE;
goto
dotted_decimal_version;
}
if
(*d ==
'_'
) {
if
(strict) {
BADVERSION(s,errstr,
"Invalid version format (no underscores)"
);
}
if
( alpha ) {
BADVERSION(s,errstr,
"Invalid version format (multiple underscores)"
);
}
if
( ! isDIGIT(d[1]) ) {
BADVERSION(s,errstr,
"Invalid version format (misplaced underscore)"
);
}
width = j;
d++;
alpha = TRUE;
}
}
}
version_prescan_finish:
while
(isSPACE(*d))
d++;
if
(!isDIGIT(*d) && (! (!*d || *d ==
';'
|| *d ==
':'
|| *d ==
'{'
|| *d ==
'}'
) )) {
BADVERSION(s,errstr,
"Invalid version format (non-numeric data)"
);
}
if
(saw_decimal > 1 && d[-1] ==
'.'
) {
BADVERSION(s,errstr,
"Invalid version format (trailing decimal)"
);
}
if
(sqv)
*sqv = qv;
if
(swidth)
*swidth = width;
if
(ssaw_decimal)
*ssaw_decimal = saw_decimal;
if
(salpha)
*salpha = alpha;
return
d;
}
const
char
*
#ifdef VUTIL_REPLACE_CORE
Perl_scan_version2(pTHX_
const
char
*s, SV *rv,
bool
qv)
#else
Perl_scan_version(pTHX_
const
char
*s, SV *rv,
bool
qv)
#endif
{
const
char
*start = s;
const
char
*pos;
const
char
*last;
const
char
*errstr = NULL;
int
saw_decimal = 0;
int
width = 3;
bool
alpha = FALSE;
bool
vinf = FALSE;
AV * av;
SV * hv;
PERL_ARGS_ASSERT_SCAN_VERSION;
while
(isSPACE(*s))
s++;
last = PRESCAN_VERSION(s, FALSE, &errstr, &qv, &saw_decimal, &width, &alpha);
if
(errstr) {
if
( ! ( *s ==
'u'
&& strEQ(s+1,
"ndef"
)) ) {
Perl_croak(aTHX_
"%s"
, errstr);
}
}
start = s;
if
(*s ==
'v'
)
s++;
pos = s;
av = newAV();
hv = newSVrv(rv,
"version"
);
(
void
)sv_upgrade(hv, SVt_PVHV);
#ifndef NODEFAULT_SHAREKEYS
HvSHAREKEYS_on(hv);
#endif
if
( qv )
(
void
)hv_stores(MUTABLE_HV(hv),
"qv"
, newSViv(qv));
if
( alpha )
(
void
)hv_stores(MUTABLE_HV(hv),
"alpha"
, newSViv(alpha));
if
( !qv && width < 3 )
(
void
)hv_stores(MUTABLE_HV(hv),
"width"
, newSViv(width));
while
(isDIGIT(*pos) || *pos ==
'_'
)
pos++;
if
(!isALPHA(*pos)) {
I32 rev;
for
(;;) {
rev = 0;
{
const
char
*end = pos;
I32 mult = 1;
I32 orev;
if
( !qv && s > start && saw_decimal == 1 ) {
mult *= 100;
while
( s < end ) {
if
(*s ==
'_'
)
continue
;
orev = rev;
rev += (*s -
'0'
) * mult;
mult /= 10;
if
( (PERL_ABS(orev) > PERL_ABS(rev))
|| (PERL_ABS(rev) > VERSION_MAX )) {
Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
"Integer overflow in version %d"
,VERSION_MAX);
s = end - 1;
rev = VERSION_MAX;
vinf = 1;
}
s++;
if
( *s ==
'_'
)
s++;
}
}
else
{
while
(--end >= s) {
int
i;
if
(*end ==
'_'
)
continue
;
i = (*end -
'0'
);
if
( (mult == VERSION_MAX)
|| (i > VERSION_MAX / mult)
|| (i * mult > VERSION_MAX - rev))
{
Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
"Integer overflow in version"
);
end = s - 1;
rev = VERSION_MAX;
vinf = 1;
}
else
rev += i * mult;
if
(mult > VERSION_MAX / 10)
mult = VERSION_MAX;
else
mult *= 10;
}
}
}
av_push(av, newSViv(rev));
if
( vinf ) {
s = last;
break
;
}
else
if
( *pos ==
'.'
) {
pos++;
if
(qv) {
while
(*pos ==
'0'
)
++pos;
}
s = pos;
}
else
if
( *pos ==
'_'
&& isDIGIT(pos[1]) )
s = ++pos;
else
if
( *pos ==
','
&& isDIGIT(pos[1]) )
s = ++pos;
else
if
( isDIGIT(*pos) )
s = pos;
else
{
s = pos;
break
;
}
if
( qv ) {
while
( isDIGIT(*pos) || *pos ==
'_'
)
pos++;
}
else
{
int
digits = 0;
while
( ( isDIGIT(*pos) || *pos ==
'_'
) && digits < 3 ) {
if
( *pos !=
'_'
)
digits++;
pos++;
}
}
}
}
if
( qv ) {
SSize_t len = AvFILLp(av);
len = 2 - len;
while
(len-- > 0)
av_push(av, newSViv(0));
}
if
( vinf ) {
SV * orig = newSVpvn(
"v.Inf"
,
sizeof
(
"v.Inf"
)-1);
(
void
)hv_stores(MUTABLE_HV(hv),
"original"
, orig);
(
void
)hv_stores(MUTABLE_HV(hv),
"vinf"
, newSViv(1));
}
else
if
( s > start ) {
SV * orig = newSVpvn(start,s-start);
if
( qv && saw_decimal == 1 && *start !=
'v'
) {
sv_insert(orig, 0, 0,
"v"
, 1);
}
(
void
)hv_stores(MUTABLE_HV(hv),
"original"
, orig);
}
else
{
(
void
)hv_stores(MUTABLE_HV(hv),
"original"
, newSVpvs(
"0"
));
av_push(av, newSViv(0));
}
(
void
)hv_stores(MUTABLE_HV(hv),
"version"
, newRV_noinc(MUTABLE_SV(av)));
if
( *s ==
'u'
&& strEQ(s+1,
"ndef"
) ) {
s += 5;
}
return
s;
}
SV *
#ifdef VUTIL_REPLACE_CORE
Perl_new_version2(pTHX_ SV *ver)
#else
Perl_new_version(pTHX_ SV *ver)
#endif
{
SV *
const
rv = newSV(0);
PERL_ARGS_ASSERT_NEW_VERSION;
if
( ISA_VERSION_OBJ(ver) )
{
SSize_t key;
AV *
const
av = newAV();
AV *sav;
SV *
const
hv = newSVrv(rv,
"version"
);
(
void
)sv_upgrade(hv, SVt_PVHV);
#ifndef NODEFAULT_SHAREKEYS
HvSHAREKEYS_on(hv);
#endif
if
( SvROK(ver) )
ver = SvRV(ver);
if
( hv_exists(MUTABLE_HV(ver),
"qv"
, 2) )
(
void
)hv_stores(MUTABLE_HV(hv),
"qv"
, newSViv(1));
if
( hv_exists(MUTABLE_HV(ver),
"alpha"
, 5) )
(
void
)hv_stores(MUTABLE_HV(hv),
"alpha"
, newSViv(1));
{
SV ** svp = hv_fetchs(MUTABLE_HV(ver),
"width"
, FALSE);
if
(svp) {
const
I32 width = SvIV(*svp);
(
void
)hv_stores(MUTABLE_HV(hv),
"width"
, newSViv(width));
}
}
{
SV ** svp = hv_fetchs(MUTABLE_HV(ver),
"original"
, FALSE);
if
(svp)
(
void
)hv_stores(MUTABLE_HV(hv),
"original"
, newSVsv(*svp));
}
sav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(ver),
"version"
, FALSE)));
for
( key = 0; key <= av_len(sav); key++ )
{
SV *
const
sv = *av_fetch(sav, key, FALSE);
const
I32 rev = SvIV(sv);
av_push(av, newSViv(rev));
}
(
void
)hv_stores(MUTABLE_HV(hv),
"version"
, newRV_noinc(MUTABLE_SV(av)));
return
rv;
}
#ifdef SvVOK
{
const
MAGIC*
const
mg = SvVSTRING_mg(ver);
if
( mg ) {
const
STRLEN len = mg->mg_len;
const
char
*
const
version = (
const
char
*)mg->mg_ptr;
char
*raw, *under;
static
const
char
underscore[] =
"_"
;
sv_setpvn(rv,version,len);
raw = SvPV_nolen(rv);
under = ninstr(raw, raw+len, underscore, underscore + 1);
if
(under) {
Move(under + 1, under, raw + len - under - 1,
char
);
SvCUR_set(rv, SvCUR(rv) - 1);
*SvEND(rv) =
'\0'
;
}
if
( isDIGIT(*version) )
sv_insert(rv, 0, 0,
"v"
, 1);
}
else
{
#endif
SvSetSV_nosteal(rv, ver);
#ifdef SvVOK
}
}
#endif
sv_2mortal(rv);
return
SvREFCNT_inc_NN(UPG_VERSION(rv, FALSE));
}
#define GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len) \
STMT_START { \
\
\
DISABLE_LC_NUMERIC_CHANGES(); \
\
\
if
(sv) { \
Perl_sv_setpvf(aTHX_ sv,
"%.9"
NVff, SvNVX(ver)); \
len = SvCUR(sv); \
buf = SvPVX(sv); \
} \
else
{ \
len = my_snprintf(tbuf,
sizeof
(tbuf),
"%.9"
NVff, SvNVX(ver)); \
buf = tbuf; \
} \
\
REENABLE_LC_NUMERIC_CHANGES(); \
} STMT_END
SV *
#ifdef VUTIL_REPLACE_CORE
Perl_upg_version2(pTHX_ SV *ver,
bool
qv)
#else
Perl_upg_version(pTHX_ SV *ver,
bool
qv)
#endif
{
const
char
*version, *s;
#ifdef SvVOK
const
MAGIC *mg;
#endif
#if PERL_VERSION_LT(5,19,8) && defined(USE_ITHREADS)
ENTER;
#endif
PERL_ARGS_ASSERT_UPG_VERSION;
if
( (SvUOK(ver) && SvUVX(ver) > VERSION_MAX)
|| (SvIOK(ver) && SvIVX(ver) > VERSION_MAX) )
{
STRLEN len;
char
tbuf[64];
len = my_snprintf(tbuf,
sizeof
(tbuf),
"%d"
, VERSION_MAX);
version = savepvn(tbuf, len);
SAVEFREEPV(version);
Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
"Integer overflow in version %d"
,VERSION_MAX);
}
else
if
( SvUOK(ver) || SvIOK(ver))
#if PERL_VERSION_LT(5,17,2)
VER_IV:
#endif
{
version = savesvpv(ver);
SAVEFREEPV(version);
}
else
if
(SvNOK(ver) && !( SvPOK(ver) && SvCUR(ver) == 3 ) )
#if PERL_VERSION_LT(5,17,2)
VER_NV:
#endif
{
STRLEN len;
char
tbuf[64];
SV *sv = SvNVX(ver) > 10e50 ? newSV(64) : 0;
char
*buf;
#if PERL_VERSION_GE(5,19,0)
if
(SvPOK(ver)) {
goto
VER_PV;
}
#endif
{
#ifdef USE_POSIX_2008_LOCALE
const
locale_t locale_obj_on_entry = uselocale(PL_C_locale_obj);
GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len);
uselocale(locale_obj_on_entry);
#else
char
* radix = NULL;
unsigned
int
radix_len = 0;
GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len);
# ifndef ARABIC_DECIMAL_SEPARATOR_UTF8
radix =
strpbrk
(buf,
".,"
);
if
(LIKELY(radix)) {
radix_len = 1;
}
# else
radix =
strpbrk
(buf,
".,"
ARABIC_DECIMAL_SEPARATOR_UTF8_FIRST_BYTE_s);
if
(LIKELY(radix)) {
if
(LIKELY( (* (U8 *) radix)
!= ARABIC_DECIMAL_SEPARATOR_UTF8_FIRST_BYTE))
{
radix_len = 1;
}
else
{
radix_len = STRLENs(ARABIC_DECIMAL_SEPARATOR_UTF8);
if
( radix + radix_len >= buf + len
|| memNEs(radix + 1,
STRLENs(ARABIC_DECIMAL_SEPARATOR_UTF8_TAIL),
ARABIC_DECIMAL_SEPARATOR_UTF8_TAIL))
{
radix = NULL;
radix_len = 0;
}
}
}
# endif
if
(radix) {
if
(*radix !=
'.'
) {
*radix =
'.'
;
if
(radix_len > 1) {
Move(radix + radix_len,
radix + 1,
len - (radix - buf) - radix_len + 1,
char
);
len -= radix_len - 1;
}
}
if
(radix < buf + len && ! inRANGE(radix[1],
'0'
,
'9'
)) {
radix = NULL;
radix_len = 0;
}
}
if
(! radix) {
# if ! defined(LC_NUMERIC) || ! defined(USE_LOCALE_NUMERIC)
Perl_croak(aTHX_
"panic: Unexpectedly didn't find a dot radix"
" character in '%s'"
, buf);
# else
const
char
* locale_name_on_entry = NULL;
POSIX_SETLOCALE_LOCK;
locale_name_on_entry =
setlocale
(LC_NUMERIC, NULL);
if
( strEQ(locale_name_on_entry,
"C"
)
|| strEQ(locale_name_on_entry,
"C.UTF-8"
)
|| strEQ(locale_name_on_entry,
"POSIX"
))
{
locale_name_on_entry = NULL;
}
else
{
locale_name_on_entry = savepv(locale_name_on_entry);
setlocale
(LC_NUMERIC,
"C"
);
}
GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len);
if
(locale_name_on_entry) {
setlocale
(LC_NUMERIC, locale_name_on_entry);
Safefree(locale_name_on_entry);
}
POSIX_SETLOCALE_UNLOCK;
# endif
}
#endif
}
while
(buf[len-1] ==
'0'
&& len > 0) len--;
if
( buf[len-1] ==
'.'
) len--;
version = savepvn(buf, len);
SAVEFREEPV(version);
SvREFCNT_dec(sv);
}
#ifdef SvVOK
else
if
( (mg = SvVSTRING_mg(ver)) ) {
version = savepvn( (
const
char
*)mg->mg_ptr,mg->mg_len );
SAVEFREEPV(version);
qv = TRUE;
}
#endif
else
if
( SvPOK(ver))
VER_PV:
{
STRLEN len;
version = savepvn(SvPV(ver,len), SvCUR(ver));
SAVEFREEPV(version);
#ifndef SvVOK
if
( len >= 3 && !instr(version,
"."
) && !instr(version,
"_"
)) {
char
*testv = (
char
*)version;
STRLEN tlen = len;
for
(tlen=0; tlen < len; tlen++, testv++) {
if
(testv[0] <
' '
) {
SV *
const
nsv = sv_newmortal();
const
char
*nver;
const
char
*pos;
int
saw_decimal = 0;
sv_setpvf(nsv,
"v%vd"
,ver);
pos = nver = savepv(SvPV_nolen(nsv));
SAVEFREEPV(pos);
pos++;
while
( *pos ==
'.'
|| isDIGIT(*pos) ) {
if
( *pos ==
'.'
)
saw_decimal++ ;
pos++;
}
if
( saw_decimal >= 2 ) {
version = nver;
}
break
;
}
}
}
#endif
}
#if PERL_VERSION_LT(5,17,2)
else
if
(SvIOKp(ver)) {
goto
VER_IV;
}
else
if
(SvNOKp(ver)) {
goto
VER_NV;
}
else
if
(SvPOKp(ver)) {
goto
VER_PV;
}
#endif
else
{
Perl_croak(aTHX_
"Invalid version format (non-numeric data)"
);
}
s = SCAN_VERSION(version, ver, qv);
if
( *s !=
'\0'
)
Perl_ck_warner(aTHX_ packWARN(WARN_MISC),
"Version string '%s' contains invalid data; "
"ignoring: '%s'"
, version, s);
#if PERL_VERSION_LT(5,19,8) && defined(USE_ITHREADS)
LEAVE;
#endif
return
ver;
}
SV *
#ifdef VUTIL_REPLACE_CORE
Perl_vverify2(pTHX_ SV *vs)
#else
Perl_vverify(pTHX_ SV *vs)
#endif
{
SV *sv;
SV **svp;
PERL_ARGS_ASSERT_VVERIFY;
if
( SvROK(vs) )
vs = SvRV(vs);
if
( SvTYPE(vs) == SVt_PVHV
&& (svp = hv_fetchs(MUTABLE_HV(vs),
"version"
, FALSE))
&& (sv = SvRV(*svp))
&& SvTYPE(sv) == SVt_PVAV )
return
vs;
else
return
NULL;
}
SV *
#ifdef VUTIL_REPLACE_CORE
Perl_vnumify2(pTHX_ SV *vs)
#else
Perl_vnumify(pTHX_ SV *vs)
#endif
{
SSize_t i, len;
I32 digit;
bool
alpha = FALSE;
SV *sv;
AV *av;
PERL_ARGS_ASSERT_VNUMIFY;
vs = VVERIFY(vs);
if
( ! vs )
Perl_croak(aTHX_
"Invalid version object"
);
if
( hv_exists(MUTABLE_HV(vs),
"alpha"
, 5 ) )
alpha = TRUE;
if
(alpha) {
Perl_ck_warner(aTHX_ packWARN(WARN_NUMERIC),
"alpha->numify() is lossy"
);
}
if
( !(av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs),
"version"
, FALSE))) ) ) {
return
newSVpvs(
"0"
);
}
len = av_len(av);
if
( len == -1 )
{
return
newSVpvs(
"0"
);
}
{
SV * tsv = *av_fetch(av, 0, 0);
digit = SvIV(tsv);
}
sv = Perl_newSVpvf(aTHX_
"%d."
, (
int
)PERL_ABS(digit));
for
( i = 1 ; i <= len ; i++ )
{
SV * tsv = *av_fetch(av, i, 0);
digit = SvIV(tsv);
Perl_sv_catpvf(aTHX_ sv,
"%03d"
, (
int
)digit);
}
if
( len == 0 ) {
sv_catpvs(sv,
"000"
);
}
return
sv;
}
SV *
#ifdef VUTIL_REPLACE_CORE
Perl_vnormal2(pTHX_ SV *vs)
#else
Perl_vnormal(pTHX_ SV *vs)
#endif
{
I32 i, len, digit;
SV *sv;
AV *av;
PERL_ARGS_ASSERT_VNORMAL;
vs = VVERIFY(vs);
if
( ! vs )
Perl_croak(aTHX_
"Invalid version object"
);
av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs),
"version"
, FALSE)));
len = av_len(av);
if
( len == -1 )
{
return
newSVpvs(
""
);
}
{
SV * tsv = *av_fetch(av, 0, 0);
digit = SvIV(tsv);
}
sv = Perl_newSVpvf(aTHX_
"v%"
IVdf, (IV)digit);
for
( i = 1 ; i <= len ; i++ ) {
SV * tsv = *av_fetch(av, i, 0);
digit = SvIV(tsv);
Perl_sv_catpvf(aTHX_ sv,
".%"
IVdf, (IV)digit);
}
if
( len <= 2 ) {
for
( len = 2 - len; len != 0; len-- )
sv_catpvs(sv,
".0"
);
}
return
sv;
}
SV *
#ifdef VUTIL_REPLACE_CORE
Perl_vstringify2(pTHX_ SV *vs)
#else
Perl_vstringify(pTHX_ SV *vs)
#endif
{
SV ** svp;
PERL_ARGS_ASSERT_VSTRINGIFY;
vs = VVERIFY(vs);
if
( ! vs )
Perl_croak(aTHX_
"Invalid version object"
);
svp = hv_fetchs(MUTABLE_HV(vs),
"original"
, FALSE);
if
(svp) {
SV *pv;
pv = *svp;
if
( SvPOK(pv)
#if PERL_VERSION_LT(5,17,2)
|| SvPOKp(pv)
#endif
)
return
newSVsv(pv);
else
return
&PL_sv_undef;
}
else
{
if
( hv_exists(MUTABLE_HV(vs),
"qv"
, 2) )
return
VNORMAL(vs);
else
return
VNUMIFY(vs);
}
}
int
#ifdef VUTIL_REPLACE_CORE
Perl_vcmp2(pTHX_ SV *lhv, SV *rhv)
#else
Perl_vcmp(pTHX_ SV *lhv, SV *rhv)
#endif
{
SSize_t i,l,m,r;
I32 retval;
I32 left = 0;
I32 right = 0;
AV *lav, *rav;
PERL_ARGS_ASSERT_VCMP;
lhv = VVERIFY(lhv);
rhv = VVERIFY(rhv);
if
( ! ( lhv && rhv ) )
Perl_croak(aTHX_
"Invalid version object"
);
lav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(lhv),
"version"
, FALSE)));
rav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(rhv),
"version"
, FALSE)));
l = av_len(lav);
r = av_len(rav);
m = l < r ? l : r;
retval = 0;
i = 0;
while
( i <= m && retval == 0 )
{
SV *
const
lsv = *av_fetch(lav,i,0);
SV * rsv;
left = SvIV(lsv);
rsv = *av_fetch(rav,i,0);
right = SvIV(rsv);
if
( left < right )
retval = -1;
if
( left > right )
retval = +1;
i++;
}
if
( l != r && retval == 0 )
{
if
( l < r )
{
while
( i <= r && retval == 0 )
{
SV *
const
rsv = *av_fetch(rav,i,0);
if
( SvIV(rsv) != 0 )
retval = -1;
i++;
}
}
else
{
while
( i <= l && retval == 0 )
{
SV *
const
lsv = *av_fetch(lav,i,0);
if
( SvIV(lsv) != 0 )
retval = +1;
i++;
}
}
}
return
retval;
}