1 /* This file is part of the "version" CPAN distribution. Please avoid
2 editing it in the perl core. */
5 # define PERL_NO_GET_CONTEXT
9 # define NEED_my_snprintf
10 # define NEED_newRV_noinc
11 # define NEED_vnewSVpvf
12 # define NEED_newSVpvn_flags_GLOBAL
18 #define VERSION_MAX 0x7FFFFFFF
21 =for apidoc prescan_version
23 Validate that a given string can be parsed as a version object, but doesn't
24 actually perform the parsing. Can use either strict or lax validation rules.
25 Can optionally set a number of hint variables to save the parsing code
26 some time when tokenizing.
31 #ifdef VUTIL_REPLACE_CORE
32 Perl_prescan_version2(pTHX_ const char *s, bool strict,
34 Perl_prescan_version(pTHX_ const char *s, bool strict,
37 bool *sqv, int *ssaw_decimal, int *swidth, bool *salpha) {
38 bool qv = (sqv ? *sqv : FALSE);
44 PERL_ARGS_ASSERT_PRESCAN_VERSION;
46 if (qv && isDIGIT(*d))
47 goto dotted_decimal_version;
49 if (*d == 'v') { /* explicit v-string */
54 else { /* degenerate v-string */
56 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
59 dotted_decimal_version:
60 if (strict && d[0] == '0' && isDIGIT(d[1])) {
61 /* no leading zeros allowed */
62 BADVERSION(s,errstr,"Invalid version format (no leading zeros)");
65 while (isDIGIT(*d)) /* integer part */
71 d++; /* decimal point */
77 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
80 goto version_prescan_finish;
87 while (isDIGIT(*d)) { /* just keep reading */
91 /* maximum 3 digits between decimal */
92 if (strict && j > 3) {
93 BADVERSION(s,errstr,"Invalid version format (maximum 3 digits between decimals)");
98 BADVERSION(s,errstr,"Invalid version format (no underscores)");
101 BADVERSION(s,errstr,"Invalid version format (multiple underscores)");
106 else if (*d == '.') {
108 BADVERSION(s,errstr,"Invalid version format (underscores before decimal)");
113 else if (!isDIGIT(*d)) {
119 if (strict && i < 2) {
120 /* requires v1.2.3 */
121 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
124 } /* end if dotted-decimal */
126 { /* decimal versions */
127 int j = 0; /* may need this later */
128 /* special strict case for leading '.' or '0' */
131 BADVERSION(s,errstr,"Invalid version format (0 before decimal required)");
133 if (*d == '0' && isDIGIT(d[1])) {
134 BADVERSION(s,errstr,"Invalid version format (no leading zeros)");
138 /* and we never support negative versions */
140 BADVERSION(s,errstr,"Invalid version format (negative version number)");
143 /* consume all of the integer part */
147 /* look for a fractional part */
149 /* we found it, so consume it */
153 else if (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') {
156 BADVERSION(s,errstr,"Invalid version format (version required)");
158 /* found just an integer */
159 goto version_prescan_finish;
162 /* didn't find either integer or period */
163 BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
165 else if (*d == '_') {
166 /* underscore can't come after integer part */
168 BADVERSION(s,errstr,"Invalid version format (no underscores)");
170 else if (isDIGIT(d[1])) {
171 BADVERSION(s,errstr,"Invalid version format (alpha without decimal)");
174 BADVERSION(s,errstr,"Invalid version format (misplaced underscore)");
178 /* anything else after integer part is just invalid data */
179 BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
182 /* scan the fractional part after the decimal point*/
184 if (!isDIGIT(*d) && (strict || ! (!*d || *d == ';' || isSPACE(*d) || *d == '{' || *d == '}') )) {
185 /* strict or lax-but-not-the-end */
186 BADVERSION(s,errstr,"Invalid version format (fractional part required)");
189 while (isDIGIT(*d)) {
191 if (*d == '.' && isDIGIT(d[-1])) {
193 BADVERSION(s,errstr,"Invalid version format (underscores before decimal)");
196 BADVERSION(s,errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
198 d = (char *)s; /* start all over again */
200 goto dotted_decimal_version;
204 BADVERSION(s,errstr,"Invalid version format (no underscores)");
207 BADVERSION(s,errstr,"Invalid version format (multiple underscores)");
209 if ( ! isDIGIT(d[1]) ) {
210 BADVERSION(s,errstr,"Invalid version format (misplaced underscore)");
219 version_prescan_finish:
223 if (!isDIGIT(*d) && (! (!*d || *d == ';' || *d == '{' || *d == '}') )) {
224 /* trailing non-numeric data */
225 BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
233 *ssaw_decimal = saw_decimal;
240 =for apidoc scan_version
242 Returns a pointer to the next character after the parsed
243 version string, as well as upgrading the passed in SV to
246 Function must be called with an already existing SV like
249 s = scan_version(s, SV *sv, bool qv);
251 Performs some preprocessing to the string to ensure that
252 it has the correct characteristics of a version. Flags the
253 object if it contains an underscore (which denotes this
254 is an alpha version). The boolean qv denotes that the version
255 should be interpreted as if it had multiple decimals, even if
262 #ifdef VUTIL_REPLACE_CORE
263 Perl_scan_version2(pTHX_ const char *s, SV *rv, bool qv)
265 Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
268 const char *start = s;
271 const char *errstr = NULL;
279 PERL_ARGS_ASSERT_SCAN_VERSION;
281 while (isSPACE(*s)) /* leading whitespace is OK */
284 last = PRESCAN_VERSION(s, FALSE, &errstr, &qv, &saw_decimal, &width, &alpha);
286 /* "undef" is a special case and not an error */
287 if ( ! ( *s == 'u' && strEQ(s+1,"ndef")) ) {
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 = AvFILLp(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+1,"ndef") ) {
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 #ifdef 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_VERSION_OBJ(ver) ) /* 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 SV ** svp = hv_fetchs(MUTABLE_HV(ver), "width", FALSE);
489 const I32 width = SvIV(*svp);
490 (void)hv_stores(MUTABLE_HV(hv), "width", newSViv(width));
494 SV ** svp = hv_fetchs(MUTABLE_HV(ver), "original", FALSE);
496 (void)hv_stores(MUTABLE_HV(hv), "original", newSVsv(*svp));
498 sav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(ver), "version", FALSE)));
499 /* This will get reblessed later if a derived class*/
500 for ( key = 0; key <= av_len(sav); key++ )
502 SV * const sv = *av_fetch(sav, key, FALSE);
503 const I32 rev = SvIV(sv);
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 const char * const version = (const char*)mg->mg_ptr;
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);
523 SvSetSV_nosteal(rv, ver); /* make a duplicate */
528 sv_2mortal(rv); /* in case upg_version croaks before it returns */
529 return SvREFCNT_inc_NN(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 #ifdef 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 #if PERL_VERSION_LT(5,19,8) && defined(USE_ITHREADS)
560 PERL_ARGS_ASSERT_UPG_VERSION;
562 if ( (SvUOK(ver) && SvUVX(ver) > VERSION_MAX)
563 || (SvIOK(ver) && SvIVX(ver) > VERSION_MAX) ) {
564 /* out of bounds [unsigned] integer */
567 len = my_snprintf(tbuf, sizeof(tbuf), "%d", VERSION_MAX);
568 version = savepvn(tbuf, len);
570 Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
571 "Integer overflow in version %d",VERSION_MAX);
573 else if ( SvUOK(ver) || SvIOK(ver))
574 #if PERL_VERSION_LT(5,17,2)
578 version = savesvpv(ver);
581 else if (SvNOK(ver) && !( SvPOK(ver) && SvCUR(ver) == 3 ) )
582 #if PERL_VERSION_LT(5,17,2)
588 /* may get too much accuracy */
590 SV *sv = SvNVX(ver) > 10e50 ? newSV(64) : 0;
592 STORE_NUMERIC_LOCAL_SET_STANDARD();
594 Perl_sv_catpvf(aTHX_ sv, "%.9"NVff, SvNVX(ver));
599 len = my_snprintf(tbuf, sizeof(tbuf), "%.9"NVff, SvNVX(ver));
602 RESTORE_NUMERIC_LOCAL();
603 while (buf[len-1] == '0' && len > 0) len--;
604 if ( buf[len-1] == '.' ) len--; /* eat the trailing decimal */
605 version = savepvn(buf, len);
610 else if ( (mg = SvVSTRING_mg(ver)) ) { /* already a v-string */
611 version = savepvn( (const char*)mg->mg_ptr,mg->mg_len );
616 else if ( SvPOK(ver))/* must be a string or something like a string */
617 #if PERL_VERSION_LT(5,17,2)
622 version = savepvn(SvPV(ver,len), SvCUR(ver));
625 # if PERL_VERSION > 5
626 /* This will only be executed for 5.6.0 - 5.8.0 inclusive */
627 if ( len >= 3 && !instr(version,".") && !instr(version,"_")) {
628 /* may be a v-string */
629 char *testv = (char *)version;
631 for (tlen=0; tlen < len; tlen++, testv++) {
632 /* if one of the characters is non-text assume v-string */
633 if (testv[0] < ' ') {
634 SV * const nsv = sv_newmortal();
638 sv_setpvf(nsv,"v%vd",ver);
639 pos = nver = savepv(SvPV_nolen(nsv));
642 /* scan the resulting formatted string */
643 pos++; /* skip the leading 'v' */
644 while ( *pos == '.' || isDIGIT(*pos) ) {
650 /* is definitely a v-string */
651 if ( saw_decimal >= 2 ) {
661 #if PERL_VERSION_LT(5,17,2)
662 else if (SvIOKp(ver)) {
665 else if (SvNOKp(ver)) {
668 else if (SvPOKp(ver)) {
674 /* no idea what this is */
675 Perl_croak(aTHX_ "Invalid version format (non-numeric data)");
678 s = SCAN_VERSION(version, ver, qv);
680 Perl_ck_warner(aTHX_ packWARN(WARN_MISC),
681 "Version string '%s' contains invalid data; "
682 "ignoring: '%s'", version, s);
684 #if PERL_VERSION_LT(5,19,8) && defined(USE_ITHREADS)
694 Validates that the SV contains valid internal structure for a version object.
695 It may be passed either the version object (RV) or the hash itself (HV). If
696 the structure is valid, it returns the HV. If the structure is invalid,
699 SV *hv = vverify(sv);
701 Note that it only confirms the bare minimum structure (so as not to get
702 confused by derived classes which may contain additional hash entries):
706 =item * The SV is an HV or a reference to an HV
708 =item * The hash contains a "version" key
710 =item * The "version" key has a reference to an AV as its value
718 #ifdef VUTIL_REPLACE_CORE
719 Perl_vverify2(pTHX_ SV *vs)
721 Perl_vverify(pTHX_ SV *vs)
727 PERL_ARGS_ASSERT_VVERIFY;
732 /* see if the appropriate elements exist */
733 if ( SvTYPE(vs) == SVt_PVHV
734 && (svp = hv_fetchs(MUTABLE_HV(vs), "version", FALSE))
736 && SvTYPE(sv) == SVt_PVAV )
745 Accepts a version object and returns the normalized floating
746 point representation. Call like:
750 NOTE: you can pass either the object directly or the SV
751 contained within the RV.
753 The SV returned has a refcount of 1.
759 #ifdef VUTIL_REPLACE_CORE
760 Perl_vnumify2(pTHX_ SV *vs)
762 Perl_vnumify(pTHX_ SV *vs)
772 PERL_ARGS_ASSERT_VNUMIFY;
774 /* extract the HV from the object */
777 Perl_croak(aTHX_ "Invalid version object");
779 /* see if various flags exist */
780 if ( hv_exists(MUTABLE_HV(vs), "alpha", 5 ) )
783 SV ** svp = hv_fetchs(MUTABLE_HV(vs), "width", FALSE);
791 /* attempt to retrieve the version array */
792 if ( !(av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE))) ) ) {
793 return newSVpvs("0");
799 return newSVpvs("0");
803 SV * tsv = *av_fetch(av, 0, 0);
806 sv = Perl_newSVpvf(aTHX_ "%d.", (int)PERL_ABS(digit));
807 for ( i = 1 ; i < len ; i++ )
809 SV * tsv = *av_fetch(av, i, 0);
812 const int denom = (width == 2 ? 10 : 100);
813 const div_t term = div((int)PERL_ABS(digit),denom);
814 Perl_sv_catpvf(aTHX_ sv, "%0*d_%d", width, term.quot, term.rem);
817 Perl_sv_catpvf(aTHX_ sv, "%0*d", width, (int)digit);
823 SV * tsv = *av_fetch(av, len, 0);
825 if ( alpha && width == 3 ) /* alpha version */
827 Perl_sv_catpvf(aTHX_ sv, "%0*d", width, (int)digit);
831 sv_catpvs(sv, "000");
839 Accepts a version object and returns the normalized string
840 representation. Call like:
844 NOTE: you can pass either the object directly or the SV
845 contained within the RV.
847 The SV returned has a refcount of 1.
853 #ifdef VUTIL_REPLACE_CORE
854 Perl_vnormal2(pTHX_ SV *vs)
856 Perl_vnormal(pTHX_ SV *vs)
864 PERL_ARGS_ASSERT_VNORMAL;
866 /* extract the HV from the object */
869 Perl_croak(aTHX_ "Invalid version object");
871 if ( hv_exists(MUTABLE_HV(vs), "alpha", 5 ) )
873 av = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(vs), "version", FALSE)));
881 SV * tsv = *av_fetch(av, 0, 0);
884 sv = Perl_newSVpvf(aTHX_ "v%"IVdf, (IV)digit);
885 for ( i = 1 ; i < len ; i++ ) {
886 SV * tsv = *av_fetch(av, i, 0);
888 Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit);
893 /* handle last digit specially */
894 SV * tsv = *av_fetch(av, len, 0);
897 Perl_sv_catpvf(aTHX_ sv, "_%"IVdf, (IV)digit);
899 Perl_sv_catpvf(aTHX_ sv, ".%"IVdf, (IV)digit);
902 if ( len <= 2 ) { /* short version, must be at least three */
903 for ( len = 2 - len; len != 0; len-- )
910 =for apidoc vstringify
912 In order to maintain maximum compatibility with earlier versions
913 of Perl, this function will return either the floating point
914 notation or the multiple dotted notation, depending on whether
915 the original version contained 1 or more dots, respectively.
917 The SV returned has a refcount of 1.
923 #ifdef VUTIL_REPLACE_CORE
924 Perl_vstringify2(pTHX_ SV *vs)
926 Perl_vstringify(pTHX_ SV *vs)
930 PERL_ARGS_ASSERT_VSTRINGIFY;
932 /* extract the HV from the object */
935 Perl_croak(aTHX_ "Invalid version object");
937 svp = hv_fetchs(MUTABLE_HV(vs), "original", FALSE);
947 if ( hv_exists(MUTABLE_HV(vs), "qv", 2) )
957 Version object aware cmp. Both operands must already have been
958 converted into version objects.
964 #ifdef VUTIL_REPLACE_CORE
965 Perl_vcmp2(pTHX_ SV *lhv, SV *rhv)
967 Perl_vcmp(pTHX_ SV *lhv, SV *rhv)
978 PERL_ARGS_ASSERT_VCMP;
980 /* extract the HVs from the objects */
983 if ( ! ( lhv && rhv ) )
984 Perl_croak(aTHX_ "Invalid version object");
986 /* get the left hand term */
987 lav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(lhv), "version", FALSE)));
988 if ( hv_exists(MUTABLE_HV(lhv), "alpha", 5 ) )
991 /* and the right hand term */
992 rav = MUTABLE_AV(SvRV(*hv_fetchs(MUTABLE_HV(rhv), "version", FALSE)));
993 if ( hv_exists(MUTABLE_HV(rhv), "alpha", 5 ) )
1001 while ( i <= m && retval == 0 )
1003 SV * const lsv = *av_fetch(lav,i,0);
1006 rsv = *av_fetch(rav,i,0);
1015 /* tiebreaker for alpha with identical terms */
1016 if ( retval == 0 && l == r && left == right && ( lalpha || ralpha ) )
1018 if ( lalpha && !ralpha )
1022 else if ( ralpha && !lalpha)
1028 if ( l != r && retval == 0 ) /* possible match except for trailing 0's */
1032 while ( i <= r && retval == 0 )
1034 SV * const rsv = *av_fetch(rav,i,0);
1035 if ( SvIV(rsv) != 0 )
1036 retval = -1; /* not a match after all */
1042 while ( i <= l && retval == 0 )
1044 SV * const lsv = *av_fetch(lav,i,0);
1045 if ( SvIV(lsv) != 0 )
1046 retval = +1; /* not a match after all */