16fb12b70Safresh1 /* This file is part of the "version" CPAN distribution. Please avoid 26fb12b70Safresh1 editing it in the perl core. */ 36fb12b70Safresh1 4b8851fccSafresh1 #ifdef PERL_CORE 56fb12b70Safresh1 # include "vutil.h" 6b8851fccSafresh1 #endif 76fb12b70Safresh1 86fb12b70Safresh1 #define VERSION_MAX 0x7FFFFFFF 96fb12b70Safresh1 103d61058aSafresh1 #ifndef STRLENs 113d61058aSafresh1 # define STRLENs(s) (sizeof("" s "") - 1) 123d61058aSafresh1 #endif 133d61058aSafresh1 #ifndef POSIX_SETLOCALE_LOCK 143d61058aSafresh1 # ifdef gwLOCALE_LOCK 153d61058aSafresh1 # define POSIX_SETLOCALE_LOCK gwLOCALE_LOCK 163d61058aSafresh1 # define POSIX_SETLOCALE_UNLOCK gwLOCALE_UNLOCK 173d61058aSafresh1 # else 183d61058aSafresh1 # define POSIX_SETLOCALE_LOCK NOOP 193d61058aSafresh1 # define POSIX_SETLOCALE_UNLOCK NOOP 203d61058aSafresh1 # endif 213d61058aSafresh1 #endif 223d61058aSafresh1 #ifndef DISABLE_LC_NUMERIC_CHANGES 233d61058aSafresh1 # ifdef LOCK_LC_NUMERIC_STANDARD 243d61058aSafresh1 # define DISABLE_LC_NUMERIC_CHANGES() LOCK_LC_NUMERIC_STANDARD() 253d61058aSafresh1 # define REENABLE_LC_NUMERIC_CHANGES() UNLOCK_LC_NUMERIC_STANDARD() 263d61058aSafresh1 # else 273d61058aSafresh1 # define DISABLE_LC_NUMERIC_CHANGES() NOOP 283d61058aSafresh1 # define REENABLE_LC_NUMERIC_CHANGES() NOOP 293d61058aSafresh1 # endif 303d61058aSafresh1 #endif 313d61058aSafresh1 326fb12b70Safresh1 /* 336fb12b70Safresh1 =for apidoc prescan_version 346fb12b70Safresh1 356fb12b70Safresh1 Validate that a given string can be parsed as a version object, but doesn't 366fb12b70Safresh1 actually perform the parsing. Can use either strict or lax validation rules. 376fb12b70Safresh1 Can optionally set a number of hint variables to save the parsing code 386fb12b70Safresh1 some time when tokenizing. 396fb12b70Safresh1 406fb12b70Safresh1 =cut 416fb12b70Safresh1 */ 426fb12b70Safresh1 const char * 436fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 446fb12b70Safresh1 Perl_prescan_version2(pTHX_ const char *s, bool strict, 456fb12b70Safresh1 #else 466fb12b70Safresh1 Perl_prescan_version(pTHX_ const char *s, bool strict, 476fb12b70Safresh1 #endif 486fb12b70Safresh1 const char **errstr, 496fb12b70Safresh1 bool *sqv, int *ssaw_decimal, int *swidth, bool *salpha) { 506fb12b70Safresh1 bool qv = (sqv ? *sqv : FALSE); 516fb12b70Safresh1 int width = 3; 526fb12b70Safresh1 int saw_decimal = 0; 536fb12b70Safresh1 bool alpha = FALSE; 546fb12b70Safresh1 const char *d = s; 556fb12b70Safresh1 566fb12b70Safresh1 PERL_ARGS_ASSERT_PRESCAN_VERSION; 57b8851fccSafresh1 PERL_UNUSED_CONTEXT; 586fb12b70Safresh1 596fb12b70Safresh1 if (qv && isDIGIT(*d)) 606fb12b70Safresh1 goto dotted_decimal_version; 616fb12b70Safresh1 626fb12b70Safresh1 if (*d == 'v') { /* explicit v-string */ 636fb12b70Safresh1 d++; 646fb12b70Safresh1 if (isDIGIT(*d)) { 656fb12b70Safresh1 qv = TRUE; 666fb12b70Safresh1 } 676fb12b70Safresh1 else { /* degenerate v-string */ 686fb12b70Safresh1 /* requires v1.2.3 */ 696fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)"); 706fb12b70Safresh1 } 716fb12b70Safresh1 726fb12b70Safresh1 dotted_decimal_version: 736fb12b70Safresh1 if (strict && d[0] == '0' && isDIGIT(d[1])) { 746fb12b70Safresh1 /* no leading zeros allowed */ 756fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (no leading zeros)"); 766fb12b70Safresh1 } 776fb12b70Safresh1 786fb12b70Safresh1 while (isDIGIT(*d)) /* integer part */ 796fb12b70Safresh1 d++; 806fb12b70Safresh1 816fb12b70Safresh1 if (*d == '.') 826fb12b70Safresh1 { 836fb12b70Safresh1 saw_decimal++; 846fb12b70Safresh1 d++; /* decimal point */ 856fb12b70Safresh1 } 866fb12b70Safresh1 else 876fb12b70Safresh1 { 886fb12b70Safresh1 if (strict) { 896fb12b70Safresh1 /* require v1.2.3 */ 906fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)"); 916fb12b70Safresh1 } 926fb12b70Safresh1 else { 936fb12b70Safresh1 goto version_prescan_finish; 946fb12b70Safresh1 } 956fb12b70Safresh1 } 966fb12b70Safresh1 976fb12b70Safresh1 { 986fb12b70Safresh1 int i = 0; 996fb12b70Safresh1 int j = 0; 1006fb12b70Safresh1 while (isDIGIT(*d)) { /* just keep reading */ 1016fb12b70Safresh1 i++; 1026fb12b70Safresh1 while (isDIGIT(*d)) { 1036fb12b70Safresh1 d++; j++; 1046fb12b70Safresh1 /* maximum 3 digits between decimal */ 1056fb12b70Safresh1 if (strict && j > 3) { 1066fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (maximum 3 digits between decimals)"); 1076fb12b70Safresh1 } 1086fb12b70Safresh1 } 1096fb12b70Safresh1 if (*d == '_') { 1106fb12b70Safresh1 if (strict) { 1116fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (no underscores)"); 1126fb12b70Safresh1 } 1136fb12b70Safresh1 if ( alpha ) { 1146fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (multiple underscores)"); 1156fb12b70Safresh1 } 1166fb12b70Safresh1 d++; 1176fb12b70Safresh1 alpha = TRUE; 1186fb12b70Safresh1 } 1196fb12b70Safresh1 else if (*d == '.') { 1206fb12b70Safresh1 if (alpha) { 1216fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (underscores before decimal)"); 1226fb12b70Safresh1 } 1236fb12b70Safresh1 saw_decimal++; 1246fb12b70Safresh1 d++; 1256fb12b70Safresh1 } 1266fb12b70Safresh1 else if (!isDIGIT(*d)) { 1276fb12b70Safresh1 break; 1286fb12b70Safresh1 } 1296fb12b70Safresh1 j = 0; 1306fb12b70Safresh1 } 1316fb12b70Safresh1 1326fb12b70Safresh1 if (strict && i < 2) { 1336fb12b70Safresh1 /* requires v1.2.3 */ 1346fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)"); 1356fb12b70Safresh1 } 1366fb12b70Safresh1 } 1376fb12b70Safresh1 } /* end if dotted-decimal */ 1386fb12b70Safresh1 else 1396fb12b70Safresh1 { /* decimal versions */ 1406fb12b70Safresh1 int j = 0; /* may need this later */ 1416fb12b70Safresh1 /* special strict case for leading '.' or '0' */ 1426fb12b70Safresh1 if (strict) { 1436fb12b70Safresh1 if (*d == '.') { 1446fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (0 before decimal required)"); 1456fb12b70Safresh1 } 1466fb12b70Safresh1 if (*d == '0' && isDIGIT(d[1])) { 1476fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (no leading zeros)"); 1486fb12b70Safresh1 } 1496fb12b70Safresh1 } 1506fb12b70Safresh1 1516fb12b70Safresh1 /* and we never support negative versions */ 1526fb12b70Safresh1 if ( *d == '-') { 1536fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (negative version number)"); 1546fb12b70Safresh1 } 1556fb12b70Safresh1 1566fb12b70Safresh1 /* consume all of the integer part */ 1576fb12b70Safresh1 while (isDIGIT(*d)) 1586fb12b70Safresh1 d++; 1596fb12b70Safresh1 1606fb12b70Safresh1 /* look for a fractional part */ 1616fb12b70Safresh1 if (*d == '.') { 1626fb12b70Safresh1 /* we found it, so consume it */ 1636fb12b70Safresh1 saw_decimal++; 1646fb12b70Safresh1 d++; 1656fb12b70Safresh1 } 1666fb12b70Safresh1 else if (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') { 1676fb12b70Safresh1 if ( d == s ) { 1686fb12b70Safresh1 /* found nothing */ 1696fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (version required)"); 1706fb12b70Safresh1 } 1716fb12b70Safresh1 /* found just an integer */ 1726fb12b70Safresh1 goto version_prescan_finish; 1736fb12b70Safresh1 } 1746fb12b70Safresh1 else if ( d == s ) { 1756fb12b70Safresh1 /* didn't find either integer or period */ 1766fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (non-numeric data)"); 1776fb12b70Safresh1 } 1786fb12b70Safresh1 else if (*d == '_') { 1796fb12b70Safresh1 /* underscore can't come after integer part */ 1806fb12b70Safresh1 if (strict) { 1816fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (no underscores)"); 1826fb12b70Safresh1 } 1836fb12b70Safresh1 else if (isDIGIT(d[1])) { 1846fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (alpha without decimal)"); 1856fb12b70Safresh1 } 1866fb12b70Safresh1 else { 1876fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (misplaced underscore)"); 1886fb12b70Safresh1 } 1896fb12b70Safresh1 } 1906fb12b70Safresh1 else { 1916fb12b70Safresh1 /* anything else after integer part is just invalid data */ 1926fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (non-numeric data)"); 1936fb12b70Safresh1 } 1946fb12b70Safresh1 1956fb12b70Safresh1 /* scan the fractional part after the decimal point*/ 1966fb12b70Safresh1 1976fb12b70Safresh1 if (!isDIGIT(*d) && (strict || ! (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') )) { 1986fb12b70Safresh1 /* strict or lax-but-not-the-end */ 1996fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (fractional part required)"); 2006fb12b70Safresh1 } 2016fb12b70Safresh1 2026fb12b70Safresh1 while (isDIGIT(*d)) { 2036fb12b70Safresh1 d++; j++; 2046fb12b70Safresh1 if (*d == '.' && isDIGIT(d[-1])) { 2056fb12b70Safresh1 if (alpha) { 2066fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (underscores before decimal)"); 2076fb12b70Safresh1 } 2086fb12b70Safresh1 if (strict) { 2096fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions must begin with 'v')"); 2106fb12b70Safresh1 } 2116fb12b70Safresh1 d = (char *)s; /* start all over again */ 2126fb12b70Safresh1 qv = TRUE; 2136fb12b70Safresh1 goto dotted_decimal_version; 2146fb12b70Safresh1 } 2156fb12b70Safresh1 if (*d == '_') { 2166fb12b70Safresh1 if (strict) { 2176fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (no underscores)"); 2186fb12b70Safresh1 } 2196fb12b70Safresh1 if ( alpha ) { 2206fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (multiple underscores)"); 2216fb12b70Safresh1 } 2226fb12b70Safresh1 if ( ! isDIGIT(d[1]) ) { 2236fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (misplaced underscore)"); 2246fb12b70Safresh1 } 2256fb12b70Safresh1 width = j; 2266fb12b70Safresh1 d++; 2276fb12b70Safresh1 alpha = TRUE; 2286fb12b70Safresh1 } 2296fb12b70Safresh1 } 2306fb12b70Safresh1 } 2316fb12b70Safresh1 2326fb12b70Safresh1 version_prescan_finish: 2336fb12b70Safresh1 while (isSPACE(*d)) 2346fb12b70Safresh1 d++; 2356fb12b70Safresh1 2363d61058aSafresh1 if (!isDIGIT(*d) && (! (!*d || *d == ';' || *d == ':' || *d == '{' || *d == '}') )) { 2376fb12b70Safresh1 /* trailing non-numeric data */ 2386fb12b70Safresh1 BADVERSION(s,errstr,"Invalid version format (non-numeric data)"); 2396fb12b70Safresh1 } 240b8851fccSafresh1 if (saw_decimal > 1 && d[-1] == '.') { 241b8851fccSafresh1 /* no trailing period allowed */ 242b8851fccSafresh1 BADVERSION(s,errstr,"Invalid version format (trailing decimal)"); 243b8851fccSafresh1 } 244b8851fccSafresh1 2456fb12b70Safresh1 2466fb12b70Safresh1 if (sqv) 2476fb12b70Safresh1 *sqv = qv; 2486fb12b70Safresh1 if (swidth) 2496fb12b70Safresh1 *swidth = width; 2506fb12b70Safresh1 if (ssaw_decimal) 2516fb12b70Safresh1 *ssaw_decimal = saw_decimal; 2526fb12b70Safresh1 if (salpha) 2536fb12b70Safresh1 *salpha = alpha; 2546fb12b70Safresh1 return d; 2556fb12b70Safresh1 } 2566fb12b70Safresh1 2576fb12b70Safresh1 /* 2586fb12b70Safresh1 =for apidoc scan_version 2596fb12b70Safresh1 2606fb12b70Safresh1 Returns a pointer to the next character after the parsed 2616fb12b70Safresh1 version string, as well as upgrading the passed in SV to 2626fb12b70Safresh1 an RV. 2636fb12b70Safresh1 2646fb12b70Safresh1 Function must be called with an already existing SV like 2656fb12b70Safresh1 2666fb12b70Safresh1 sv = newSV(0); 2676fb12b70Safresh1 s = scan_version(s, SV *sv, bool qv); 2686fb12b70Safresh1 2696fb12b70Safresh1 Performs some preprocessing to the string to ensure that 2706fb12b70Safresh1 it has the correct characteristics of a version. Flags the 2716fb12b70Safresh1 object if it contains an underscore (which denotes this 2726fb12b70Safresh1 is an alpha version). The boolean qv denotes that the version 2736fb12b70Safresh1 should be interpreted as if it had multiple decimals, even if 2746fb12b70Safresh1 it doesn't. 2756fb12b70Safresh1 2766fb12b70Safresh1 =cut 2776fb12b70Safresh1 */ 2786fb12b70Safresh1 2796fb12b70Safresh1 const char * 2806fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 2816fb12b70Safresh1 Perl_scan_version2(pTHX_ const char *s, SV *rv, bool qv) 2826fb12b70Safresh1 #else 2836fb12b70Safresh1 Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv) 2846fb12b70Safresh1 #endif 2856fb12b70Safresh1 { 2866fb12b70Safresh1 const char *start = s; 2876fb12b70Safresh1 const char *pos; 2886fb12b70Safresh1 const char *last; 2896fb12b70Safresh1 const char *errstr = NULL; 2906fb12b70Safresh1 int saw_decimal = 0; 2916fb12b70Safresh1 int width = 3; 2926fb12b70Safresh1 bool alpha = FALSE; 2936fb12b70Safresh1 bool vinf = FALSE; 2946fb12b70Safresh1 AV * av; 2956fb12b70Safresh1 SV * hv; 2966fb12b70Safresh1 2976fb12b70Safresh1 PERL_ARGS_ASSERT_SCAN_VERSION; 2986fb12b70Safresh1 2996fb12b70Safresh1 while (isSPACE(*s)) /* leading whitespace is OK */ 3006fb12b70Safresh1 s++; 3016fb12b70Safresh1 3026fb12b70Safresh1 last = PRESCAN_VERSION(s, FALSE, &errstr, &qv, &saw_decimal, &width, &alpha); 3036fb12b70Safresh1 if (errstr) { 3046fb12b70Safresh1 /* "undef" is a special case and not an error */ 3056fb12b70Safresh1 if ( ! ( *s == 'u' && strEQ(s+1,"ndef")) ) { 3066fb12b70Safresh1 Perl_croak(aTHX_ "%s", errstr); 3076fb12b70Safresh1 } 3086fb12b70Safresh1 } 3096fb12b70Safresh1 3106fb12b70Safresh1 start = s; 3116fb12b70Safresh1 if (*s == 'v') 3126fb12b70Safresh1 s++; 3136fb12b70Safresh1 pos = s; 3146fb12b70Safresh1 3156fb12b70Safresh1 /* Now that we are through the prescan, start creating the object */ 3166fb12b70Safresh1 av = newAV(); 3176fb12b70Safresh1 hv = newSVrv(rv, "version"); /* create an SV and upgrade the RV */ 3186fb12b70Safresh1 (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */ 3196fb12b70Safresh1 3206fb12b70Safresh1 #ifndef NODEFAULT_SHAREKEYS 3216fb12b70Safresh1 HvSHAREKEYS_on(hv); /* key-sharing on by default */ 3226fb12b70Safresh1 #endif 3236fb12b70Safresh1 3246fb12b70Safresh1 if ( qv ) 3256fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "qv", newSViv(qv)); 3266fb12b70Safresh1 if ( alpha ) 3276fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "alpha", newSViv(alpha)); 3286fb12b70Safresh1 if ( !qv && width < 3 ) 3296fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "width", newSViv(width)); 3306fb12b70Safresh1 331b8851fccSafresh1 while (isDIGIT(*pos) || *pos == '_') 3326fb12b70Safresh1 pos++; 3336fb12b70Safresh1 if (!isALPHA(*pos)) { 3346fb12b70Safresh1 I32 rev; 3356fb12b70Safresh1 3366fb12b70Safresh1 for (;;) { 3376fb12b70Safresh1 rev = 0; 3386fb12b70Safresh1 { 3396fb12b70Safresh1 /* this is atoi() that delimits on underscores */ 3406fb12b70Safresh1 const char *end = pos; 3416fb12b70Safresh1 I32 mult = 1; 3426fb12b70Safresh1 I32 orev; 3436fb12b70Safresh1 3446fb12b70Safresh1 /* the following if() will only be true after the decimal 3456fb12b70Safresh1 * point of a version originally created with a bare 3466fb12b70Safresh1 * floating point number, i.e. not quoted in any way 3476fb12b70Safresh1 */ 3486fb12b70Safresh1 if ( !qv && s > start && saw_decimal == 1 ) { 3496fb12b70Safresh1 mult *= 100; 3506fb12b70Safresh1 while ( s < end ) { 351b8851fccSafresh1 if (*s == '_') 352b8851fccSafresh1 continue; 3536fb12b70Safresh1 orev = rev; 3546fb12b70Safresh1 rev += (*s - '0') * mult; 3556fb12b70Safresh1 mult /= 10; 3566fb12b70Safresh1 if ( (PERL_ABS(orev) > PERL_ABS(rev)) 3576fb12b70Safresh1 || (PERL_ABS(rev) > VERSION_MAX )) { 3586fb12b70Safresh1 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW), 3596fb12b70Safresh1 "Integer overflow in version %d",VERSION_MAX); 3606fb12b70Safresh1 s = end - 1; 3616fb12b70Safresh1 rev = VERSION_MAX; 3626fb12b70Safresh1 vinf = 1; 3636fb12b70Safresh1 } 3646fb12b70Safresh1 s++; 3656fb12b70Safresh1 if ( *s == '_' ) 3666fb12b70Safresh1 s++; 3676fb12b70Safresh1 } 3686fb12b70Safresh1 } 3696fb12b70Safresh1 else { 3706fb12b70Safresh1 while (--end >= s) { 371b8851fccSafresh1 int i; 372b8851fccSafresh1 if (*end == '_') 373b8851fccSafresh1 continue; 374b8851fccSafresh1 i = (*end - '0'); 375b8851fccSafresh1 if ( (mult == VERSION_MAX) 376b8851fccSafresh1 || (i > VERSION_MAX / mult) 377b8851fccSafresh1 || (i * mult > VERSION_MAX - rev)) 378b8851fccSafresh1 { 3796fb12b70Safresh1 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW), 3806fb12b70Safresh1 "Integer overflow in version"); 3816fb12b70Safresh1 end = s - 1; 3826fb12b70Safresh1 rev = VERSION_MAX; 3836fb12b70Safresh1 vinf = 1; 3846fb12b70Safresh1 } 385b8851fccSafresh1 else 386b8851fccSafresh1 rev += i * mult; 387b8851fccSafresh1 388b8851fccSafresh1 if (mult > VERSION_MAX / 10) 389b8851fccSafresh1 mult = VERSION_MAX; 390b8851fccSafresh1 else 391b8851fccSafresh1 mult *= 10; 3926fb12b70Safresh1 } 3936fb12b70Safresh1 } 3946fb12b70Safresh1 } 3956fb12b70Safresh1 3966fb12b70Safresh1 /* Append revision */ 3976fb12b70Safresh1 av_push(av, newSViv(rev)); 3986fb12b70Safresh1 if ( vinf ) { 3996fb12b70Safresh1 s = last; 4006fb12b70Safresh1 break; 4016fb12b70Safresh1 } 402b8851fccSafresh1 else if ( *pos == '.' ) { 403b8851fccSafresh1 pos++; 404b8851fccSafresh1 if (qv) { 405b8851fccSafresh1 while (*pos == '0') 406b8851fccSafresh1 ++pos; 407b8851fccSafresh1 } 408b8851fccSafresh1 s = pos; 409b8851fccSafresh1 } 4106fb12b70Safresh1 else if ( *pos == '_' && isDIGIT(pos[1]) ) 4116fb12b70Safresh1 s = ++pos; 4126fb12b70Safresh1 else if ( *pos == ',' && isDIGIT(pos[1]) ) 4136fb12b70Safresh1 s = ++pos; 4146fb12b70Safresh1 else if ( isDIGIT(*pos) ) 4156fb12b70Safresh1 s = pos; 4166fb12b70Safresh1 else { 4176fb12b70Safresh1 s = pos; 4186fb12b70Safresh1 break; 4196fb12b70Safresh1 } 4206fb12b70Safresh1 if ( qv ) { 421b8851fccSafresh1 while ( isDIGIT(*pos) || *pos == '_') 4226fb12b70Safresh1 pos++; 4236fb12b70Safresh1 } 4246fb12b70Safresh1 else { 4256fb12b70Safresh1 int digits = 0; 4266fb12b70Safresh1 while ( ( isDIGIT(*pos) || *pos == '_' ) && digits < 3 ) { 4276fb12b70Safresh1 if ( *pos != '_' ) 4286fb12b70Safresh1 digits++; 4296fb12b70Safresh1 pos++; 4306fb12b70Safresh1 } 4316fb12b70Safresh1 } 4326fb12b70Safresh1 } 4336fb12b70Safresh1 } 4346fb12b70Safresh1 if ( qv ) { /* quoted versions always get at least three terms*/ 4356fb12b70Safresh1 SSize_t len = AvFILLp(av); 4366fb12b70Safresh1 /* This for loop appears to trigger a compiler bug on OS X, as it 4376fb12b70Safresh1 loops infinitely. Yes, len is negative. No, it makes no sense. 4386fb12b70Safresh1 Compiler in question is: 4396fb12b70Safresh1 gcc version 3.3 20030304 (Apple Computer, Inc. build 1640) 4406fb12b70Safresh1 for ( len = 2 - len; len > 0; len-- ) 4416fb12b70Safresh1 av_push(MUTABLE_AV(sv), newSViv(0)); 4426fb12b70Safresh1 */ 4436fb12b70Safresh1 len = 2 - len; 4446fb12b70Safresh1 while (len-- > 0) 4456fb12b70Safresh1 av_push(av, newSViv(0)); 4466fb12b70Safresh1 } 4476fb12b70Safresh1 4486fb12b70Safresh1 /* need to save off the current version string for later */ 4496fb12b70Safresh1 if ( vinf ) { 4506fb12b70Safresh1 SV * orig = newSVpvn("v.Inf", sizeof("v.Inf")-1); 4516fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "original", orig); 4526fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "vinf", newSViv(1)); 4536fb12b70Safresh1 } 4546fb12b70Safresh1 else if ( s > start ) { 4556fb12b70Safresh1 SV * orig = newSVpvn(start,s-start); 4566fb12b70Safresh1 if ( qv && saw_decimal == 1 && *start != 'v' ) { 4576fb12b70Safresh1 /* need to insert a v to be consistent */ 4586fb12b70Safresh1 sv_insert(orig, 0, 0, "v", 1); 4596fb12b70Safresh1 } 4606fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "original", orig); 4616fb12b70Safresh1 } 4626fb12b70Safresh1 else { 4636fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "original", newSVpvs("0")); 4646fb12b70Safresh1 av_push(av, newSViv(0)); 4656fb12b70Safresh1 } 4666fb12b70Safresh1 4676fb12b70Safresh1 /* And finally, store the AV in the hash */ 4686fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "version", newRV_noinc(MUTABLE_SV(av))); 4696fb12b70Safresh1 4706fb12b70Safresh1 /* fix RT#19517 - special case 'undef' as string */ 4716fb12b70Safresh1 if ( *s == 'u' && strEQ(s+1,"ndef") ) { 4726fb12b70Safresh1 s += 5; 4736fb12b70Safresh1 } 4746fb12b70Safresh1 4756fb12b70Safresh1 return s; 4766fb12b70Safresh1 } 4776fb12b70Safresh1 4786fb12b70Safresh1 /* 4796fb12b70Safresh1 =for apidoc new_version 4806fb12b70Safresh1 4816fb12b70Safresh1 Returns a new version object based on the passed in SV: 4826fb12b70Safresh1 4836fb12b70Safresh1 SV *sv = new_version(SV *ver); 4846fb12b70Safresh1 4856fb12b70Safresh1 Does not alter the passed in ver SV. See "upg_version" if you 4866fb12b70Safresh1 want to upgrade the SV. 4876fb12b70Safresh1 4886fb12b70Safresh1 =cut 4896fb12b70Safresh1 */ 4906fb12b70Safresh1 4916fb12b70Safresh1 SV * 4926fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 4936fb12b70Safresh1 Perl_new_version2(pTHX_ SV *ver) 4946fb12b70Safresh1 #else 4956fb12b70Safresh1 Perl_new_version(pTHX_ SV *ver) 4966fb12b70Safresh1 #endif 4976fb12b70Safresh1 { 4986fb12b70Safresh1 SV * const rv = newSV(0); 4996fb12b70Safresh1 PERL_ARGS_ASSERT_NEW_VERSION; 5006fb12b70Safresh1 if ( ISA_VERSION_OBJ(ver) ) /* can just copy directly */ 5016fb12b70Safresh1 { 5026fb12b70Safresh1 SSize_t key; 5036fb12b70Safresh1 AV * const av = newAV(); 5046fb12b70Safresh1 AV *sav; 5056fb12b70Safresh1 /* This will get reblessed later if a derived class*/ 5066fb12b70Safresh1 SV * const hv = newSVrv(rv, "version"); 5076fb12b70Safresh1 (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */ 5086fb12b70Safresh1 #ifndef NODEFAULT_SHAREKEYS 5096fb12b70Safresh1 HvSHAREKEYS_on(hv); /* key-sharing on by default */ 5106fb12b70Safresh1 #endif 5116fb12b70Safresh1 5126fb12b70Safresh1 if ( SvROK(ver) ) 5136fb12b70Safresh1 ver = SvRV(ver); 5146fb12b70Safresh1 5156fb12b70Safresh1 /* Begin copying all of the elements */ 5166fb12b70Safresh1 if ( hv_exists(MUTABLE_HV(ver), "qv", 2) ) 5176fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "qv", newSViv(1)); 5186fb12b70Safresh1 5196fb12b70Safresh1 if ( hv_exists(MUTABLE_HV(ver), "alpha", 5) ) 5206fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "alpha", newSViv(1)); 5216fb12b70Safresh1 { 5226fb12b70Safresh1 SV ** svp = hv_fetchs(MUTABLE_HV(ver), "width", FALSE); 5236fb12b70Safresh1 if(svp) { 5246fb12b70Safresh1 const I32 width = SvIV(*svp); 5256fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "width", newSViv(width)); 5266fb12b70Safresh1 } 5276fb12b70Safresh1 } 5286fb12b70Safresh1 { 5296fb12b70Safresh1 SV ** svp = hv_fetchs(MUTABLE_HV(ver), "original", FALSE); 5306fb12b70Safresh1 if(svp) 5316fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "original", newSVsv(*svp)); 5326fb12b70Safresh1 } 5336fb12b70Safresh1 sav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(ver), "version", FALSE))); 5346fb12b70Safresh1 /* This will get reblessed later if a derived class*/ 5356fb12b70Safresh1 for ( key = 0; key <= av_len(sav); key++ ) 5366fb12b70Safresh1 { 5376fb12b70Safresh1 SV * const sv = *av_fetch(sav, key, FALSE); 5386fb12b70Safresh1 const I32 rev = SvIV(sv); 5396fb12b70Safresh1 av_push(av, newSViv(rev)); 5406fb12b70Safresh1 } 5416fb12b70Safresh1 5426fb12b70Safresh1 (void)hv_stores(MUTABLE_HV(hv), "version", newRV_noinc(MUTABLE_SV(av))); 5436fb12b70Safresh1 return rv; 5446fb12b70Safresh1 } 5456fb12b70Safresh1 #ifdef SvVOK 5466fb12b70Safresh1 { 5476fb12b70Safresh1 const MAGIC* const mg = SvVSTRING_mg(ver); 5486fb12b70Safresh1 if ( mg ) { /* already a v-string */ 5496fb12b70Safresh1 const STRLEN len = mg->mg_len; 5506fb12b70Safresh1 const char * const version = (const char*)mg->mg_ptr; 551b8851fccSafresh1 char *raw, *under; 552b8851fccSafresh1 static const char underscore[] = "_"; 5536fb12b70Safresh1 sv_setpvn(rv,version,len); 554b8851fccSafresh1 raw = SvPV_nolen(rv); 555b8851fccSafresh1 under = ninstr(raw, raw+len, underscore, underscore + 1); 556b8851fccSafresh1 if (under) { 557b8851fccSafresh1 Move(under + 1, under, raw + len - under - 1, char); 55856d68f1eSafresh1 SvCUR_set(rv, SvCUR(rv) - 1); 559b8851fccSafresh1 *SvEND(rv) = '\0'; 560b8851fccSafresh1 } 5616fb12b70Safresh1 /* this is for consistency with the pure Perl class */ 5626fb12b70Safresh1 if ( isDIGIT(*version) ) 5636fb12b70Safresh1 sv_insert(rv, 0, 0, "v", 1); 5646fb12b70Safresh1 } 5656fb12b70Safresh1 else { 5666fb12b70Safresh1 #endif 5676fb12b70Safresh1 SvSetSV_nosteal(rv, ver); /* make a duplicate */ 5686fb12b70Safresh1 #ifdef SvVOK 5696fb12b70Safresh1 } 5706fb12b70Safresh1 } 5716fb12b70Safresh1 #endif 5726fb12b70Safresh1 sv_2mortal(rv); /* in case upg_version croaks before it returns */ 5736fb12b70Safresh1 return SvREFCNT_inc_NN(UPG_VERSION(rv, FALSE)); 5746fb12b70Safresh1 } 5756fb12b70Safresh1 5766fb12b70Safresh1 /* 5776fb12b70Safresh1 =for apidoc upg_version 5786fb12b70Safresh1 5796fb12b70Safresh1 In-place upgrade of the supplied SV to a version object. 5806fb12b70Safresh1 5816fb12b70Safresh1 SV *sv = upg_version(SV *sv, bool qv); 5826fb12b70Safresh1 5836fb12b70Safresh1 Returns a pointer to the upgraded SV. Set the boolean qv if you want 5846fb12b70Safresh1 to force this SV to be interpreted as an "extended" version. 5856fb12b70Safresh1 5866fb12b70Safresh1 =cut 5876fb12b70Safresh1 */ 5886fb12b70Safresh1 5893d61058aSafresh1 /* Macro to do the meat of getting the PV of an NV version number. This is 5903d61058aSafresh1 * macroized because can be called from several places */ 5913d61058aSafresh1 #define GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len) \ 5923d61058aSafresh1 STMT_START { \ 5933d61058aSafresh1 \ 5943d61058aSafresh1 /* Prevent callees from trying to change the locale */ \ 5953d61058aSafresh1 DISABLE_LC_NUMERIC_CHANGES(); \ 5963d61058aSafresh1 \ 5973d61058aSafresh1 /* We earlier created 'sv' for very large version numbers, to rely \ 5983d61058aSafresh1 * on the specialized algorithms SV code has built-in for such \ 5993d61058aSafresh1 * values */ \ 6003d61058aSafresh1 if (sv) { \ 6013d61058aSafresh1 Perl_sv_setpvf(aTHX_ sv, "%.9" NVff, SvNVX(ver)); \ 6023d61058aSafresh1 len = SvCUR(sv); \ 6033d61058aSafresh1 buf = SvPVX(sv); \ 6043d61058aSafresh1 } \ 6053d61058aSafresh1 else { \ 6063d61058aSafresh1 len = my_snprintf(tbuf, sizeof(tbuf), "%.9" NVff, SvNVX(ver)); \ 6073d61058aSafresh1 buf = tbuf; \ 6083d61058aSafresh1 } \ 6093d61058aSafresh1 \ 6103d61058aSafresh1 REENABLE_LC_NUMERIC_CHANGES(); \ 6113d61058aSafresh1 } STMT_END 6123d61058aSafresh1 6136fb12b70Safresh1 SV * 6146fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 6156fb12b70Safresh1 Perl_upg_version2(pTHX_ SV *ver, bool qv) 6166fb12b70Safresh1 #else 6176fb12b70Safresh1 Perl_upg_version(pTHX_ SV *ver, bool qv) 6186fb12b70Safresh1 #endif 6196fb12b70Safresh1 { 6206fb12b70Safresh1 const char *version, *s; 6216fb12b70Safresh1 #ifdef SvVOK 6226fb12b70Safresh1 const MAGIC *mg; 6236fb12b70Safresh1 #endif 6246fb12b70Safresh1 6256fb12b70Safresh1 #if PERL_VERSION_LT(5,19,8) && defined(USE_ITHREADS) 6266fb12b70Safresh1 ENTER; 6276fb12b70Safresh1 #endif 6286fb12b70Safresh1 PERL_ARGS_ASSERT_UPG_VERSION; 6296fb12b70Safresh1 6306fb12b70Safresh1 if ( (SvUOK(ver) && SvUVX(ver) > VERSION_MAX) 6313d61058aSafresh1 || (SvIOK(ver) && SvIVX(ver) > VERSION_MAX) ) 6323d61058aSafresh1 { 6336fb12b70Safresh1 /* out of bounds [unsigned] integer */ 6346fb12b70Safresh1 STRLEN len; 6356fb12b70Safresh1 char tbuf[64]; 6366fb12b70Safresh1 len = my_snprintf(tbuf, sizeof(tbuf), "%d", VERSION_MAX); 6376fb12b70Safresh1 version = savepvn(tbuf, len); 6386fb12b70Safresh1 SAVEFREEPV(version); 6396fb12b70Safresh1 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW), 6406fb12b70Safresh1 "Integer overflow in version %d",VERSION_MAX); 6416fb12b70Safresh1 } 6426fb12b70Safresh1 else if ( SvUOK(ver) || SvIOK(ver)) 6436fb12b70Safresh1 #if PERL_VERSION_LT(5,17,2) 6446fb12b70Safresh1 VER_IV: 6456fb12b70Safresh1 #endif 6466fb12b70Safresh1 { 6476fb12b70Safresh1 version = savesvpv(ver); 6486fb12b70Safresh1 SAVEFREEPV(version); 6496fb12b70Safresh1 } 6506fb12b70Safresh1 else if (SvNOK(ver) && !( SvPOK(ver) && SvCUR(ver) == 3 ) ) 6516fb12b70Safresh1 #if PERL_VERSION_LT(5,17,2) 6526fb12b70Safresh1 VER_NV: 6536fb12b70Safresh1 #endif 6546fb12b70Safresh1 { 6556fb12b70Safresh1 STRLEN len; 6566fb12b70Safresh1 6576fb12b70Safresh1 /* may get too much accuracy */ 6586fb12b70Safresh1 char tbuf[64]; 659*e0a54000Safresh1 #ifdef __vax__ 660*e0a54000Safresh1 SV *sv = SvNVX(ver) > 10e37 ? newSV(64) : 0; 661*e0a54000Safresh1 #else 6626fb12b70Safresh1 SV *sv = SvNVX(ver) > 10e50 ? newSV(64) : 0; 663*e0a54000Safresh1 #endif 6646fb12b70Safresh1 char *buf; 665b8851fccSafresh1 666b8851fccSafresh1 #if PERL_VERSION_GE(5,19,0) 667b8851fccSafresh1 if (SvPOK(ver)) { 668b8851fccSafresh1 /* dualvar? */ 669b8851fccSafresh1 goto VER_PV; 670b8851fccSafresh1 } 671b8851fccSafresh1 #endif 6726fb12b70Safresh1 6736fb12b70Safresh1 { 6749f11ffb7Safresh1 6753d61058aSafresh1 #ifdef USE_POSIX_2008_LOCALE 6769f11ffb7Safresh1 6773d61058aSafresh1 /* With POSIX 2008, all we have to do is toggle to the C locale 6783d61058aSafresh1 * just long enough to get the value (which should have a dot). */ 6793d61058aSafresh1 const locale_t locale_obj_on_entry = uselocale(PL_C_locale_obj); 6803d61058aSafresh1 GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len); 6813d61058aSafresh1 uselocale(locale_obj_on_entry); 6829f11ffb7Safresh1 #else 6833d61058aSafresh1 /* Without POSIX 2008, it could be that toggling will zap another 6843d61058aSafresh1 * thread's locale. Avoid that if possible by looking at the NV and 6853d61058aSafresh1 * changing a non-dot radix into a dot */ 6869f11ffb7Safresh1 6873d61058aSafresh1 char * radix = NULL; 6883d61058aSafresh1 unsigned int radix_len = 0; 6899f11ffb7Safresh1 6903d61058aSafresh1 GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len); 6919f11ffb7Safresh1 6923d61058aSafresh1 # ifndef ARABIC_DECIMAL_SEPARATOR_UTF8 6939f11ffb7Safresh1 6943d61058aSafresh1 /* This becomes feasible since there are only very few possible 6953d61058aSafresh1 * radix characters in the world. khw knows of just 3 possible 6963d61058aSafresh1 * ones. If we are being compiled on a perl without the very rare 6973d61058aSafresh1 * third one, ARABIC DECIMAL SEPARATOR, just scan for the other 6983d61058aSafresh1 * two: FULL STOP (dot) and COMMA */ 6993d61058aSafresh1 radix = strpbrk(buf, ".,"); 7003d61058aSafresh1 if (LIKELY(radix)) { 7013d61058aSafresh1 radix_len = 1; 7023d61058aSafresh1 } 7033d61058aSafresh1 # else 7043d61058aSafresh1 /* Here, we have information about the third one; since it is 7053d61058aSafresh1 * multi-byte, it becomes a little more work. Scan for the dot, 7063d61058aSafresh1 * comma, or first byte of the arabic one */ 7073d61058aSafresh1 radix = strpbrk(buf, 7083d61058aSafresh1 ".," 7093d61058aSafresh1 ARABIC_DECIMAL_SEPARATOR_UTF8_FIRST_BYTE_s); 7103d61058aSafresh1 7113d61058aSafresh1 if (LIKELY(radix)) { 7123d61058aSafresh1 if (LIKELY( (* (U8 *) radix) 7133d61058aSafresh1 != ARABIC_DECIMAL_SEPARATOR_UTF8_FIRST_BYTE)) 7149f11ffb7Safresh1 { 7153d61058aSafresh1 radix_len = 1; /* Dot and comma are length 1 */ 7166fb12b70Safresh1 } 7176fb12b70Safresh1 else { 7183d61058aSafresh1 7193d61058aSafresh1 /* Make sure that the rest of the bytes are what we expect 7203d61058aSafresh1 * for the remainder of the arabic radix. If not, we 7213d61058aSafresh1 * didn't find the radix. */ 7223d61058aSafresh1 radix_len = STRLENs(ARABIC_DECIMAL_SEPARATOR_UTF8); 7233d61058aSafresh1 if ( radix + radix_len >= buf + len 7243d61058aSafresh1 || memNEs(radix + 1, 7253d61058aSafresh1 STRLENs(ARABIC_DECIMAL_SEPARATOR_UTF8_TAIL), 7263d61058aSafresh1 ARABIC_DECIMAL_SEPARATOR_UTF8_TAIL)) 7273d61058aSafresh1 { 7283d61058aSafresh1 radix = NULL; 7293d61058aSafresh1 radix_len = 0; 7306fb12b70Safresh1 } 7316fb12b70Safresh1 } 7329f11ffb7Safresh1 } 7339f11ffb7Safresh1 7349f11ffb7Safresh1 # endif 7359f11ffb7Safresh1 7363d61058aSafresh1 /* Now convert any found radix into a dot (if not already). This 7373d61058aSafresh1 * effectively does: ver =~ s/radix/dot/ */ 7383d61058aSafresh1 if (radix) { 7393d61058aSafresh1 if (*radix != '.') { 7403d61058aSafresh1 *radix = '.'; 7413d61058aSafresh1 7423d61058aSafresh1 if (radix_len > 1) { 7433d61058aSafresh1 Move(radix + radix_len, /* from what follows the radix 7443d61058aSafresh1 */ 7453d61058aSafresh1 radix + 1, /* to just after the new dot */ 7463d61058aSafresh1 7473d61058aSafresh1 /* the number of bytes remaining, plus the NUL 7483d61058aSafresh1 * */ 7493d61058aSafresh1 len - (radix - buf) - radix_len + 1, 7503d61058aSafresh1 char); 7513d61058aSafresh1 len -= radix_len - 1; 7523d61058aSafresh1 } 7539f11ffb7Safresh1 } 7549f11ffb7Safresh1 7553d61058aSafresh1 /* Guard against the very unlikely case that the radix is more 7563d61058aSafresh1 * than a single character, like ".."; that is, make sure the 7573d61058aSafresh1 * radix string we found above is the whole radix, and not just 7583d61058aSafresh1 * the prefix of a longer one. Success is indicated by it 7593d61058aSafresh1 * being at the end of the string, or the next byte should be a 7603d61058aSafresh1 * digit */ 7613d61058aSafresh1 if (radix < buf + len && ! inRANGE(radix[1], '0', '9')) { 7623d61058aSafresh1 radix = NULL; 7633d61058aSafresh1 radix_len = 0; 7643d61058aSafresh1 } 7653d61058aSafresh1 } 7669f11ffb7Safresh1 7673d61058aSafresh1 if (! radix) { 7683d61058aSafresh1 7693d61058aSafresh1 /* If we couldn't find what the radix is, or didn't find it in 7703d61058aSafresh1 * the PV, resort to toggling the locale to one known to have a 7713d61058aSafresh1 * dot radix. This may or may not be called from code that has 7723d61058aSafresh1 * switched locales without letting perl know, therefore we 7733d61058aSafresh1 * have to find it from first principals. See [perl #121930]. 7743d61058aSafresh1 * */ 7753d61058aSafresh1 7763d61058aSafresh1 # if ! defined(LC_NUMERIC) || ! defined(USE_LOCALE_NUMERIC) 7773d61058aSafresh1 7783d61058aSafresh1 Perl_croak(aTHX_ "panic: Unexpectedly didn't find a dot radix" 7793d61058aSafresh1 " character in '%s'", buf); 7803d61058aSafresh1 # else 7813d61058aSafresh1 const char * locale_name_on_entry = NULL; 7823d61058aSafresh1 7833d61058aSafresh1 /* In windows, or not threaded, or not thread-safe, if it isn't 7843d61058aSafresh1 * C, set it to C. */ 7853d61058aSafresh1 7863d61058aSafresh1 POSIX_SETLOCALE_LOCK; /* Start critical section */ 7873d61058aSafresh1 7883d61058aSafresh1 locale_name_on_entry = setlocale(LC_NUMERIC, NULL); 7893d61058aSafresh1 if ( strEQ(locale_name_on_entry, "C") 7903d61058aSafresh1 || strEQ(locale_name_on_entry, "C.UTF-8") 7913d61058aSafresh1 || strEQ(locale_name_on_entry, "POSIX")) 7923d61058aSafresh1 { 7933d61058aSafresh1 /* No need to change the locale, since these all are known 7943d61058aSafresh1 * to have a dot radix. Change the variable to indicate to 7953d61058aSafresh1 * the restore code that nothing needs to be done */ 7963d61058aSafresh1 locale_name_on_entry = NULL; 7973d61058aSafresh1 } 7983d61058aSafresh1 else { 7993d61058aSafresh1 /* The setlocale() call might free or overwrite the name */ 8003d61058aSafresh1 locale_name_on_entry = savepv(locale_name_on_entry); 8013d61058aSafresh1 setlocale(LC_NUMERIC, "C"); 8023d61058aSafresh1 } 8033d61058aSafresh1 8043d61058aSafresh1 GET_NUMERIC_VERSION(ver, sv, tbuf, buf, len); 8053d61058aSafresh1 8063d61058aSafresh1 if (locale_name_on_entry) { 8073d61058aSafresh1 setlocale(LC_NUMERIC, locale_name_on_entry); 8083d61058aSafresh1 Safefree(locale_name_on_entry); 8093d61058aSafresh1 } 8103d61058aSafresh1 8113d61058aSafresh1 POSIX_SETLOCALE_UNLOCK; /* End critical section */ 8123d61058aSafresh1 # endif 8133d61058aSafresh1 } 8143d61058aSafresh1 #endif 8153d61058aSafresh1 } 8163d61058aSafresh1 8173d61058aSafresh1 /* Strip trailing zero's from the version number */ 8186fb12b70Safresh1 while (buf[len-1] == '0' && len > 0) len--; 8193d61058aSafresh1 8206fb12b70Safresh1 if ( buf[len-1] == '.' ) len--; /* eat the trailing decimal */ 8213d61058aSafresh1 8226fb12b70Safresh1 version = savepvn(buf, len); 8236fb12b70Safresh1 SAVEFREEPV(version); 8246fb12b70Safresh1 SvREFCNT_dec(sv); 8256fb12b70Safresh1 } 8266fb12b70Safresh1 #ifdef SvVOK 8276fb12b70Safresh1 else if ( (mg = SvVSTRING_mg(ver)) ) { /* already a v-string */ 8286fb12b70Safresh1 version = savepvn( (const char*)mg->mg_ptr,mg->mg_len ); 8296fb12b70Safresh1 SAVEFREEPV(version); 8306fb12b70Safresh1 qv = TRUE; 8316fb12b70Safresh1 } 8326fb12b70Safresh1 #endif 8336fb12b70Safresh1 else if ( SvPOK(ver))/* must be a string or something like a string */ 8346fb12b70Safresh1 VER_PV: 8356fb12b70Safresh1 { 8366fb12b70Safresh1 STRLEN len; 8376fb12b70Safresh1 version = savepvn(SvPV(ver,len), SvCUR(ver)); 8386fb12b70Safresh1 SAVEFREEPV(version); 8396fb12b70Safresh1 #ifndef SvVOK 8406fb12b70Safresh1 /* This will only be executed for 5.6.0 - 5.8.0 inclusive */ 8416fb12b70Safresh1 if ( len >= 3 && !instr(version,".") && !instr(version,"_")) { 8426fb12b70Safresh1 /* may be a v-string */ 8436fb12b70Safresh1 char *testv = (char *)version; 8446fb12b70Safresh1 STRLEN tlen = len; 8456fb12b70Safresh1 for (tlen=0; tlen < len; tlen++, testv++) { 8466fb12b70Safresh1 /* if one of the characters is non-text assume v-string */ 8476fb12b70Safresh1 if (testv[0] < ' ') { 8486fb12b70Safresh1 SV * const nsv = sv_newmortal(); 8496fb12b70Safresh1 const char *nver; 8506fb12b70Safresh1 const char *pos; 8516fb12b70Safresh1 int saw_decimal = 0; 8526fb12b70Safresh1 sv_setpvf(nsv,"v%vd",ver); 8536fb12b70Safresh1 pos = nver = savepv(SvPV_nolen(nsv)); 8546fb12b70Safresh1 SAVEFREEPV(pos); 8556fb12b70Safresh1 8566fb12b70Safresh1 /* scan the resulting formatted string */ 8576fb12b70Safresh1 pos++; /* skip the leading 'v' */ 8586fb12b70Safresh1 while ( *pos == '.' || isDIGIT(*pos) ) { 8596fb12b70Safresh1 if ( *pos == '.' ) 8606fb12b70Safresh1 saw_decimal++ ; 8616fb12b70Safresh1 pos++; 8626fb12b70Safresh1 } 8636fb12b70Safresh1 8646fb12b70Safresh1 /* is definitely a v-string */ 8656fb12b70Safresh1 if ( saw_decimal >= 2 ) { 8666fb12b70Safresh1 version = nver; 8676fb12b70Safresh1 } 8686fb12b70Safresh1 break; 8696fb12b70Safresh1 } 8706fb12b70Safresh1 } 8716fb12b70Safresh1 } 8726fb12b70Safresh1 #endif 8736fb12b70Safresh1 } 8746fb12b70Safresh1 #if PERL_VERSION_LT(5,17,2) 8756fb12b70Safresh1 else if (SvIOKp(ver)) { 8766fb12b70Safresh1 goto VER_IV; 8776fb12b70Safresh1 } 8786fb12b70Safresh1 else if (SvNOKp(ver)) { 8796fb12b70Safresh1 goto VER_NV; 8806fb12b70Safresh1 } 8816fb12b70Safresh1 else if (SvPOKp(ver)) { 8826fb12b70Safresh1 goto VER_PV; 8836fb12b70Safresh1 } 8846fb12b70Safresh1 #endif 8856fb12b70Safresh1 else 8866fb12b70Safresh1 { 8876fb12b70Safresh1 /* no idea what this is */ 8886fb12b70Safresh1 Perl_croak(aTHX_ "Invalid version format (non-numeric data)"); 8896fb12b70Safresh1 } 8906fb12b70Safresh1 8916fb12b70Safresh1 s = SCAN_VERSION(version, ver, qv); 8926fb12b70Safresh1 if ( *s != '\0' ) 8936fb12b70Safresh1 Perl_ck_warner(aTHX_ packWARN(WARN_MISC), 8946fb12b70Safresh1 "Version string '%s' contains invalid data; " 8956fb12b70Safresh1 "ignoring: '%s'", version, s); 8966fb12b70Safresh1 8976fb12b70Safresh1 #if PERL_VERSION_LT(5,19,8) && defined(USE_ITHREADS) 8986fb12b70Safresh1 LEAVE; 8996fb12b70Safresh1 #endif 9006fb12b70Safresh1 9016fb12b70Safresh1 return ver; 9026fb12b70Safresh1 } 9036fb12b70Safresh1 9046fb12b70Safresh1 /* 9056fb12b70Safresh1 =for apidoc vverify 9066fb12b70Safresh1 9076fb12b70Safresh1 Validates that the SV contains valid internal structure for a version object. 9086fb12b70Safresh1 It may be passed either the version object (RV) or the hash itself (HV). If 9096fb12b70Safresh1 the structure is valid, it returns the HV. If the structure is invalid, 9106fb12b70Safresh1 it returns NULL. 9116fb12b70Safresh1 9126fb12b70Safresh1 SV *hv = vverify(sv); 9136fb12b70Safresh1 9146fb12b70Safresh1 Note that it only confirms the bare minimum structure (so as not to get 9156fb12b70Safresh1 confused by derived classes which may contain additional hash entries): 9166fb12b70Safresh1 9176fb12b70Safresh1 =over 4 9186fb12b70Safresh1 9196fb12b70Safresh1 =item * The SV is an HV or a reference to an HV 9206fb12b70Safresh1 9216fb12b70Safresh1 =item * The hash contains a "version" key 9226fb12b70Safresh1 9236fb12b70Safresh1 =item * The "version" key has a reference to an AV as its value 9246fb12b70Safresh1 9256fb12b70Safresh1 =back 9266fb12b70Safresh1 9276fb12b70Safresh1 =cut 9286fb12b70Safresh1 */ 9296fb12b70Safresh1 9306fb12b70Safresh1 SV * 9316fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 9326fb12b70Safresh1 Perl_vverify2(pTHX_ SV *vs) 9336fb12b70Safresh1 #else 9346fb12b70Safresh1 Perl_vverify(pTHX_ SV *vs) 9356fb12b70Safresh1 #endif 9366fb12b70Safresh1 { 9376fb12b70Safresh1 SV *sv; 9386fb12b70Safresh1 SV **svp; 9396fb12b70Safresh1 9406fb12b70Safresh1 PERL_ARGS_ASSERT_VVERIFY; 9416fb12b70Safresh1 9426fb12b70Safresh1 if ( SvROK(vs) ) 9436fb12b70Safresh1 vs = SvRV(vs); 9446fb12b70Safresh1 9456fb12b70Safresh1 /* see if the appropriate elements exist */ 9466fb12b70Safresh1 if ( SvTYPE(vs) == SVt_PVHV 9476fb12b70Safresh1 && (svp = hv_fetchs(MUTABLE_HV(vs), "version", FALSE)) 9486fb12b70Safresh1 && (sv = SvRV(*svp)) 9496fb12b70Safresh1 && SvTYPE(sv) == SVt_PVAV ) 9506fb12b70Safresh1 return vs; 9516fb12b70Safresh1 else 9526fb12b70Safresh1 return NULL; 9536fb12b70Safresh1 } 9546fb12b70Safresh1 9556fb12b70Safresh1 /* 9566fb12b70Safresh1 =for apidoc vnumify 9576fb12b70Safresh1 9586fb12b70Safresh1 Accepts a version object and returns the normalized floating 9596fb12b70Safresh1 point representation. Call like: 9606fb12b70Safresh1 9616fb12b70Safresh1 sv = vnumify(rv); 9626fb12b70Safresh1 9636fb12b70Safresh1 NOTE: you can pass either the object directly or the SV 9646fb12b70Safresh1 contained within the RV. 9656fb12b70Safresh1 9666fb12b70Safresh1 The SV returned has a refcount of 1. 9676fb12b70Safresh1 9686fb12b70Safresh1 =cut 9696fb12b70Safresh1 */ 9706fb12b70Safresh1 9716fb12b70Safresh1 SV * 9726fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 9736fb12b70Safresh1 Perl_vnumify2(pTHX_ SV *vs) 9746fb12b70Safresh1 #else 9756fb12b70Safresh1 Perl_vnumify(pTHX_ SV *vs) 9766fb12b70Safresh1 #endif 9776fb12b70Safresh1 { 9786fb12b70Safresh1 SSize_t i, len; 9796fb12b70Safresh1 I32 digit; 9806fb12b70Safresh1 bool alpha = FALSE; 9816fb12b70Safresh1 SV *sv; 9826fb12b70Safresh1 AV *av; 9836fb12b70Safresh1 9846fb12b70Safresh1 PERL_ARGS_ASSERT_VNUMIFY; 9856fb12b70Safresh1 9866fb12b70Safresh1 /* extract the HV from the object */ 9876fb12b70Safresh1 vs = VVERIFY(vs); 9886fb12b70Safresh1 if ( ! vs ) 9896fb12b70Safresh1 Perl_croak(aTHX_ "Invalid version object"); 9906fb12b70Safresh1 9916fb12b70Safresh1 /* see if various flags exist */ 9926fb12b70Safresh1 if ( hv_exists(MUTABLE_HV(vs), "alpha", 5 ) ) 9936fb12b70Safresh1 alpha = TRUE; 9946fb12b70Safresh1 995b8851fccSafresh1 if (alpha) { 996b8851fccSafresh1 Perl_ck_warner(aTHX_ packWARN(WARN_NUMERIC), 997b8851fccSafresh1 "alpha->numify() is lossy"); 998b8851fccSafresh1 } 9996fb12b70Safresh1 10006fb12b70Safresh1 /* attempt to retrieve the version array */ 10016fb12b70Safresh1 if ( !(av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE))) ) ) { 10026fb12b70Safresh1 return newSVpvs("0"); 10036fb12b70Safresh1 } 10046fb12b70Safresh1 10056fb12b70Safresh1 len = av_len(av); 10066fb12b70Safresh1 if ( len == -1 ) 10076fb12b70Safresh1 { 10086fb12b70Safresh1 return newSVpvs("0"); 10096fb12b70Safresh1 } 10106fb12b70Safresh1 10116fb12b70Safresh1 { 10126fb12b70Safresh1 SV * tsv = *av_fetch(av, 0, 0); 10136fb12b70Safresh1 digit = SvIV(tsv); 10146fb12b70Safresh1 } 10156fb12b70Safresh1 sv = Perl_newSVpvf(aTHX_ "%d.", (int)PERL_ABS(digit)); 1016b8851fccSafresh1 for ( i = 1 ; i <= len ; i++ ) 10176fb12b70Safresh1 { 10186fb12b70Safresh1 SV * tsv = *av_fetch(av, i, 0); 10196fb12b70Safresh1 digit = SvIV(tsv); 1020b8851fccSafresh1 Perl_sv_catpvf(aTHX_ sv, "%03d", (int)digit); 10216fb12b70Safresh1 } 10226fb12b70Safresh1 1023b8851fccSafresh1 if ( len == 0 ) { 10246fb12b70Safresh1 sv_catpvs(sv, "000"); 10256fb12b70Safresh1 } 10266fb12b70Safresh1 return sv; 10276fb12b70Safresh1 } 10286fb12b70Safresh1 10296fb12b70Safresh1 /* 10306fb12b70Safresh1 =for apidoc vnormal 10316fb12b70Safresh1 10326fb12b70Safresh1 Accepts a version object and returns the normalized string 10336fb12b70Safresh1 representation. Call like: 10346fb12b70Safresh1 10356fb12b70Safresh1 sv = vnormal(rv); 10366fb12b70Safresh1 10376fb12b70Safresh1 NOTE: you can pass either the object directly or the SV 10386fb12b70Safresh1 contained within the RV. 10396fb12b70Safresh1 10406fb12b70Safresh1 The SV returned has a refcount of 1. 10416fb12b70Safresh1 10426fb12b70Safresh1 =cut 10436fb12b70Safresh1 */ 10446fb12b70Safresh1 10456fb12b70Safresh1 SV * 10466fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 10476fb12b70Safresh1 Perl_vnormal2(pTHX_ SV *vs) 10486fb12b70Safresh1 #else 10496fb12b70Safresh1 Perl_vnormal(pTHX_ SV *vs) 10506fb12b70Safresh1 #endif 10516fb12b70Safresh1 { 10526fb12b70Safresh1 I32 i, len, digit; 10536fb12b70Safresh1 SV *sv; 10546fb12b70Safresh1 AV *av; 10556fb12b70Safresh1 10566fb12b70Safresh1 PERL_ARGS_ASSERT_VNORMAL; 10576fb12b70Safresh1 10586fb12b70Safresh1 /* extract the HV from the object */ 10596fb12b70Safresh1 vs = VVERIFY(vs); 10606fb12b70Safresh1 if ( ! vs ) 10616fb12b70Safresh1 Perl_croak(aTHX_ "Invalid version object"); 10626fb12b70Safresh1 10636fb12b70Safresh1 av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE))); 10646fb12b70Safresh1 10656fb12b70Safresh1 len = av_len(av); 10666fb12b70Safresh1 if ( len == -1 ) 10676fb12b70Safresh1 { 10686fb12b70Safresh1 return newSVpvs(""); 10696fb12b70Safresh1 } 10706fb12b70Safresh1 { 10716fb12b70Safresh1 SV * tsv = *av_fetch(av, 0, 0); 10726fb12b70Safresh1 digit = SvIV(tsv); 10736fb12b70Safresh1 } 10746fb12b70Safresh1 sv = Perl_newSVpvf(aTHX_ "v%" IVdf, (IV)digit); 1075b8851fccSafresh1 for ( i = 1 ; i <= len ; i++ ) { 10766fb12b70Safresh1 SV * tsv = *av_fetch(av, i, 0); 10776fb12b70Safresh1 digit = SvIV(tsv); 10786fb12b70Safresh1 Perl_sv_catpvf(aTHX_ sv, ".%" IVdf, (IV)digit); 10796fb12b70Safresh1 } 10806fb12b70Safresh1 10816fb12b70Safresh1 if ( len <= 2 ) { /* short version, must be at least three */ 10826fb12b70Safresh1 for ( len = 2 - len; len != 0; len-- ) 10836fb12b70Safresh1 sv_catpvs(sv,".0"); 10846fb12b70Safresh1 } 10856fb12b70Safresh1 return sv; 10866fb12b70Safresh1 } 10876fb12b70Safresh1 10886fb12b70Safresh1 /* 10896fb12b70Safresh1 =for apidoc vstringify 10906fb12b70Safresh1 10916fb12b70Safresh1 In order to maintain maximum compatibility with earlier versions 10926fb12b70Safresh1 of Perl, this function will return either the floating point 10936fb12b70Safresh1 notation or the multiple dotted notation, depending on whether 10946fb12b70Safresh1 the original version contained 1 or more dots, respectively. 10956fb12b70Safresh1 10966fb12b70Safresh1 The SV returned has a refcount of 1. 10976fb12b70Safresh1 10986fb12b70Safresh1 =cut 10996fb12b70Safresh1 */ 11006fb12b70Safresh1 11016fb12b70Safresh1 SV * 11026fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 11036fb12b70Safresh1 Perl_vstringify2(pTHX_ SV *vs) 11046fb12b70Safresh1 #else 11056fb12b70Safresh1 Perl_vstringify(pTHX_ SV *vs) 11066fb12b70Safresh1 #endif 11076fb12b70Safresh1 { 11086fb12b70Safresh1 SV ** svp; 11096fb12b70Safresh1 PERL_ARGS_ASSERT_VSTRINGIFY; 11106fb12b70Safresh1 11116fb12b70Safresh1 /* extract the HV from the object */ 11126fb12b70Safresh1 vs = VVERIFY(vs); 11136fb12b70Safresh1 if ( ! vs ) 11146fb12b70Safresh1 Perl_croak(aTHX_ "Invalid version object"); 11156fb12b70Safresh1 11166fb12b70Safresh1 svp = hv_fetchs(MUTABLE_HV(vs), "original", FALSE); 11176fb12b70Safresh1 if (svp) { 11186fb12b70Safresh1 SV *pv; 11196fb12b70Safresh1 pv = *svp; 11209f11ffb7Safresh1 if ( SvPOK(pv) 11219f11ffb7Safresh1 #if PERL_VERSION_LT(5,17,2) 11229f11ffb7Safresh1 || SvPOKp(pv) 11239f11ffb7Safresh1 #endif 11249f11ffb7Safresh1 ) 11256fb12b70Safresh1 return newSVsv(pv); 11266fb12b70Safresh1 else 11276fb12b70Safresh1 return &PL_sv_undef; 11286fb12b70Safresh1 } 11296fb12b70Safresh1 else { 11306fb12b70Safresh1 if ( hv_exists(MUTABLE_HV(vs), "qv", 2) ) 11316fb12b70Safresh1 return VNORMAL(vs); 11326fb12b70Safresh1 else 11336fb12b70Safresh1 return VNUMIFY(vs); 11346fb12b70Safresh1 } 11356fb12b70Safresh1 } 11366fb12b70Safresh1 11376fb12b70Safresh1 /* 11386fb12b70Safresh1 =for apidoc vcmp 11396fb12b70Safresh1 11406fb12b70Safresh1 Version object aware cmp. Both operands must already have been 11416fb12b70Safresh1 converted into version objects. 11426fb12b70Safresh1 11436fb12b70Safresh1 =cut 11446fb12b70Safresh1 */ 11456fb12b70Safresh1 11466fb12b70Safresh1 int 11476fb12b70Safresh1 #ifdef VUTIL_REPLACE_CORE 11486fb12b70Safresh1 Perl_vcmp2(pTHX_ SV *lhv, SV *rhv) 11496fb12b70Safresh1 #else 11506fb12b70Safresh1 Perl_vcmp(pTHX_ SV *lhv, SV *rhv) 11516fb12b70Safresh1 #endif 11526fb12b70Safresh1 { 11536fb12b70Safresh1 SSize_t i,l,m,r; 11546fb12b70Safresh1 I32 retval; 11556fb12b70Safresh1 I32 left = 0; 11566fb12b70Safresh1 I32 right = 0; 11576fb12b70Safresh1 AV *lav, *rav; 11586fb12b70Safresh1 11596fb12b70Safresh1 PERL_ARGS_ASSERT_VCMP; 11606fb12b70Safresh1 11616fb12b70Safresh1 /* extract the HVs from the objects */ 11626fb12b70Safresh1 lhv = VVERIFY(lhv); 11636fb12b70Safresh1 rhv = VVERIFY(rhv); 11646fb12b70Safresh1 if ( ! ( lhv && rhv ) ) 11656fb12b70Safresh1 Perl_croak(aTHX_ "Invalid version object"); 11666fb12b70Safresh1 11676fb12b70Safresh1 /* get the left hand term */ 11686fb12b70Safresh1 lav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(lhv), "version", FALSE))); 11696fb12b70Safresh1 11706fb12b70Safresh1 /* and the right hand term */ 11716fb12b70Safresh1 rav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(rhv), "version", FALSE))); 11726fb12b70Safresh1 11736fb12b70Safresh1 l = av_len(lav); 11746fb12b70Safresh1 r = av_len(rav); 11756fb12b70Safresh1 m = l < r ? l : r; 11766fb12b70Safresh1 retval = 0; 11776fb12b70Safresh1 i = 0; 11786fb12b70Safresh1 while ( i <= m && retval == 0 ) 11796fb12b70Safresh1 { 11806fb12b70Safresh1 SV * const lsv = *av_fetch(lav,i,0); 11816fb12b70Safresh1 SV * rsv; 11826fb12b70Safresh1 left = SvIV(lsv); 11836fb12b70Safresh1 rsv = *av_fetch(rav,i,0); 11846fb12b70Safresh1 right = SvIV(rsv); 11856fb12b70Safresh1 if ( left < right ) 11866fb12b70Safresh1 retval = -1; 11876fb12b70Safresh1 if ( left > right ) 11886fb12b70Safresh1 retval = +1; 11896fb12b70Safresh1 i++; 11906fb12b70Safresh1 } 11916fb12b70Safresh1 11926fb12b70Safresh1 if ( l != r && retval == 0 ) /* possible match except for trailing 0's */ 11936fb12b70Safresh1 { 11946fb12b70Safresh1 if ( l < r ) 11956fb12b70Safresh1 { 11966fb12b70Safresh1 while ( i <= r && retval == 0 ) 11976fb12b70Safresh1 { 11986fb12b70Safresh1 SV * const rsv = *av_fetch(rav,i,0); 11996fb12b70Safresh1 if ( SvIV(rsv) != 0 ) 12006fb12b70Safresh1 retval = -1; /* not a match after all */ 12016fb12b70Safresh1 i++; 12026fb12b70Safresh1 } 12036fb12b70Safresh1 } 12046fb12b70Safresh1 else 12056fb12b70Safresh1 { 12066fb12b70Safresh1 while ( i <= l && retval == 0 ) 12076fb12b70Safresh1 { 12086fb12b70Safresh1 SV * const lsv = *av_fetch(lav,i,0); 12096fb12b70Safresh1 if ( SvIV(lsv) != 0 ) 12106fb12b70Safresh1 retval = +1; /* not a match after all */ 12116fb12b70Safresh1 i++; 12126fb12b70Safresh1 } 12136fb12b70Safresh1 } 12146fb12b70Safresh1 } 12156fb12b70Safresh1 return retval; 12166fb12b70Safresh1 } 1217b8851fccSafresh1 1218b8851fccSafresh1 /* ex: set ro: */ 1219