case OP_REPEAT:
if (o->op_flags & OPf_STACKED)
break;
+ goto func_ops;
+ case OP_SUBSTR:
+ if (o->op_private == 4)
+ break;
/* FALL THROUGH */
case OP_GVSV:
case OP_WANTARRAY:
case OP_HEX:
case OP_OCT:
case OP_LENGTH:
- case OP_SUBSTR:
case OP_VEC:
case OP_INDEX:
case OP_RINDEX:
case OP_GGRNAM:
case OP_GGRGID:
case OP_GETLOGIN:
+ func_ops:
if (!(o->op_private & OPpLVAL_INTRO))
useless = op_desc[o->op_type];
break;
case OP_KEYS:
if (type != OP_SASSIGN)
goto nomod;
+ goto lvalue_func;
+ case OP_SUBSTR:
+ if (o->op_private == 4) /* don't allow 4 arg substr as lvalue */
+ goto nomod;
/* FALL THROUGH */
case OP_POS:
case OP_VEC:
- case OP_SUBSTR:
+ lvalue_func:
pad_free(o->op_targ);
o->op_targ = pad_alloc(o->op_type, SVs_PADMY);
assert(SvTYPE(PAD_SV(o->op_targ)) == SVt_NULL);
An alternative to using substr() as an lvalue is to specify the
replacement string as the 4th argument. This allows you to replace
parts of the EXPR and return what was there before in one operation.
-In this case LEN can be C<undef> if you want to affect everything to
-the end of the string.
=item symlink OLDFILE,NEWFILE
djSP; dTARGET;
SV *sv;
I32 len;
- I32 len_ok = 0;
STRLEN curlen;
I32 pos;
I32 rem;
STRLEN repl_len;
SvTAINTED_off(TARG); /* decontaminate */
- if (MAXARG > 3) {
- /* pop off replacement string */
- sv = POPs;
- repl = SvPV(sv, repl_len);
- /* pop off length */
- sv = POPs;
- if (SvOK(sv)) {
- len = SvIV(sv);
- len_ok++;
+ if (MAXARG > 2) {
+ if (MAXARG > 3) {
+ sv = POPs;
+ repl = SvPV(sv, repl_len);
}
- } else if (MAXARG == 3) {
len = POPi;
- len_ok++;
- }
-
+ }
pos = POPi;
sv = POPs;
PUTBACK;
pos -= arybase;
rem = curlen-pos;
fail = rem;
- if (len_ok) {
- if (len < 0) {
- rem += len;
- if (rem < 0)
- rem = 0;
- }
- else if (rem > len)
- rem = len;
- }
+ if (MAXARG > 2) {
+ if (len < 0) {
+ rem += len;
+ if (rem < 0)
+ rem = 0;
+ }
+ else if (rem > len)
+ rem = len;
+ }
}
else {
- pos += curlen;
- if (!len_ok)
- rem = curlen;
- else if (len >= 0) {
- rem = pos+len;
- if (rem > (I32)curlen)
- rem = curlen;
- }
- else {
- rem = curlen+len;
- if (rem < pos)
- rem = pos;
- }
- if (pos < 0)
- pos = 0;
- fail = rem;
- rem -= pos;
+ pos += curlen;
+ if (MAXARG < 3)
+ rem = curlen;
+ else if (len >= 0) {
+ rem = pos+len;
+ if (rem > (I32)curlen)
+ rem = curlen;
+ }
+ else {
+ rem = curlen+len;
+ if (rem < pos)
+ rem = pos;
+ }
+ if (pos < 0)
+ pos = 0;
+ fail = rem;
+ rem -= pos;
}
if (fail < 0) {
if (dowarn || lvalue || repl)
LvTARGOFF(TARG) = pos;
LvTARGLEN(TARG) = rem;
}
- else if (repl)
+ else if (repl)
sv_insert(sv, pos, rem, repl, repl_len);
}
SPAGAIN;
#!./perl
-print "1..100\n";
+print "1..106\n";
#P = start of string Q = start of substr R = end of substr S = end of string
$w++;
} elsif ($_[0] =~ /^Attempt to use reference as lvalue in substr/) {
$w += 2;
+ } elsif ($_[0] =~ /^Use of uninitialized value/) {
+ $w += 3;
} else {
- warn @_;
+ warn $_[0];
}
};
# check no spurious warnings
print $w ? "not ok 97\n" : "ok 97\n";
-# check new replacement syntax
+# check new 4 arg replacement syntax
$a = "abcxyz";
+$w = 0;
print "not " unless substr($a, 0, 3, "") eq "abc" && $a eq "xyz";
print "ok 98\n";
print "not " unless substr($a, 0, 0, "abc") eq "" && $a eq "abcxyz";
print "ok 99\n";
-print "not " unless substr($a, 3, undef, "") eq "xyz" && $a eq "abc";
+print "not " unless substr($a, 3, -1, "") eq "xy" && $a eq "abcz";
print "ok 100\n";
+print "not " unless substr($a, 3, undef, "xy") eq "" && $a eq "abcxyz"
+ && $w == 3;
+print "ok 101\n";
+$w = 0;
+
+print "not " unless substr($a, 3, 9999999, "") eq "xyz" && $a eq "abc";
+print "ok 102\n";
+print "not " unless fail(substr($a, -99, 0, ""));
+print "ok 103\n";
+print "not " unless fail(substr($a, 99, 3, ""));
+print "ok 104\n";
+
+substr($a, 0, length($a), "foo");
+print "not " unless $a eq "foo" && !$w;
+print "ok 105\n";
+
+# using 4 arg substr as lvalue is a compile time error
+eval 'substr($a,0,0,"") = "abc"';
+print "not " unless $@ && $@ =~ /Can't modify substr/ && $a eq "foo";
+print "ok 106\n";