summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarkko Hietaniemi <jhi@iki.fi>2002-03-20 13:59:58 +0000
committerJarkko Hietaniemi <jhi@iki.fi>2002-03-20 13:59:58 +0000
commit33b8afdf5cd7f66238db46e095c6effe7bebe9ee (patch)
tree4708cd5f7fcc68c5cc6584c05a825f6c168c901e
parent6d5328bc15982a12e4db34e42922fa0ff551ed7c (diff)
downloadperl-33b8afdf5cd7f66238db46e095c6effe7bebe9ee.tar.gz
Fix for "UTF-8 bug with s///" from Hugo.
p4raw-id: //depot/perl@15356
-rw-r--r--embed.fnc2
-rw-r--r--embed.h4
-rw-r--r--proto.h2
-rw-r--r--regcomp.c76
-rw-r--r--regcomp.h6
-rw-r--r--regexec.c308
-rw-r--r--sv.c1
-rwxr-xr-xt/op/pat.t43
8 files changed, 327 insertions, 115 deletions
diff --git a/embed.fnc b/embed.fnc
index 84d89d02d2..8b7727b6e7 100644
--- a/embed.fnc
+++ b/embed.fnc
@@ -1167,6 +1167,8 @@ s |U8* |reghop3 |U8 *pos|I32 off|U8 *lim
s |U8* |reghopmaybe |U8 *pos|I32 off
s |U8* |reghopmaybe3 |U8 *pos|I32 off|U8 *lim
s |char* |find_byclass |regexp * prog|regnode *c|char *s|char *strend|char *startpos|I32 norun
+s |void |to_utf8_substr |regexp * prog
+s |void |to_byte_substr |regexp * prog
#endif
#if defined(PERL_IN_DUMP_C) || defined(PERL_DECL_PROT)
diff --git a/embed.h b/embed.h
index 787a045d1c..10c6a534c0 100644
--- a/embed.h
+++ b/embed.h
@@ -1088,6 +1088,8 @@
#define reghopmaybe S_reghopmaybe
#define reghopmaybe3 S_reghopmaybe3
#define find_byclass S_find_byclass
+#define to_utf8_substr S_to_utf8_substr
+#define to_byte_substr S_to_byte_substr
#endif
#if defined(PERL_IN_DUMP_C) || defined(PERL_DECL_PROT)
#define deb_curcv S_deb_curcv
@@ -2639,6 +2641,8 @@
#define reghopmaybe(a,b) S_reghopmaybe(aTHX_ a,b)
#define reghopmaybe3(a,b,c) S_reghopmaybe3(aTHX_ a,b,c)
#define find_byclass(a,b,c,d,e,f) S_find_byclass(aTHX_ a,b,c,d,e,f)
+#define to_utf8_substr(a) S_to_utf8_substr(aTHX_ a)
+#define to_byte_substr(a) S_to_byte_substr(aTHX_ a)
#endif
#if defined(PERL_IN_DUMP_C) || defined(PERL_DECL_PROT)
#define deb_curcv(a) S_deb_curcv(aTHX_ a)
diff --git a/proto.h b/proto.h
index dad9b57299..ac6f2815cd 100644
--- a/proto.h
+++ b/proto.h
@@ -1206,6 +1206,8 @@ STATIC U8* S_reghop3(pTHX_ U8 *pos, I32 off, U8 *lim);
STATIC U8* S_reghopmaybe(pTHX_ U8 *pos, I32 off);
STATIC U8* S_reghopmaybe3(pTHX_ U8 *pos, I32 off, U8 *lim);
STATIC char* S_find_byclass(pTHX_ regexp * prog, regnode *c, char *s, char *strend, char *startpos, I32 norun);
+STATIC void S_to_utf8_substr(pTHX_ regexp * prog);
+STATIC void S_to_byte_substr(pTHX_ regexp * prog);
#endif
#if defined(PERL_IN_DUMP_C) || defined(PERL_DECL_PROT)
diff --git a/regcomp.c b/regcomp.c
index 7c34d8f949..639f140582 100644
--- a/regcomp.c
+++ b/regcomp.c
@@ -931,6 +931,8 @@ S_study_chunk(pTHX_ RExC_state_t *pRExC_state, regnode **scanp, I32 *deltap, reg
? I32_MAX : data->pos_min + data->pos_delta;
}
sv_catpvn(data->last_found, STRING(scan), STR_LEN(scan));
+ if (UTF)
+ SvUTF8_on(data->last_found);
data->last_end = data->pos_min + l;
data->pos_min += l; /* As in the first entry. */
data->flags &= ~SF_BEFORE_EOL;
@@ -1963,17 +1965,23 @@ Perl_pregcomp(pTHX_ char *exp, char *xend, PMOP *pm)
&& SvCUR(data.longest_fixed) == SvCUR(data.longest_float))
goto remove_float; /* As in (a)+. */
- r->float_substr = data.longest_float;
+ if (SvUTF8(data.longest_float)) {
+ r->float_utf8 = data.longest_float;
+ r->float_substr = Nullsv;
+ } else {
+ r->float_substr = data.longest_float;
+ r->float_utf8 = Nullsv;
+ }
r->float_min_offset = data.offset_float_min;
r->float_max_offset = data.offset_float_max;
t = (data.flags & SF_FL_BEFORE_EOL /* Can't have SEOL and MULTI */
&& (!(data.flags & SF_FL_BEFORE_MEOL)
|| (RExC_flags16 & PMf_MULTILINE)));
- fbm_compile(r->float_substr, t ? FBMcf_TAIL : 0);
+ fbm_compile(data.longest_float, t ? FBMcf_TAIL : 0);
}
else {
remove_float:
- r->float_substr = Nullsv;
+ r->float_substr = r->float_utf8 = Nullsv;
SvREFCNT_dec(data.longest_float);
longest_float_length = 0;
}
@@ -1985,22 +1993,29 @@ Perl_pregcomp(pTHX_ char *exp, char *xend, PMOP *pm)
|| (RExC_flags16 & PMf_MULTILINE)))) {
int t;
- r->anchored_substr = data.longest_fixed;
+ if (SvUTF8(data.longest_fixed)) {
+ r->anchored_utf8 = data.longest_fixed;
+ r->anchored_substr = Nullsv;
+ } else {
+ r->anchored_substr = data.longest_fixed;
+ r->anchored_utf8 = Nullsv;
+ }
r->anchored_offset = data.offset_fixed;
t = (data.flags & SF_FIX_BEFORE_EOL /* Can't have SEOL and MULTI */
&& (!(data.flags & SF_FIX_BEFORE_MEOL)
|| (RExC_flags16 & PMf_MULTILINE)));
- fbm_compile(r->anchored_substr, t ? FBMcf_TAIL : 0);
+ fbm_compile(data.longest_fixed, t ? FBMcf_TAIL : 0);
}
else {
- r->anchored_substr = Nullsv;
+ r->anchored_substr = r->anchored_utf8 = Nullsv;
SvREFCNT_dec(data.longest_fixed);
longest_fixed_length = 0;
}
if (r->regstclass
&& (OP(r->regstclass) == REG_ANY || OP(r->regstclass) == SANY))
r->regstclass = NULL;
- if ((!r->anchored_substr || r->anchored_offset) && stclass_flag
+ if ((!(r->anchored_substr || r->anchored_utf8) || r->anchored_offset)
+ && stclass_flag
&& !(data.start_class->flags & ANYOF_EOS)
&& !cl_is_anything(data.start_class)) {
I32 n = add_data(pRExC_state, 1, "f");
@@ -2023,20 +2038,22 @@ Perl_pregcomp(pTHX_ char *exp, char *xend, PMOP *pm)
/* A temporary algorithm prefers floated substr to fixed one to dig more info. */
if (longest_fixed_length > longest_float_length) {
r->check_substr = r->anchored_substr;
+ r->check_utf8 = r->anchored_utf8;
r->check_offset_min = r->check_offset_max = r->anchored_offset;
if (r->reganch & ROPT_ANCH_SINGLE)
r->reganch |= ROPT_NOSCAN;
}
else {
r->check_substr = r->float_substr;
+ r->check_utf8 = r->float_utf8;
r->check_offset_min = data.offset_float_min;
r->check_offset_max = data.offset_float_max;
}
/* XXXX Currently intuiting is not compatible with ANCH_GPOS.
This should be changed ASAP! */
- if (r->check_substr && !(r->reganch & ROPT_ANCH_GPOS)) {
+ if ((r->check_substr || r->check_utf8) && !(r->reganch & ROPT_ANCH_GPOS)) {
r->reganch |= RE_USE_INTUIT;
- if (SvTAIL(r->check_substr))
+ if (SvTAIL(r->check_substr ? r->check_substr : r->check_utf8))
r->reganch |= RE_INTUIT_TAIL;
}
}
@@ -2052,7 +2069,8 @@ Perl_pregcomp(pTHX_ char *exp, char *xend, PMOP *pm)
data.start_class = &ch_class;
data.last_closep = &last_close;
minlen = study_chunk(pRExC_state, &scan, &fake, scan + RExC_size, &data, SCF_DO_STCLASS_AND|SCF_WHILEM_VISITED_POS);
- r->check_substr = r->anchored_substr = r->float_substr = Nullsv;
+ r->check_substr = r->check_utf8 = r->anchored_substr = r->anchored_utf8
+ = r->float_substr = r->float_utf8 = Nullsv;
if (!(data.start_class->flags & ANYOF_EOS)
&& !cl_is_anything(data.start_class)) {
I32 n = add_data(pRExC_state, 1, "f");
@@ -4529,6 +4547,15 @@ Perl_regdump(pTHX_ regexp *r)
PL_colors[1],
SvTAIL(r->anchored_substr) ? "$" : "",
(IV)r->anchored_offset);
+ else if (r->anchored_utf8)
+ PerlIO_printf(Perl_debug_log,
+ "anchored utf8 `%s%.*s%s'%s at %"IVdf" ",
+ PL_colors[0],
+ (int)(SvCUR(r->anchored_utf8) - (SvTAIL(r->anchored_utf8)!=0)),
+ SvPVX(r->anchored_utf8),
+ PL_colors[1],
+ SvTAIL(r->anchored_utf8) ? "$" : "",
+ (IV)r->anchored_offset);
if (r->float_substr)
PerlIO_printf(Perl_debug_log,
"floating `%s%.*s%s'%s at %"IVdf"..%"UVuf" ",
@@ -4538,15 +4565,25 @@ Perl_regdump(pTHX_ regexp *r)
PL_colors[1],
SvTAIL(r->float_substr) ? "$" : "",
(IV)r->float_min_offset, (UV)r->float_max_offset);
- if (r->check_substr)
+ else if (r->float_utf8)
+ PerlIO_printf(Perl_debug_log,
+ "floating utf8 `%s%.*s%s'%s at %"IVdf"..%"UVuf" ",
+ PL_colors[0],
+ (int)(SvCUR(r->float_utf8) - (SvTAIL(r->float_utf8)!=0)),
+ SvPVX(r->float_utf8),
+ PL_colors[1],
+ SvTAIL(r->float_utf8) ? "$" : "",
+ (IV)r->float_min_offset, (UV)r->float_max_offset);
+ if (r->check_substr || r->check_utf8)
PerlIO_printf(Perl_debug_log,
r->check_substr == r->float_substr
+ && r->check_utf8 == r->float_utf8
? "(checking floating" : "(checking anchored");
if (r->reganch & ROPT_NOSCAN)
PerlIO_printf(Perl_debug_log, " noscan");
if (r->reganch & ROPT_CHECK_ALL)
PerlIO_printf(Perl_debug_log, " isall");
- if (r->check_substr)
+ if (r->check_substr || r->check_utf8)
PerlIO_printf(Perl_debug_log, ") ");
if (r->regstclass) {
@@ -4795,18 +4832,21 @@ Perl_re_intuit_string(pTHX_ regexp *prog)
{ /* Assume that RE_INTUIT is set */
DEBUG_r(
{ STRLEN n_a;
- char *s = SvPV(prog->check_substr,n_a);
+ char *s = SvPV(prog->check_substr
+ ? prog->check_substr : prog->check_utf8, n_a);
if (!PL_colorset) reginitcolors();
PerlIO_printf(Perl_debug_log,
- "%sUsing REx substr:%s `%s%.60s%s%s'\n",
- PL_colors[4],PL_colors[5],PL_colors[0],
+ "%sUsing REx %ssubstr:%s `%s%.60s%s%s'\n",
+ PL_colors[4],
+ prog->check_substr ? "" : "utf8 ",
+ PL_colors[5],PL_colors[0],
s,
PL_colors[1],
(strlen(s) > 60 ? "..." : ""));
} );
- return prog->check_substr;
+ return prog->check_substr ? prog->check_substr : prog->check_utf8;
}
void
@@ -4841,8 +4881,12 @@ Perl_pregfree(pTHX_ struct regexp *r)
if (r->substrs) {
if (r->anchored_substr)
SvREFCNT_dec(r->anchored_substr);
+ if (r->anchored_utf8)
+ SvREFCNT_dec(r->anchored_utf8);
if (r->float_substr)
SvREFCNT_dec(r->float_substr);
+ if (r->float_utf8)
+ SvREFCNT_dec(r->float_utf8);
Safefree(r->substrs);
}
if (r->data) {
diff --git a/regcomp.h b/regcomp.h
index bfd00dcc7b..8c027bfbb8 100644
--- a/regcomp.h
+++ b/regcomp.h
@@ -386,7 +386,8 @@ struct reg_data {
struct reg_substr_datum {
I32 min_offset;
I32 max_offset;
- SV *substr;
+ SV *substr; /* non-utf8 variant */
+ SV *utf8_substr; /* utf8 variant */
};
struct reg_substr_data {
@@ -394,10 +395,13 @@ struct reg_substr_data {
};
#define anchored_substr substrs->data[0].substr
+#define anchored_utf8 substrs->data[0].utf8_substr
#define anchored_offset substrs->data[0].min_offset
#define float_substr substrs->data[1].substr
+#define float_utf8 substrs->data[1].utf8_substr
#define float_min_offset substrs->data[1].min_offset
#define float_max_offset substrs->data[1].max_offset
#define check_substr substrs->data[2].substr
+#define check_utf8 substrs->data[2].utf8_substr
#define check_offset_min substrs->data[2].min_offset
#define check_offset_max substrs->data[2].max_offset
diff --git a/regexec.c b/regexec.c
index e676568bdd..5a6d72db8c 100644
--- a/regexec.c
+++ b/regexec.c
@@ -102,7 +102,7 @@
* Forwards.
*/
-#define CHR_SVLEN(sv) (UTF ? sv_len_utf8(sv) : SvCUR(sv))
+#define CHR_SVLEN(sv) (do_utf8 ? sv_len_utf8(sv) : SvCUR(sv))
#define CHR_DIST(a,b) (PL_reg_match_utf8 ? utf8_distance(a,b) : a - b)
#define reghop_c(pos,off) ((char*)reghop((U8*)pos, off))
@@ -392,6 +392,7 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
register SV *check;
char *strbeg;
char *t;
+ int do_utf8 = sv ? SvUTF8(sv) : 0; /* if no sv we have to assume bytes */
I32 ml_anch;
register char *other_last = Nullch; /* other substr checked before this */
char *check_at = Nullch; /* check substr found at this pos */
@@ -437,7 +438,20 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
}
strbeg = (sv && SvPOK(sv)) ? strend - SvCUR(sv) : strpos;
PL_regeol = strend;
- check = prog->check_substr;
+ if (do_utf8) {
+ if (!prog->check_utf8 && prog->check_substr)
+ to_utf8_substr(prog);
+ check = prog->check_utf8;
+ } else {
+ if (!prog->check_substr && prog->check_utf8)
+ to_byte_substr(prog);
+ check = prog->check_substr;
+ }
+ if (check == &PL_sv_undef) {
+ DEBUG_r(PerlIO_printf(Perl_debug_log,
+ "Non-utf string cannot match utf check string\n"));
+ goto fail;
+ }
if (prog->reganch & ROPT_ANCH) { /* Match at beg-of-str or after \n */
ml_anch = !( (prog->reganch & ROPT_ANCH_SINGLE)
|| ( (prog->reganch & ROPT_ANCH_BOL)
@@ -543,7 +557,7 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
DEBUG_r(PerlIO_printf(Perl_debug_log, "%s %s substr `%s%.*s%s'%s%s",
(s ? "Found" : "Did not find"),
- ((check == prog->anchored_substr) ? "anchored" : "floating"),
+ (check == (do_utf8 ? prog->anchored_utf8 : prog->anchored_substr) ? "anchored" : "floating"),
PL_colors[0],
(int)(SvCUR(check) - (SvTAIL(check)!=0)),
SvPVX(check),
@@ -566,16 +580,17 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
Probably it is right to do no SCREAM here...
*/
- if (prog->float_substr && prog->anchored_substr) {
+ if (do_utf8 ? (prog->float_utf8 && prog->anchored_utf8) : (prog->float_substr && prog->anchored_substr)) {
/* Take into account the "other" substring. */
/* XXXX May be hopelessly wrong for UTF... */
if (!other_last)
other_last = strpos;
- if (check == prog->float_substr) {
+ if (check == (do_utf8 ? prog->float_utf8 : prog->float_substr)) {
do_other_anchored:
{
char *last = HOP3c(s, -start_shift, strbeg), *last1, *last2;
char *s1 = s;
+ SV* must;
t = s - prog->check_offset_max;
if (s - strpos > prog->check_offset_max /* signed-corrected t > strpos */
@@ -593,20 +608,27 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
last1 = last;
/* XXXX It is not documented what units *_offsets are in. Assume bytes. */
/* On end-of-str: see comment below. */
- s = fbm_instr((unsigned char*)t,
- HOP3(HOP3(last1, prog->anchored_offset, strend)
- + SvCUR(prog->anchored_substr),
- -(SvTAIL(prog->anchored_substr)!=0), strbeg),
- prog->anchored_substr,
- PL_multiline ? FBMrf_MULTILINE : 0);
+ must = do_utf8 ? prog->anchored_utf8 : prog->anchored_substr;
+ if (must == &PL_sv_undef) {
+ s = (char*)NULL;
+ DEBUG_r(must = prog->anchored_utf8); /* for debug */
+ }
+ else
+ s = fbm_instr(
+ (unsigned char*)t,
+ HOP3(HOP3(last1, prog->anchored_offset, strend)
+ + SvCUR(must), -(SvTAIL(must)!=0), strbeg),
+ must,
+ PL_multiline ? FBMrf_MULTILINE : 0
+ );
DEBUG_r(PerlIO_printf(Perl_debug_log,
"%s anchored substr `%s%.*s%s'%s",
(s ? "Found" : "Contradicts"),
PL_colors[0],
- (int)(SvCUR(prog->anchored_substr)
- - (SvTAIL(prog->anchored_substr)!=0)),
- SvPVX(prog->anchored_substr),
- PL_colors[1], (SvTAIL(prog->anchored_substr) ? "$" : "")));
+ (int)(SvCUR(must)
+ - (SvTAIL(must)!=0)),
+ SvPVX(must),
+ PL_colors[1], (SvTAIL(must) ? "$" : "")));
if (!s) {
if (last1 >= last2) {
DEBUG_r(PerlIO_printf(Perl_debug_log,
@@ -633,54 +655,60 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
}
}
else { /* Take into account the floating substring. */
- char *last, *last1;
- char *s1 = s;
-
- t = HOP3c(s, -start_shift, strbeg);
- last1 = last =
- HOP3c(strend, -prog->minlen + prog->float_min_offset, strbeg);
- if (CHR_DIST((U8*)last, (U8*)t) > prog->float_max_offset)
- last = HOP3c(t, prog->float_max_offset, strend);
- s = HOP3c(t, prog->float_min_offset, strend);
- if (s < other_last)
- s = other_last;
+ char *last, *last1;
+ char *s1 = s;
+ SV* must;
+
+ t = HOP3c(s, -start_shift, strbeg);
+ last1 = last =
+ HOP3c(strend, -prog->minlen + prog->float_min_offset, strbeg);
+ if (CHR_DIST((U8*)last, (U8*)t) > prog->float_max_offset)
+ last = HOP3c(t, prog->float_max_offset, strend);
+ s = HOP3c(t, prog->float_min_offset, strend);
+ if (s < other_last)
+ s = other_last;
/* XXXX It is not documented what units *_offsets are in. Assume bytes. */
- /* fbm_instr() takes into account exact value of end-of-str
- if the check is SvTAIL(ed). Since false positives are OK,
- and end-of-str is not later than strend we are OK. */
+ must = do_utf8 ? prog->float_utf8 : prog->float_substr;
+ /* fbm_instr() takes into account exact value of end-of-str
+ if the check is SvTAIL(ed). Since false positives are OK,
+ and end-of-str is not later than strend we are OK. */
+ if (must == &PL_sv_undef) {
+ s = (char*)NULL;
+ DEBUG_r(must = prog->float_utf8); /* for debug message */
+ }
+ else
s = fbm_instr((unsigned char*)s,
- (unsigned char*)last + SvCUR(prog->float_substr)
- - (SvTAIL(prog->float_substr)!=0),
- prog->float_substr, PL_multiline ? FBMrf_MULTILINE : 0);
- DEBUG_r(PerlIO_printf(Perl_debug_log, "%s floating substr `%s%.*s%s'%s",
- (s ? "Found" : "Contradicts"),
- PL_colors[0],
- (int)(SvCUR(prog->float_substr)
- - (SvTAIL(prog->float_substr)!=0)),
- SvPVX(prog->float_substr),
- PL_colors[1], (SvTAIL(prog->float_substr) ? "$" : "")));
- if (!s) {
- if (last1 == last) {
- DEBUG_r(PerlIO_printf(Perl_debug_log,
- ", giving up...\n"));
- goto fail_finish;
- }
+ (unsigned char*)last + SvCUR(must)
+ - (SvTAIL(must)!=0),
+ must, PL_multiline ? FBMrf_MULTILINE : 0);
+ DEBUG_r(PerlIO_printf(Perl_debug_log, "%s floating substr `%s%.*s%s'%s",
+ (s ? "Found" : "Contradicts"),
+ PL_colors[0],
+ (int)(SvCUR(must) - (SvTAIL(must)!=0)),
+ SvPVX(must),
+ PL_colors[1], (SvTAIL(must) ? "$" : "")));
+ if (!s) {
+ if (last1 == last) {
DEBUG_r(PerlIO_printf(Perl_debug_log,
- ", trying anchored starting at offset %ld...\n",
- (long)(s1 + 1 - i_strpos)));
- other_last = last;
- s = HOP3c(t, 1, strend);
- goto restart;
- }
- else {
- DEBUG_r(PerlIO_printf(Perl_debug_log, " at offset %ld...\n",
- (long)(s - i_strpos)));
- other_last = s; /* Fix this later. --Hugo */
- s = s1;
- if (t == strpos)
- goto try_at_start;
- goto try_at_offset;
+ ", giving up...\n"));
+ goto fail_finish;
}
+ DEBUG_r(PerlIO_printf(Perl_debug_log,
+ ", trying anchored starting at offset %ld...\n",
+ (long)(s1 + 1 - i_strpos)));
+ other_last = last;
+ s = HOP3c(t, 1, strend);
+ goto restart;
+ }
+ else {
+ DEBUG_r(PerlIO_printf(Perl_debug_log, " at offset %ld...\n",
+ (long)(s - i_strpos)));
+ other_last = s; /* Fix this later. --Hugo */
+ s = s1;
+ if (t == strpos)
+ goto try_at_start;
+ goto try_at_offset;
+ }
}
}
@@ -703,7 +731,7 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
while (t < strend - prog->minlen) {
if (*t == '\n') {
if (t < check_at - prog->check_offset_min) {
- if (prog->anchored_substr) {
+ if (do_utf8 ? prog->anchored_utf8 : prog->anchored_substr) {
/* Since we moved from the found position,
we definitely contradict the found anchored
substr. Due to the above check we do not
@@ -743,7 +771,7 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
}
s = t;
set_useful:
- ++BmUSEFUL(prog->check_substr); /* hooray/5 */
+ ++BmUSEFUL(do_utf8 ? prog->check_utf8 : prog->check_substr); /* hooray/5 */
}
else {
/* The found string does not prohibit matching at strpos,
@@ -767,15 +795,23 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
);
success_at_start:
if (!(prog->reganch & ROPT_NAUGHTY) /* XXXX If strpos moved? */
- && prog->check_substr /* Could be deleted already */
- && --BmUSEFUL(prog->check_substr) < 0
- && prog->check_substr == prog->float_substr)
+ && (do_utf8 ? (
+ prog->check_utf8 /* Could be deleted already */
+ && --BmUSEFUL(prog->check_utf8) < 0
+ && (prog->check_utf8 == prog->float_utf8)
+ ) : (
+ prog->check_substr /* Could be deleted already */
+ && --BmUSEFUL(prog->check_substr) < 0
+ && (prog->check_substr == prog->float_substr)
+ )))
{
/* If flags & SOMETHING - do not do it many times on the same match */
DEBUG_r(PerlIO_printf(Perl_debug_log, "... Disabling check substring...\n"));
- SvREFCNT_dec(prog->check_substr);
- prog->check_substr = Nullsv; /* disable */
- prog->float_substr = Nullsv; /* clear */
+ SvREFCNT_dec(do_utf8 ? prog->check_utf8 : prog->check_substr);
+ if (do_utf8 ? prog->check_substr : prog->check_utf8)
+ SvREFCNT_dec(do_utf8 ? prog->check_substr : prog->check_utf8);
+ prog->check_substr = prog->check_utf8 = Nullsv; /* disable */
+ prog->float_substr = prog->float_utf8 = Nullsv; /* clear */
check = Nullsv; /* abort */
s = strpos;
/* XXXX This is a remnant of the old implementation. It
@@ -802,9 +838,9 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
int cl_l = (PL_regkind[(U8)OP(prog->regstclass)] == EXACT
? CHR_DIST(str+STR_LEN(prog->regstclass), str)
: 1);
- char *endpos = (prog->anchored_substr || ml_anch)
+ char *endpos = (prog->anchored_substr || prog->anchored_utf8 || ml_anch)
? HOP3c(s, (prog->minlen ? cl_l : 0), strend)
- : (prog->float_substr
+ : (prog->float_substr || prog->float_utf8
? HOP3c(HOP3c(check_at, -start_shift, strbeg),
cl_l, strend)
: strend);
@@ -830,8 +866,8 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
if ((prog->reganch & ROPT_ANCH) && !ml_anch)
goto fail;
/* Contradict one of substrings */
- if (prog->anchored_substr) {
- if (prog->anchored_substr == check) {
+ if (prog->anchored_substr || prog->anchored_utf8) {
+ if ((do_utf8 ? prog->anchored_utf8 : prog->anchored_substr) == check) {
DEBUG_r( what = "anchored" );
hop_and_restart:
s = HOP3c(t, 1, strend);
@@ -871,7 +907,7 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
PL_colors[0],PL_colors[1], (long)(t - i_strpos)) );
goto try_at_offset;
}
- if (!prog->float_substr) /* Could have been deleted */
+ if (!(do_utf8 ? prog->float_utf8 : prog->float_substr)) /* Could have been deleted */
goto fail;
/* Check is floating subtring. */
retry_floating_check:
@@ -898,8 +934,8 @@ Perl_re_intuit_start(pTHX_ regexp *prog, SV *sv, char *strpos,
return s;
fail_finish: /* Substring not found */
- if (prog->check_substr) /* could be removed already */
- BmUSEFUL(prog->check_substr) += 5; /* hooray */
+ if (prog->check_substr || prog->check_utf8) /* could be removed already */
+ BmUSEFUL(do_utf8 ? prog->check_utf8 : prog->check_substr) += 5; /* hooray */
fail:
DEBUG_r(PerlIO_printf(Perl_debug_log, "%sMatch rejected by optimizer%s\n",
PL_colors[4],PL_colors[5]));
@@ -1626,8 +1662,7 @@ Perl_regexec_flags(pTHX_ register regexp *prog, char *stringarg, register char *
PL_reg_ganch = strbeg;
}
- if (do_utf8 == (UTF!=0) &&
- !(flags & REXEC_CHECKED) && prog->check_substr != Nullsv) {
+ if (!(flags & REXEC_CHECKED) && (prog->check_substr != Nullsv || prog->check_utf8 != Nullsv)) {
re_scream_pos_data d;
d.scream_olds = &scream_olds;
@@ -1677,7 +1712,7 @@ Perl_regexec_flags(pTHX_ register regexp *prog, char *stringarg, register char *
dontbother = minlen - 1;
end = HOP3c(strend, -dontbother, strbeg) - 1;
/* for multiline we only have to try after newlines */
- if (prog->check_substr) {
+ if (prog->check_substr || prog->check_utf8) {
if (s == startpos)
goto after_try;
while (1) {
@@ -1713,13 +1748,16 @@ Perl_regexec_flags(pTHX_ register regexp *prog, char *stringarg, register char *
}
/* Messy cases: unanchored match. */
- if (prog->anchored_substr && prog->reganch & ROPT_SKIP) {
+ if ((prog->anchored_substr || prog->anchored_utf8) && prog->reganch & ROPT_SKIP) {
/* we have /x+whatever/ */
/* it must be a one character string (XXXX Except UTF?) */
- char ch = SvPVX(prog->anchored_substr)[0];
+ char ch;
#ifdef DEBUGGING
int did_match = 0;
#endif
+ if (!(do_utf8 ? prog->anchored_utf8 : prog->anchored_substr))
+ do_utf8 ? to_utf8_substr(prog) : to_byte_substr(prog);
+ ch = SvPVX(do_utf8 ? prog->anchored_utf8 : prog->anchored_substr)[0];
if (do_utf8) {
while (s < strend) {
@@ -1751,23 +1789,37 @@ Perl_regexec_flags(pTHX_ register regexp *prog, char *stringarg, register char *
);
}
/*SUPPRESS 560*/
- else if (do_utf8 == (UTF!=0) &&
- (prog->anchored_substr != Nullsv
- || (prog->float_substr != Nullsv
- && prog->float_max_offset < strend - s))) {
- SV *must = prog->anchored_substr
- ? prog->anchored_substr : prog->float_substr;
- I32 back_max =
- prog->anchored_substr ? prog->anchored_offset : prog->float_max_offset;
- I32 back_min =
- prog->anchored_substr ? prog->anchored_offset : prog->float_min_offset;
- char *last = HOP3c(strend, /* Cannot start after this */
- -(I32)(CHR_SVLEN(must)
- - (SvTAIL(must) != 0) + back_min), strbeg);
+ else if (prog->anchored_substr != Nullsv
+ || prog->anchored_utf8 != Nullsv
+ || ((prog->float_substr != Nullsv || prog->float_utf8 != Nullsv)
+ && prog->float_max_offset < strend - s)) {
+ SV *must;
+ I32 back_max;
+ I32 back_min;
+ char *last;
char *last1; /* Last position checked before */
#ifdef DEBUGGING
int did_match = 0;
#endif
+ if (prog->anchored_substr || prog->anchored_utf8) {
+ if (!(do_utf8 ? prog->anchored_utf8 : prog->anchored_substr))
+ do_utf8 ? to_utf8_substr(prog) : to_byte_substr(prog);
+ must = do_utf8 ? prog->anchored_utf8 : prog->anchored_substr;
+ back_max = back_min = prog->anchored_offset;
+ } else {
+ if (!(do_utf8 ? prog->float_utf8 : prog->float_substr))
+ do_utf8 ? to_utf8_substr(prog) : to_byte_substr(prog);
+ must = do_utf8 ? prog->float_utf8 : prog->float_substr;
+ back_max = prog->float_max_offset;
+ back_min = prog->float_min_offset;
+ }
+ if (must == &PL_sv_undef)
+ /* could not downgrade utf8 check substring, so must fail */
+ goto phooey;
+
+ last = HOP3c(strend, /* Cannot start after this */
+ -(I32)(CHR_SVLEN(must)
+ - (SvTAIL(must) != 0) + back_min), strbeg);
if (s > PL_bostr)
last1 = HOPc(s, -1);
@@ -1815,7 +1867,7 @@ Perl_regexec_flags(pTHX_ register regexp *prog, char *stringarg, register char *
DEBUG_r(if (!did_match)
PerlIO_printf(Perl_debug_log,
"Did not find %s substr `%s%.*s%s'%s...\n",
- ((must == prog->anchored_substr)
+ ((must == prog->anchored_substr || must == prog->anchored_utf8)
? "anchored" : "floating"),
PL_colors[0],
(int)(SvCUR(must) - (SvTAIL(must)!=0)),
@@ -1855,20 +1907,26 @@ Perl_regexec_flags(pTHX_ register regexp *prog, char *stringarg, register char *
}
else {
dontbother = 0;
- if (prog->float_substr != Nullsv) { /* Trim the end. */
+ if (prog->float_substr != Nullsv || prog->float_utf8 != Nullsv) {
+ /* Trim the end. */
char *last;
+ SV* float_real;
+
+ if (!(do_utf8 ? prog->float_utf8 : prog->float_substr))
+ do_utf8 ? to_utf8_substr(prog) : to_byte_substr(prog);
+ float_real = do_utf8 ? prog->float_utf8 : prog->float_substr;
if (flags & REXEC_SCREAM) {
- last = screaminstr(sv, prog->float_substr, s - strbeg,
+ last = screaminstr(sv, float_real, s - strbeg,
end_shift, &scream_pos, 1); /* last one */
if (!last)
last = scream_olds; /* Only one occurrence. */
}
else {
STRLEN len;
- char *little = SvPV(prog->float_substr, len);
+ char *little = SvPV(float_real, len);
- if (SvTAIL(prog->float_substr)) {
+ if (SvTAIL(float_real)) {
if (memEQ(strend - len + 1, little, len - 1))
last = strend - len + 1;
else if (!PL_multiline)
@@ -4426,3 +4484,59 @@ restore_pos(pTHX_ void *arg)
PL_curpm = PL_reg_oldcurpm;
}
}
+
+STATIC void
+S_to_utf8_substr(pTHX_ register regexp *prog)
+{
+ SV* sv;
+ if (prog->float_substr && !prog->float_utf8) {
+ prog->float_utf8 = sv = NEWSV(117, 0);
+ SvSetMagicSV(sv, prog->float_substr);
+ sv_utf8_upgrade(sv);
+ if (SvTAIL(prog->float_substr))
+ SvTAIL_on(sv);
+ if (prog->float_substr == prog->check_substr)
+ prog->check_utf8 = sv;
+ }
+ if (prog->anchored_substr && !prog->anchored_utf8) {
+ prog->anchored_utf8 = sv = NEWSV(118, 0);
+ SvSetMagicSV(sv, prog->anchored_substr);
+ sv_utf8_upgrade(sv);
+ if (SvTAIL(prog->anchored_substr))
+ SvTAIL_on(sv);
+ if (prog->anchored_substr == prog->check_substr)
+ prog->check_utf8 = sv;
+ }
+}
+
+STATIC void
+S_to_byte_substr(pTHX_ register regexp *prog)
+{
+ SV* sv;
+ if (prog->float_utf8 && !prog->float_substr) {
+ prog->float_substr = sv = NEWSV(117, 0);
+ SvSetMagicSV(sv, prog->float_utf8);
+ if (sv_utf8_downgrade(sv, TRUE)) {
+ if (SvTAIL(prog->float_utf8))
+ SvTAIL_on(sv);
+ } else {
+ SvREFCNT_dec(sv);
+ prog->float_substr = sv = &PL_sv_undef;
+ }
+ if (prog->float_utf8 == prog->check_utf8)
+ prog->check_substr = sv;
+ }
+ if (prog->anchored_utf8 && !prog->anchored_substr) {
+ prog->anchored_substr = sv = NEWSV(118, 0);
+ SvSetMagicSV(sv, prog->anchored_utf8);
+ if (sv_utf8_downgrade(sv, TRUE)) {
+ if (SvTAIL(prog->anchored_utf8))
+ SvTAIL_on(sv);
+ } else {
+ SvREFCNT_dec(sv);
+ prog->anchored_substr = sv = &PL_sv_undef;
+ }
+ if (prog->anchored_utf8 == prog->check_utf8)
+ prog->check_substr = sv;
+ }
+}
diff --git a/sv.c b/sv.c
index ac4090052c..21de7ed654 100644
--- a/sv.c
+++ b/sv.c
@@ -8592,6 +8592,7 @@ Perl_re_dup(pTHX_ REGEXP *r, CLONE_PARAMS *param)
s->min_offset = r->substrs->data[i].min_offset;
s->max_offset = r->substrs->data[i].max_offset;
s->substr = sv_dup_inc(r->substrs->data[i].substr, param);
+ s->utf8_substr = sv_dup_inc(r->substrs->data[i].utf8_substr, param);
}
ret->regstclass = NULL;
diff --git a/t/op/pat.t b/t/op/pat.t
index a00e624bf4..b5dff4b7e3 100755
--- a/t/op/pat.t
+++ b/t/op/pat.t
@@ -6,7 +6,7 @@
$| = 1;
-print "1..864\n";
+print "1..892\n";
BEGIN {
chdir 't' if -d 't';
@@ -2730,3 +2730,44 @@ print "# some Unicode properties\n";
print $u eq "feeber" ? "ok 864\n" : "not ok 864\n";
}
+{
+ print "# UTF-8 bug with s///\n";
+ # check utf8/non-utf8 mixtures
+ # try to force all float/anchored check combinations
+ my $c = "\x{100}";
+ my $test = 865;
+ my $subst;
+ for my $re (
+ "xx.*$c", "x.*$c$c", "$c.*xx", "$c$c.*x", "xx.*(?=$c)", "(?=$c).*xx",
+ ) {
+ print "xxx" =~ /$re/ ? "not ok $test\n" : "ok $test\n";
+ ++$test;
+ print +($subst = "xxx") =~ s/$re// ? "not ok $test\n" : "ok $test\n";
+ ++$test;
+ }
+ for my $re ("xx.*$c*", "$c*.*xx") {
+ print "xxx" =~ /$re/ ? "ok $test\n" : "not ok $test\n";
+ ++$test;
+ ($subst = "xxx") =~ s/$re//;
+ print $subst eq '' ? "ok $test\n" : "not ok $test\t# $subst\n";
+ ++$test;
+ }
+ for my $re ("xxy*", "y*xx") {
+ print "xx$c" =~ /$re/ ? "ok $test\n" : "not ok $test\n";
+ ++$test;
+ ($subst = "xx$c") =~ s/$re//;
+ print $subst eq $c ? "ok $test\n" : "not ok $test\n";
+ ++$test;
+ print "xy$c" =~ /$re/ ? "not ok $test\n" : "ok $test\n";
+ ++$test;
+ print +($subst = "xy$c") =~ /$re/ ? "not ok $test\n" : "ok $test\n";
+ ++$test;
+ }
+ for my $re ("xy$c*z", "x$c*yz") {
+ print "xyz" =~ /$re/ ? "ok $test\n" : "not ok $test\n";
+ ++$test;
+ ($subst = "xyz") =~ s/$re//;
+ print $subst eq '' ? "ok $test\n" : "not ok $test\n";
+ ++$test;
+ }
+}