1 /* This file is part of the "version" CPAN distribution. Please avoid
2 editing it in the perl core. */
8 # define NEED_my_snprintf
9 # define NEED_newRV_noinc
10 # define NEED_vnewSVpvf
11 # define NEED_newSVpvn_flags_GLOBAL
17 #define VERSION_MAX 0x7FFFFFFF
20 =for apidoc prescan_version
22 Validate that a given string can be parsed as a version object, but doesn't
23 actually perform the parsing. Can use either strict or lax validation rules.
24 Can optionally set a number of hint variables to save the parsing code
25 some time when tokenizing.
30 #if VUTIL_REPLACE_CORE
31 Perl_prescan_version2(pTHX_ const char *s, bool strict,
33 Perl_prescan_version(pTHX_ const char *s, bool strict,
36 bool *sqv, int *ssaw_decimal, int *swidth, bool *salpha) {
37 bool qv = (sqv ? *sqv : FALSE);
43 PERL_ARGS_ASSERT_PRESCAN_VERSION;
45 if (qv && isDIGIT(*d))
46 goto dotted_decimal_version;
48 if (*d == 'v') { /* explicit v-string */
53 else { /* degenerate v-string */
55 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
58 dotted_decimal_version:
59 if (strict && d[0] == '0' && isDIGIT(d[1])) {
60 /* no leading zeros allowed */
61 BADVERSION(s,errstr,"Invalid version format (no leading zeros)");
64 while (isDIGIT(*d)) /* integer part */
70 d++; /* decimal point */
76 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
79 goto version_prescan_finish;
86 while (isDIGIT(*d)) { /* just keep reading */
90 /* maximum 3 digits between decimal */
91 if (strict && j > 3) {
92 BADVERSION(s,errstr,"Invalid version format (maximum 3 digits between decimals)");
97 BADVERSION(s,errstr,"Invalid version format (no underscores)");
100 BADVERSION(s,errstr,"Invalid version format (multiple underscores)");
105 else if (*d == '.') {
107 BADVERSION(s,errstr,"Invalid version format (underscores before decimal)");
112 else if (!isDIGIT(*d)) {
118 if (strict && i < 2) {
119 /* requires v1.2.3 */
120 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
123 } /* end if dotted-decimal */
125 { /* decimal versions */
126 int j = 0; /* may need this later */
127 /* special strict case for leading '.' or '0' */
130 BADVERSION(s,errstr,"Invalid version format (0 before decimal required)");
132 if (*d == '0' && isDIGIT(d[1])) {
133 BADVERSION(s,errstr,"Invalid version format (no leading zeros)");
137 /* and we never support negative versions */
139 BADVERSION(s,errstr,"Invalid version format (negative version number)");
142 /* consume all of the integer part */
146 /* look for a fractional part */
148 /* we found it, so consume it */
152 else if (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') {
155 BADVERSION(s,errstr,"Invalid version format (version required)");
157 /* found just an integer */
158 goto version_prescan_finish;
161 /* didn't find either integer or period */
162 BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
164 else if (*d == '_') {
165 /* underscore can't come after integer part */
167 BADVERSION(s,errstr,"Invalid version format (no underscores)");
169 else if (isDIGIT(d[1])) {
170 BADVERSION(s,errstr,"Invalid version format (alpha without decimal)");
173 BADVERSION(s,errstr,"Invalid version format (misplaced underscore)");
177 /* anything else after integer part is just invalid data */
178 BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
181 /* scan the fractional part after the decimal point*/
183 if (!isDIGIT(*d) && (strict || ! (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') )) {
184 /* strict or lax-but-not-the-end */
185 BADVERSION(s,errstr,"Invalid version format (fractional part required)");
188 while (isDIGIT(*d)) {
190 if (*d == '.' && isDIGIT(d[-1])) {
192 BADVERSION(s,errstr,"Invalid version format (underscores before decimal)");
195 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
197 d = (char *)s; /* start all over again */
199 goto dotted_decimal_version;
203 BADVERSION(s,errstr,"Invalid version format (no underscores)");
206 BADVERSION(s,errstr,"Invalid version format (multiple underscores)");
208 if ( ! isDIGIT(d[1]) ) {
209 BADVERSION(s,errstr,"Invalid version format (misplaced underscore)");
218 version_prescan_finish:
222 if (!isDIGIT(*d) && (! (!*d || *d == ';' || *d == '{' || *d == '}') )) {
223 /* trailing non-numeric data */
224 BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
232 *ssaw_decimal = saw_decimal;
239 =for apidoc scan_version
241 Returns a pointer to the next character after the parsed
242 version string, as well as upgrading the passed in SV to
245 Function must be called with an already existing SV like
248 s = scan_version(s, SV *sv, bool qv);
250 Performs some preprocessing to the string to ensure that
251 it has the correct characteristics of a version. Flags the
252 object if it contains an underscore (which denotes this
253 is an alpha version). The boolean qv denotes that the version
254 should be interpreted as if it had multiple decimals, even if
261 #if VUTIL_REPLACE_CORE
262 Perl_scan_version2(pTHX_ const char *s, SV *rv, bool qv)
264 Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
267 const char *start = s;
270 const char *errstr = NULL;
278 PERL_ARGS_ASSERT_SCAN_VERSION;
280 while (isSPACE(*s)) /* leading whitespace is OK */
283 last = PRESCAN_VERSION(s, FALSE, &errstr, &qv, &saw_decimal, &width, &alpha);
285 /* "undef" is a special case and not an error */
286 if ( ! ( *s == 'u' && strEQ(s,"undef")) ) {
288 Perl_croak(aTHX_ "%s", errstr);
297 /* Now that we are through the prescan, start creating the object */
299 hv = newSVrv(rv, "version"); /* create an SV and upgrade the RV */
300 (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */
302 #ifndef NODEFAULT_SHAREKEYS
303 HvSHAREKEYS_on(hv); /* key-sharing on by default */
307 (void)hv_stores(MUTABLE_HV(hv), "qv", newSViv(qv));
309 (void)hv_stores(MUTABLE_HV(hv), "alpha", newSViv(alpha));
310 if ( !qv && width < 3 )
311 (void)hv_stores(MUTABLE_HV(hv), "width", newSViv(width));
313 while (isDIGIT(*pos))
315 if (!isALPHA(*pos)) {
321 /* this is atoi() that delimits on underscores */
322 const char *end = pos;
326 /* the following if() will only be true after the decimal
327 * point of a version originally created with a bare
328 * floating point number, i.e. not quoted in any way
330 if ( !qv && s > start && saw_decimal == 1 ) {
334 rev += (*s - '0') * mult;
336 if ( (PERL_ABS(orev) > PERL_ABS(rev))
337 || (PERL_ABS(rev) > VERSION_MAX )) {
338 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
339 "Integer overflow in version %d",VERSION_MAX);
352 rev += (*end - '0') * mult;
354 if ( (PERL_ABS(orev) > PERL_ABS(rev))
355 || (PERL_ABS(rev) > VERSION_MAX )) {
356 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
357 "Integer overflow in version");
366 /* Append revision */
367 av_push(av, newSViv(rev));
372 else if ( *pos == '.' )
374 else if ( *pos == '_' && isDIGIT(pos[1]) )
376 else if ( *pos == ',' && isDIGIT(pos[1]) )
378 else if ( isDIGIT(*pos) )
385 while ( isDIGIT(*pos) )
390 while ( ( isDIGIT(*pos) || *pos == '_' ) && digits < 3 ) {
398 if ( qv ) { /* quoted versions always get at least three terms*/
399 SSize_t len = av_len(av);
400 /* This for loop appears to trigger a compiler bug on OS X, as it
401 loops infinitely. Yes, len is negative. No, it makes no sense.
402 Compiler in question is:
403 gcc version 3.3 20030304 (Apple Computer, Inc. build 1640)
404 for ( len = 2 - len; len > 0; len-- )
405 av_push(MUTABLE_AV(sv), newSViv(0));
409 av_push(av, newSViv(0));
412 /* need to save off the current version string for later */
414 SV * orig = newSVpvn("v.Inf", sizeof("v.Inf")-1);
415 (void)hv_stores(MUTABLE_HV(hv), "original", orig);
416 (void)hv_stores(MUTABLE_HV(hv), "vinf", newSViv(1));
418 else if ( s > start ) {
419 SV * orig = newSVpvn(start,s-start);
420 if ( qv && saw_decimal == 1 && *start != 'v' ) {
421 /* need to insert a v to be consistent */
422 sv_insert(orig, 0, 0, "v", 1);
424 (void)hv_stores(MUTABLE_HV(hv), "original", orig);
427 (void)hv_stores(MUTABLE_HV(hv), "original", newSVpvs("0"));
428 av_push(av, newSViv(0));
431 /* And finally, store the AV in the hash */
432 (void)hv_stores(MUTABLE_HV(hv), "version", newRV_noinc(MUTABLE_SV(av)));
434 /* fix RT#19517 - special case 'undef' as string */
435 if ( *s == 'u' && strEQ(s,"undef") ) {
443 =for apidoc new_version
445 Returns a new version object based on the passed in SV:
447 SV *sv = new_version(SV *ver);
449 Does not alter the passed in ver SV. See "upg_version" if you
450 want to upgrade the SV.
456 #if VUTIL_REPLACE_CORE
457 Perl_new_version2(pTHX_ SV *ver)
459 Perl_new_version(pTHX_ SV *ver)
463 SV * const rv = newSV(0);
464 PERL_ARGS_ASSERT_NEW_VERSION;
465 if ( ISA_CLASS_OBJ(ver,"version") ) /* can just copy directly */
468 AV * const av = newAV();
470 /* This will get reblessed later if a derived class*/
471 SV * const hv = newSVrv(rv, "version");
472 (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */
473 #ifndef NODEFAULT_SHAREKEYS
474 HvSHAREKEYS_on(hv); /* key-sharing on by default */
480 /* Begin copying all of the elements */
481 if ( hv_exists(MUTABLE_HV(ver), "qv", 2) )
482 (void)hv_stores(MUTABLE_HV(hv), "qv", newSViv(1));
484 if ( hv_exists(MUTABLE_HV(ver), "alpha", 5) )
485 (void)hv_stores(MUTABLE_HV(hv), "alpha", newSViv(1));
487 if ( hv_exists(MUTABLE_HV(ver), "width", 5 ) )
489 const I32 width = SvIV(*hv_fetchs(MUTABLE_HV(ver), "width", FALSE));
490 (void)hv_stores(MUTABLE_HV(hv), "width", newSViv(width));
493 if ( hv_exists(MUTABLE_HV(ver), "original", 8 ) )
495 SV * pv = *hv_fetchs(MUTABLE_HV(ver), "original", FALSE);
496 (void)hv_stores(MUTABLE_HV(hv), "original", newSVsv(pv));
499 sav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(ver), "version", FALSE)));
500 /* This will get reblessed later if a derived class*/
501 for ( key = 0; key <= av_len(sav); key++ )
503 const I32 rev = SvIV(*av_fetch(sav, key, FALSE));
504 av_push(av, newSViv(rev));
507 (void)hv_stores(MUTABLE_HV(hv), "version", newRV_noinc(MUTABLE_SV(av)));
512 const MAGIC* const mg = SvVSTRING_mg(ver);
513 if ( mg ) { /* already a v-string */
514 const STRLEN len = mg->mg_len;
515 char * const version = savepvn( (const char*)mg->mg_ptr, len);
516 sv_setpvn(rv,version,len);
517 /* this is for consistency with the pure Perl class */
518 if ( isDIGIT(*version) )
519 sv_insert(rv, 0, 0, "v", 1);
524 SvSetSV_nosteal(rv, ver); /* make a duplicate */
529 return UPG_VERSION(rv, FALSE);
533 =for apidoc upg_version
535 In-place upgrade of the supplied SV to a version object.
537 SV *sv = upg_version(SV *sv, bool qv);
539 Returns a pointer to the upgraded SV. Set the boolean qv if you want
540 to force this SV to be interpreted as an "extended" version.
546 #if VUTIL_REPLACE_CORE
547 Perl_upg_version2(pTHX_ SV *ver, bool qv)
549 Perl_upg_version(pTHX_ SV *ver, bool qv)
552 const char *version, *s;
557 PERL_ARGS_ASSERT_UPG_VERSION;
559 if ( SvNOK(ver) && !( SvPOK(ver) && sv_len(ver) == 3 ) )
563 /* may get too much accuracy */
565 SV *sv = SvNVX(ver) > 10e50 ? newSV(64) : 0;
567 #ifdef USE_LOCALE_NUMERIC
569 if (! PL_numeric_standard) {
570 loc = savepv(setlocale(LC_NUMERIC, NULL));
571 setlocale(LC_NUMERIC, "C");
575 Perl_sv_setpvf(aTHX_ sv, "%.9"NVff, SvNVX(ver));
579 len = my_snprintf(tbuf, sizeof(tbuf), "%.9"NVff, SvNVX(ver));
582 #ifdef USE_LOCALE_NUMERIC
584 setlocale(LC_NUMERIC, loc);
588 while (buf[len-1] == '0' && len > 0) len--;
589 if ( buf[len-1] == '.' ) len--; /* eat the trailing decimal */
590 version = savepvn(buf, len);
594 else if ( (mg = SvVSTRING_mg(ver)) ) { /* already a v-string */
595 version = savepvn( (const char*)mg->mg_ptr,mg->mg_len );
599 else if ( (SvUOK(ver) && SvUVX(ver) > VERSION_MAX)
600 || (SvIOK(ver) && SvIVX(ver) > VERSION_MAX) ) {
601 /* out of bounds [unsigned] integer */
604 len = my_snprintf(tbuf, sizeof(tbuf), "%d", VERSION_MAX);
605 version = savepvn(tbuf, len);
606 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
607 "Integer overflow in version %d",VERSION_MAX);
609 else if ( SvUOK(ver) || SvIOK(ver) ) {
610 version = savesvpv(ver);
612 else /* must be a string or something like a string */
615 version = savepvn(SvPV(ver,len), SvCUR(ver));
617 # if PERL_VERSION > 5
618 /* This will only be executed for 5.6.0 - 5.8.0 inclusive */
619 if ( len >= 3 && !instr(version,".") && !instr(version,"_")) {
620 /* may be a v-string */
621 char *testv = (char *)version;
623 for (tlen=0; tlen < len; tlen++, testv++) {
624 /* if one of the characters is non-text assume v-string */
625 if (testv[0] < ' ') {
626 SV * const nsv = sv_newmortal();
630 sv_setpvf(nsv,"v%vd",ver);
631 pos = nver = savepv(SvPV_nolen(nsv));
633 /* scan the resulting formatted string */
634 pos++; /* skip the leading 'v' */
635 while ( *pos == '.' || isDIGIT(*pos) ) {
641 /* is definitely a v-string */
642 if ( saw_decimal >= 2 ) {
654 s = SCAN_VERSION(version, ver, qv);
656 Perl_ck_warner(aTHX_ packWARN(WARN_MISC),
657 "Version string '%s' contains invalid data; "
658 "ignoring: '%s'", version, s);
666 Validates that the SV contains valid internal structure for a version object.
667 It may be passed either the version object (RV) or the hash itself (HV). If
668 the structure is valid, it returns the HV. If the structure is invalid,
671 SV *hv = vverify(sv);
673 Note that it only confirms the bare minimum structure (so as not to get
674 confused by derived classes which may contain additional hash entries):
678 =item * The SV is an HV or a reference to an HV
680 =item * The hash contains a "version" key
682 =item * The "version" key has a reference to an AV as its value
690 #if VUTIL_REPLACE_CORE
691 Perl_vverify2(pTHX_ SV *vs)
693 Perl_vverify(pTHX_ SV *vs)
698 PERL_ARGS_ASSERT_VVERIFY;
703 /* see if the appropriate elements exist */
704 if ( SvTYPE(vs) == SVt_PVHV
705 && hv_exists(MUTABLE_HV(vs), "version", 7)
706 && (sv = SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE)))
707 && SvTYPE(sv) == SVt_PVAV )
716 Accepts a version object and returns the normalized floating
717 point representation. Call like:
721 NOTE: you can pass either the object directly or the SV
722 contained within the RV.
724 The SV returned has a refcount of 1.
730 #if VUTIL_REPLACE_CORE
731 Perl_vnumify2(pTHX_ SV *vs)
733 Perl_vnumify(pTHX_ SV *vs)
743 PERL_ARGS_ASSERT_VNUMIFY;
745 /* extract the HV from the object */
748 Perl_croak(aTHX_ "Invalid version object");
750 /* see if various flags exist */
751 if ( hv_exists(MUTABLE_HV(vs), "alpha", 5 ) )
753 if ( hv_exists(MUTABLE_HV(vs), "width", 5 ) )
754 width = SvIV(*hv_fetchs(MUTABLE_HV(vs), "width", FALSE));
759 /* attempt to retrieve the version array */
760 if ( !(av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE))) ) ) {
761 return newSVpvs("0");
767 return newSVpvs("0");
770 digit = SvIV(*av_fetch(av, 0, 0));
771 sv = Perl_newSVpvf(aTHX_ "%d.", (int)PERL_ABS(digit));
772 for ( i = 1 ; i < len ; i++ )
774 digit = SvIV(*av_fetch(av, i, 0));
776 const int denom = (width == 2 ? 10 : 100);
777 const div_t term = div((int)PERL_ABS(digit),denom);
778 Perl_sv_catpvf(aTHX_ sv, "%0*d_%d", width, term.quot, term.rem);
781 Perl_sv_catpvf(aTHX_ sv, "%0*d", width, (int)digit);
787 digit = SvIV(*av_fetch(av, len, 0));
788 if ( alpha && width == 3 ) /* alpha version */
790 Perl_sv_catpvf(aTHX_ sv, "%0*d", width, (int)digit);
794 sv_catpvs(sv, "000");
802 Accepts a version object and returns the normalized string
803 representation. Call like:
807 NOTE: you can pass either the object directly or the SV
808 contained within the RV.
810 The SV returned has a refcount of 1.
816 #if VUTIL_REPLACE_CORE
817 Perl_vnormal2(pTHX_ SV *vs)
819 Perl_vnormal(pTHX_ SV *vs)
827 PERL_ARGS_ASSERT_VNORMAL;
829 /* extract the HV from the object */
832 Perl_croak(aTHX_ "Invalid version object");
834 if ( hv_exists(MUTABLE_HV(vs), "alpha", 5 ) )
836 av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE)));
843 digit = SvIV(*av_fetch(av, 0, 0));
844 sv = Perl_newSVpvf(aTHX_ "v%"IVdf, (IV)digit);
845 for ( i = 1 ; i < len ; i++ ) {
846 digit = SvIV(*av_fetch(av, i, 0));
847 Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit);
852 /* handle last digit specially */
853 digit = SvIV(*av_fetch(av, len, 0));
855 Perl_sv_catpvf(aTHX_ sv, "_%"IVdf, (IV)digit);
857 Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit);
860 if ( len <= 2 ) { /* short version, must be at least three */
861 for ( len = 2 - len; len != 0; len-- )
868 =for apidoc vstringify
870 In order to maintain maximum compatibility with earlier versions
871 of Perl, this function will return either the floating point
872 notation or the multiple dotted notation, depending on whether
873 the original version contained 1 or more dots, respectively.
875 The SV returned has a refcount of 1.
881 #if VUTIL_REPLACE_CORE
882 Perl_vstringify2(pTHX_ SV *vs)
884 Perl_vstringify(pTHX_ SV *vs)
887 PERL_ARGS_ASSERT_VSTRINGIFY;
889 /* extract the HV from the object */
892 Perl_croak(aTHX_ "Invalid version object");
894 if (hv_exists(MUTABLE_HV(vs), "original", sizeof("original") - 1)) {
896 pv = *hv_fetchs(MUTABLE_HV(vs), "original", FALSE);
903 if ( hv_exists(MUTABLE_HV(vs), "qv", 2) )
913 Version object aware cmp. Both operands must already have been
914 converted into version objects.
920 #if VUTIL_REPLACE_CORE
921 Perl_vcmp2(pTHX_ SV *lhv, SV *rhv)
923 Perl_vcmp(pTHX_ SV *lhv, SV *rhv)
934 PERL_ARGS_ASSERT_VCMP;
936 /* extract the HVs from the objects */
939 if ( ! ( lhv && rhv ) )
940 Perl_croak(aTHX_ "Invalid version object");
942 /* get the left hand term */
943 lav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(lhv), "version", FALSE)));
944 if ( hv_exists(MUTABLE_HV(lhv), "alpha", 5 ) )
947 /* and the right hand term */
948 rav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(rhv), "version", FALSE)));
949 if ( hv_exists(MUTABLE_HV(rhv), "alpha", 5 ) )
957 while ( i <= m && retval == 0 )
959 left = SvIV(*av_fetch(lav,i,0));
960 right = SvIV(*av_fetch(rav,i,0));
968 /* tiebreaker for alpha with identical terms */
969 if ( retval == 0 && l == r && left == right && ( lalpha || ralpha ) )
971 if ( lalpha && !ralpha )
975 else if ( ralpha && !lalpha)
981 if ( l != r && retval == 0 ) /* possible match except for trailing 0's */
985 while ( i <= r && retval == 0 )
987 if ( SvIV(*av_fetch(rav,i,0)) != 0 )
988 retval = -1; /* not a match after all */
994 while ( i <= l && retval == 0 )
996 if ( SvIV(*av_fetch(lav,i,0)) != 0 )
997 retval = +1; /* not a match after all */