xref: /openbsd-src/gnu/usr.bin/perl/vutil.c (revision e0a5400065cea17a7de6532c2ecb091c5f17622b)
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