This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
regmatch(): use nextchar where available
[perl5.git] / regexec.c
index b04f221..d606238 100644 (file)
--- a/regexec.c
+++ b/regexec.c
@@ -2568,6 +2568,7 @@ got_it:
                                  "Copy on write: regexp capture, type %d\n",
                                  (int) SvTYPE(sv));
                }
+                RX_MATCH_COPY_FREE(rx);
                prog->saved_copy = sv_setsv_cow(prog->saved_copy, sv);
                prog->subbeg = (char *)SvPVX_const(prog->saved_copy);
                assert (SvPOKp(prog->saved_copy));
@@ -2640,8 +2641,8 @@ got_it:
                 prog->subbeg[sublen] = '\0';
                 prog->suboffset = min;
                 prog->sublen = sublen;
+                RX_MATCH_COPIED_on(rx);
            }
-            RX_MATCH_COPIED_on(rx);
             prog->subcoffset = prog->suboffset;
             if (prog->suboffset && utf8_target) {
                 /* Convert byte offset to chars.
@@ -2711,12 +2712,13 @@ phooey:
  - regtry - try match at specific point
  */
 STATIC I32                     /* 0 failure, 1 success */
-S_regtry(pTHX_ regmatch_info *reginfo, char **startpos)
+S_regtry(pTHX_ regmatch_info *reginfo, char **startposp)
 {
     dVAR;
     CHECKPOINT lastcp;
     REGEXP *const rx = reginfo->prog;
     regexp *const prog = (struct regexp *)SvANY(rx);
+    I32 result;
     RXi_GET_DECL(prog,progi);
     GET_RE_DEBUG_FLAGS_DECL;
 
@@ -2789,10 +2791,9 @@ S_regtry(pTHX_ regmatch_info *reginfo, char **startpos)
        prog->sublen = PL_regeol - PL_bostr; /* strend may have been modified */
     }
 #ifdef DEBUGGING
-    PL_reg_starttry = *startpos;
+    PL_reg_starttry = *startposp;
 #endif
-    prog->offs[0].start = *startpos - PL_bostr;
-    PL_reginput = *startpos;
+    prog->offs[0].start = *startposp - PL_bostr;
     prog->lastparen = 0;
     prog->lastcloseparen = 0;
     PL_regsize = 0;
@@ -2822,12 +2823,13 @@ S_regtry(pTHX_ regmatch_info *reginfo, char **startpos)
     }
 #endif
     REGCP_SET(lastcp);
-    if (regmatch(reginfo, progi->program + 1)) {
-       prog->offs[0].end = PL_reginput - PL_bostr;
+    result = regmatch(reginfo, *startposp, progi->program + 1);
+    if (result != -1) {
+       prog->offs[0].end = result;
        return 1;
     }
     if (reginfo->cutpoint)
-        *startpos= reginfo->cutpoint;
+        *startposp= reginfo->cutpoint;
     REGCP_UNWIND(lastcp);
     return 0;
 }
@@ -2879,20 +2881,23 @@ S_push_slab(pTHX)
 
 /* push a new state then goto it */
 
-#define PUSH_STATE_GOTO(state, node) \
+#define PUSH_STATE_GOTO(state, node, input) \
+    pushinput = input; \
     scan = node; \
     st->resume_state = state; \
     goto push_state;
 
 /* push a new state with success backtracking, then goto it */
 
-#define PUSH_YES_STATE_GOTO(state, node) \
+#define PUSH_YES_STATE_GOTO(state, node, input) \
+    pushinput = input; \
     scan = node; \
     st->resume_state = state; \
     goto push_yes_state;
 
 
 
+
 /*
 
 regmatch() - main matching routine
@@ -2958,7 +2963,7 @@ implementation:
        // push a yes backtrack state with a resume value of
        // IFMATCH_A/IFMATCH_A_fail, then continue execution at the
        // first node of A:
-       PUSH_YES_STATE_GOTO(IFMATCH_A, A);
+       PUSH_YES_STATE_GOTO(IFMATCH_A, A, newinput);
        // NOTREACHED
 
     case IFMATCH_A: // we have successfully executed A; now continue with B
@@ -2995,8 +3000,8 @@ The topmost backtrack state, pointed to by st, is usually free. If you
 want to claim it, populate any ST.foo fields in it with values you wish to
 save, then do one of
 
-       PUSH_STATE_GOTO(resume_state, node);
-       PUSH_YES_STATE_GOTO(resume_state, node);
+       PUSH_STATE_GOTO(resume_state, node, newinput);
+       PUSH_YES_STATE_GOTO(resume_state, node, newinput);
 
 which sets that backtrack state's resume value to 'resume_state', pushes a
 new free entry to the top of the backtrack stack, then goes to 'node'.
@@ -3188,8 +3193,9 @@ S_clear_backtrack_stack(pTHX_ void *p)
 }
 
 
-STATIC I32                     /* 0 failure, 1 success */
-S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
+/* returns -1 on failure, $+[0] on success */
+STATIC I32
+S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog)
 {
 #if PERL_VERSION < 9 && !defined(PERL_CORE)
     dMY_CXT;
@@ -3208,7 +3214,8 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
     regnode *next;
     U32 n = 0; /* general value; init to avoid compiler warning */
     I32 ln = 0; /* len or last;  init to avoid compiler warning */
-    char *locinput = PL_reginput;
+    char *locinput = startpos;
+    char *pushinput; /* where to continue after a PUSH */
     I32 nextchr;   /* is always set to UCHARAT(locinput) */
 
     bool result = 0;       /* return value of S_regmatch */
@@ -3227,7 +3234,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
     U32 state_num;
     bool no_final = 0;      /* prevent failure from backtracking? */
     bool do_cutgroup = 0;   /* no_final only until next branch/trie entry */
-    char *startpoint = PL_reginput;
+    char *startpoint = locinput;
     SV *popmark = NULL;     /* are we looking for a mark? */
     SV *sv_commit = NULL;   /* last mark name seen in failure */
     SV *sv_yes_mark = NULL; /* last mark name we have seen 
@@ -3344,9 +3351,8 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
        case KEEPS:
            /* update the startpoint */
            st->u.keeper.val = rex->offs[0].start;
-           PL_reginput = locinput;
            rex->offs[0].start = locinput - PL_bostr;
-           PUSH_STATE_GOTO(KEEPS_next, next);
+           PUSH_STATE_GOTO(KEEPS_next, next, locinput);
            /*NOT-REACHED*/
        case KEEPS_next_fail:
            /* rollback the start point change */
@@ -3406,7 +3412,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
             /* In this case the charclass data is available inline so
                we can fail fast without a lot of extra overhead. 
              */
-            if(!ANYOF_BITMAP_TEST(scan, *locinput)) {
+            if(!ANYOF_BITMAP_TEST(scan, nextchr)) {
                 DEBUG_EXECUTE_r(
                     PerlIO_printf(Perl_debug_log,
                               "%*s  %sfailed to match trie start class...%s\n",
@@ -3471,7 +3477,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                HV * widecharmap = MUTABLE_HV(rexi->data->data[ ARG( scan ) + 1 ]);
                 U32 state = trie->startstate;
 
-                if (trie->bitmap && !TRIE_BITMAP_TEST(trie,*locinput) ) {
+                if (trie->bitmap && !TRIE_BITMAP_TEST(trie, nextchr) ) {
                    if (trie->states[ state ].wordnum) {
                         DEBUG_EXECUTE_r(
                             PerlIO_printf(Perl_debug_log,
@@ -3601,6 +3607,8 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
            assert(0); /* NOTREACHED */
 
        case TRIE_next_fail: /* we failed - try next alternative */
+        {
+            U8 *uc;
             if ( ST.jump) {
                 REGCP_UNWIND(ST.cp);
                 UNWIND_PAREN(ST.lastparen, ST.lastcloseparen);
@@ -3645,7 +3653,6 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
            /* find start char of end of current word */
            {
                U32 chars; /* how many chars to skip */
-               U8 *uc = ST.firstpos;
                reg_trie_data * const trie
                    = (reg_trie_data*)rexi->data->data[ARG(ST.me)];
 
@@ -3653,6 +3660,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                            >=  ST.firstchars);
                chars = (trie->wordinfo[ST.nextword].len - trie->prefixlen)
                            - ST.firstchars;
+               uc = ST.firstpos;
 
                if (ST.longfold) {
                    /* the hard option - fold each char in turn and find
@@ -3692,7 +3700,6 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                    else
                        uc += chars;
                }
-               PL_reginput = (char *)uc;
            }
 
            scan = ST.me + ((ST.jump && ST.jump[ST.nextword])
@@ -3710,7 +3717,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
            });
 
            if (ST.accepted > 1 || has_cutgroup) {
-               PUSH_STATE_GOTO(TRIE_next, scan);
+               PUSH_STATE_GOTO(TRIE_next, scan, (char*)uc);
                assert(0); /* NOTREACHED */
            }
            /* only one choice left - just continue */
@@ -3733,10 +3740,11 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                    PL_colors[5] );
            });
 
-           locinput = PL_reginput;
+           locinput = (char*)uc;
            nextchr = UCHARAT(locinput);
            continue; /* execute rest of RE */
            assert(0); /* NOTREACHED */
+        }
 #undef  ST
 
        case EXACT: {
@@ -3748,31 +3756,56 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                const char * const e = s + ln;
 
                if (utf8_target) {
-                   /* The target is utf8, the pattern is not utf8. */
+                    /* The target is utf8, the pattern is not utf8.
+                     * Above-Latin1 code points can't match the pattern;
+                     * invariants match exactly, and the other Latin1 ones need
+                     * to be downgraded to a single byte in order to do the
+                     * comparison.  (If we could be confident that the target
+                     * is not malformed, this could be refactored to have fewer
+                     * tests by just assuming that if the first bytes match, it
+                     * is an invariant, but there are tests in the test suite
+                     * dealing with (??{...}) which violate this) */
                    while (s < e) {
-                       STRLEN ulen;
                        if (l >= PL_regeol)
                             sayNO;
-                       if (NATIVE_TO_UNI(*(U8*)s) !=
-                           utf8n_to_uvuni((U8*)l, UTF8_MAXBYTES, &ulen,
-                                           uniflags))
-                            sayNO;
-                       l += ulen;
-                       s ++;
+                        if (UTF8_IS_ABOVE_LATIN1(* (U8*) l)) {
+                            sayNO;
+                        }
+                        if (UTF8_IS_INVARIANT(*(U8*)l)) {
+                           if (*l != *s) {
+                                sayNO;
+                            }
+                            l++;
+                        }
+                        else {
+                            if (TWO_BYTE_UTF8_TO_UNI(*l, *(l+1)) != * (U8*) s) {
+                                sayNO;
+                            }
+                            l += 2;
+                        }
+                       s++;
                    }
                }
                else {
                    /* The target is not utf8, the pattern is utf8. */
                    while (s < e) {
-                       STRLEN ulen;
-                       if (l >= PL_regeol)
-                           sayNO;
-                       if (NATIVE_TO_UNI(*((U8*)l)) !=
-                           utf8n_to_uvuni((U8*)s, UTF8_MAXBYTES, &ulen,
-                                          uniflags))
-                           sayNO;
-                       s += ulen;
-                       l ++;
+                        if (l >= PL_regeol || UTF8_IS_ABOVE_LATIN1(* (U8*) s))
+                        {
+                            sayNO;
+                        }
+                        if (UTF8_IS_INVARIANT(*(U8*)s)) {
+                           if (*s != *l) {
+                                sayNO;
+                            }
+                            s++;
+                        }
+                        else {
+                            if (TWO_BYTE_UTF8_TO_UNI(*s, *(s+1)) != * (U8*) l) {
+                                sayNO;
+                            }
+                            s += 2;
+                        }
+                       l++;
                    }
                }
                locinput = l;
@@ -4622,7 +4655,6 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                re->lastparen = 0;
                re->lastcloseparen = 0;
 
-               PL_reginput = locinput;
                PL_regsize = 0;
 
                /* XXXX This is too dramatic a measure... */
@@ -4646,7 +4678,7 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
                ST.prev_eval = cur_eval;
                cur_eval = st;
                /* now continue from first node in postoned RE */
-               PUSH_YES_STATE_GOTO(EVAL_AB, startpoint);
+               PUSH_YES_STATE_GOTO(EVAL_AB, startpoint, locinput);
                assert(0); /* NOTREACHED */
        }
 
@@ -4676,7 +4708,6 @@ S_regmatch(pTHX_ regmatch_info *reginfo, regnode *prog)
            rex = (struct regexp *)SvANY(rex_sv);
            rexi = RXi_GET(rex); 
 
-           PL_reginput = locinput;
            REGCP_UNWIND(ST.lastcp);
            regcppop(rex);
            cur_eval = ST.prev_eval;
@@ -4896,8 +4927,7 @@ NULL
            ST.count = -1;      /* this will be updated by WHILEM */
            ST.lastloc = NULL;  /* this will be updated by WHILEM */
 
-           PL_reginput = locinput;
-           PUSH_YES_STATE_GOTO(CURLYX_end, PREVOPER(next));
+           PUSH_YES_STATE_GOTO(CURLYX_end, PREVOPER(next), locinput);
            assert(0); /* NOTREACHED */
        }
 
@@ -4930,7 +4960,6 @@ NULL
            ST.cache_offset = 0;
            ST.cache_mask = 0;
            
-           PL_reginput = locinput;
 
            DEBUG_EXECUTE_r( PerlIO_printf(Perl_debug_log,
                  "%*s  whilem: matched %ld out of %d..%d\n",
@@ -4944,7 +4973,7 @@ NULL
                cur_curlyx->u.curlyx.lastloc = locinput;
                REGCP_SET(ST.lastcp);
 
-               PUSH_STATE_GOTO(WHILEM_A_pre, A);
+               PUSH_STATE_GOTO(WHILEM_A_pre, A, locinput);
                assert(0); /* NOTREACHED */
            }
 
@@ -5018,7 +5047,8 @@ NULL
                cur_curlyx = cur_curlyx->u.curlyx.prev_curlyx;
                ST.cp = regcppush(rex, ST.save_curlyx->u.curlyx.parenfloor);
                REGCP_SET(ST.lastcp);
-               PUSH_YES_STATE_GOTO(WHILEM_B_min, ST.save_curlyx->u.curlyx.B);
+               PUSH_YES_STATE_GOTO(WHILEM_B_min, ST.save_curlyx->u.curlyx.B,
+                                    locinput);
                assert(0); /* NOTREACHED */
            }
 
@@ -5028,7 +5058,7 @@ NULL
                ST.cp = regcppush(rex, cur_curlyx->u.curlyx.parenfloor);
                cur_curlyx->u.curlyx.lastloc = locinput;
                REGCP_SET(ST.lastcp);
-               PUSH_STATE_GOTO(WHILEM_A_max, A);
+               PUSH_STATE_GOTO(WHILEM_A_max, A, locinput);
                assert(0); /* NOTREACHED */
            }
            goto do_whilem_B_max;
@@ -5061,7 +5091,6 @@ NULL
        case WHILEM_A_max_fail: /* just failed to match A in a maximal match */
            REGCP_UNWIND(ST.lastcp);
            regcppop(rex);      /* Restore some previous $<digit>s? */
-           PL_reginput = locinput;
            DEBUG_EXECUTE_r(PerlIO_printf(Perl_debug_log,
                "%*s  whilem: failed, trying continuation...\n",
                REPORT_CODE_OFF+depth*2, "")
@@ -5081,7 +5110,8 @@ NULL
            /* now try B */
            ST.save_curlyx = cur_curlyx;
            cur_curlyx = cur_curlyx->u.curlyx.prev_curlyx;
-           PUSH_YES_STATE_GOTO(WHILEM_B_max, ST.save_curlyx->u.curlyx.B);
+           PUSH_YES_STATE_GOTO(WHILEM_B_max, ST.save_curlyx->u.curlyx.B,
+                                locinput);
            assert(0); /* NOTREACHED */
 
        case WHILEM_B_min_fail: /* just failed to match B in a minimal match */
@@ -5109,12 +5139,12 @@ NULL
                "%*s  trying longer...\n", REPORT_CODE_OFF+depth*2, "")
            );
            /* Try grabbing another A and see if it helps. */
-           PL_reginput = locinput;
            cur_curlyx->u.curlyx.lastloc = locinput;
            ST.cp = regcppush(rex, cur_curlyx->u.curlyx.parenfloor);
            REGCP_SET(ST.lastcp);
            PUSH_STATE_GOTO(WHILEM_A_min,
-               /*A*/ NEXTOPER(ST.save_curlyx->u.curlyx.me) + EXTRA_STEP_2ARGS);
+               /*A*/ NEXTOPER(ST.save_curlyx->u.curlyx.me) + EXTRA_STEP_2ARGS,
+                locinput);
            assert(0); /* NOTREACHED */
 
 #undef  ST
@@ -5133,20 +5163,18 @@ NULL
            ST.lastcloseparen = rex->lastcloseparen;
            ST.next_branch = next;
            REGCP_SET(ST.cp);
-           PL_reginput = locinput;
 
            /* Now go into the branch */
            if (has_cutgroup) {
-               PUSH_YES_STATE_GOTO(BRANCH_next, scan);    
+               PUSH_YES_STATE_GOTO(BRANCH_next, scan, locinput);
            } else {
-               PUSH_STATE_GOTO(BRANCH_next, scan);
+               PUSH_STATE_GOTO(BRANCH_next, scan, locinput);
            }
            assert(0); /* NOTREACHED */
         case CUTGROUP:
-            PL_reginput = locinput;
             sv_yes_mark = st->u.mark.mark_name = scan->flags ? NULL :
                 MUTABLE_SV(rexi->data->data[ ARG( scan ) ]);
-            PUSH_STATE_GOTO(CUTGROUP_next,next);
+            PUSH_STATE_GOTO(CUTGROUP_next, next, locinput);
             assert(0); /* NOTREACHED */
         case CUTGROUP_next_fail:
             do_cutgroup = 1;
@@ -5221,26 +5249,22 @@ NULL
                goto curlym_do_B;
 
          curlym_do_A: /* execute the A in /A{m,n}B/  */
-           PL_reginput = locinput;
-           PUSH_YES_STATE_GOTO(CURLYM_A, ST.A); /* match A */
+           PUSH_YES_STATE_GOTO(CURLYM_A, ST.A, locinput); /* match A */
            assert(0); /* NOTREACHED */
 
        case CURLYM_A: /* we've just matched an A */
-           locinput = st->locinput;
-           nextchr = UCHARAT(locinput);
-
            ST.count++;
            /* after first match, determine A's length: u.curlym.alen */
            if (ST.count == 1) {
                if (PL_reg_match_utf8) {
-                   char *s = locinput;
-                   while (s < PL_reginput) {
+                   char *s = st->locinput;
+                   while (s < locinput) {
                        ST.alen++;
                        s += UTF8SKIP(s);
                    }
                }
                else {
-                   ST.alen = PL_reginput - locinput;
+                   ST.alen = locinput - st->locinput;
                }
                if (ST.alen == 0)
                    ST.count = ST.minmod ? ARG1(ST.me) : ARG2(ST.me);
@@ -5252,8 +5276,6 @@ NULL
                          (IV) ST.count, (IV)ST.alen)
            );
 
-           locinput = PL_reginput;
-                       
            if (cur_eval && cur_eval->u.eval.close_paren && 
                cur_eval->u.eval.close_paren == (U32)ST.me->flags) 
                goto fake_end;
@@ -5274,7 +5296,6 @@ NULL
                sayNO;
 
          curlym_do_B: /* execute the B in /A{m,n}B/  */
-           PL_reginput = locinput;
            if (ST.c1 == CHRTEST_UNINIT) {
                /* calculate c1 and c2 for possible match of 1st char
                 * following curly */
@@ -5316,8 +5337,8 @@ NULL
                    "", (IV)ST.count)
                );
            if (ST.c1 != CHRTEST_VOID
-                   && UCHARAT(PL_reginput) != ST.c1
-                   && UCHARAT(PL_reginput) != ST.c2)
+                   && nextchr != ST.c1
+                   && nextchr != ST.c2)
            {
                /* simulate B failing */
                DEBUG_OPTIMISE_r(
@@ -5335,8 +5356,8 @@ NULL
                I32 paren = ST.me->flags;
                if (ST.count) {
                    rex->offs[paren].start
-                       = HOPc(PL_reginput, -ST.alen) - PL_bostr;
-                   rex->offs[paren].end = PL_reginput - PL_bostr;
+                       = HOPc(locinput, -ST.alen) - PL_bostr;
+                   rex->offs[paren].end = locinput - PL_bostr;
                    if ((U32)paren > rex->lastparen)
                        rex->lastparen = paren;
                    rex->lastcloseparen = paren;
@@ -5353,7 +5374,7 @@ NULL
                }
            }
            
-           PUSH_STATE_GOTO(CURLYM_B, ST.B); /* match B */
+           PUSH_STATE_GOTO(CURLYM_B, ST.B, locinput); /* match B */
            assert(0); /* NOTREACHED */
 
        case CURLYM_B_fail: /* just failed to match a B */
@@ -5370,6 +5391,7 @@ NULL
                sayNO;
            ST.count--;
            locinput = HOPc(locinput, -ST.alen);
+            nextchr = UCHARAT(locinput);
            goto curlym_do_B; /* try to match B */
 
 #undef ST
@@ -5492,13 +5514,14 @@ NULL
 
            ST.A = scan;
            ST.B = next;
-           PL_reginput = locinput;
            if (minmod) {
+                char *li = locinput;
                minmod = 0;
-               if (ST.min && regrepeat(rex, ST.A, ST.min, depth) < ST.min)
+               if (ST.min && regrepeat(rex, &li, ST.A, ST.min, depth) < ST.min)
                    sayNO;
+                locinput = li;
+                nextchr = UCHARAT(locinput);
                ST.count = ST.min;
-               locinput = PL_reginput;
                REGCP_SET(ST.cp);
                if (ST.c1 == CHRTEST_VOID)
                    goto curly_try_B_min;
@@ -5528,10 +5551,14 @@ NULL
 
            }
            else {
-               ST.count = regrepeat(rex, ST.A, ST.max, depth);
-               locinput = PL_reginput;
+                /* avoid taking address of locinput, so it can remain
+                 * a register var */
+                char *li = locinput;
+               ST.count = regrepeat(rex, &li, ST.A, ST.max, depth);
                if (ST.count < ST.min)
                    sayNO;
+                locinput = li;
+                nextchr = UCHARAT(locinput);
                if ((ST.count > ST.min)
                    && (PL_regkind[OP(ST.B)] == EOL) && (OP(ST.B) != MEOL))
                {
@@ -5541,7 +5568,7 @@ NULL
                    /* ...except that $ and \Z can match before *and* after
                       newline at the end.  Consider "\n\n" =~ /\n+\Z\n/.
                       We may back off by one in this case. */
-                   if (UCHARAT(PL_reginput - 1) == '\n' && OP(ST.B) != EOS)
+                   if (UCHARAT(locinput - 1) == '\n' && OP(ST.B) != EOS)
                        ST.min--;
                }
                REGCP_SET(ST.cp);
@@ -5553,7 +5580,6 @@ NULL
        case CURLY_B_min_known_fail:
            /* failed to find B in a non-greedy match where c1,c2 valid */
 
-           PL_reginput = locinput;     /* Could be reset... */
            REGCP_UNWIND(ST.cp);
             if (ST.paren) {
                 UNWIND_PAREN(ST.lastparen, ST.lastcloseparen);
@@ -5612,19 +5638,22 @@ NULL
                }
                if (locinput > ST.maxpos)
                    sayNO;
-               /* PL_reginput == oldloc now */
                if (n) {
+                    /* In /a{m,n}b/, ST.oldloc is at "a" x m, locinput is
+                     * at b; check that everything between oldloc and
+                     * locinput matches */
+                    char *li = ST.oldloc;
                    ST.count += n;
-                   if (regrepeat(rex, ST.A, n, depth) < n)
+                   if (regrepeat(rex, &li, ST.A, n, depth) < n)
                        sayNO;
+                    assert(n == REG_INFTY || locinput == li);
                }
-               PL_reginput = locinput;
                CURLY_SETPAREN(ST.paren, ST.count);
                if (cur_eval && cur_eval->u.eval.close_paren && 
                    cur_eval->u.eval.close_paren == (U32)ST.paren) {
                    goto fake_end;
                }
-               PUSH_STATE_GOTO(CURLY_B_min_known, ST.B);
+               PUSH_STATE_GOTO(CURLY_B_min_known, ST.B, locinput);
            }
            assert(0); /* NOTREACHED */
 
@@ -5637,10 +5666,15 @@ NULL
                 UNWIND_PAREN(ST.lastparen, ST.lastcloseparen);
             }
            /* failed -- move forward one */
-           PL_reginput = locinput;
-           if (regrepeat(rex, ST.A, 1, depth)) {
+            {
+                char *li = locinput;
+                if (!regrepeat(rex, &li, ST.A, 1, depth)) {
+                    sayNO;
+                }
+                locinput = li;
+            }
+            {
                ST.count++;
-               locinput = PL_reginput;
                if (ST.count <= ST.max || (ST.max == REG_INFTY &&
                        ST.count > 0)) /* count overflow ? */
                {
@@ -5650,10 +5684,9 @@ NULL
                        cur_eval->u.eval.close_paren == (U32)ST.paren) {
                         goto fake_end;
                     }
-                   PUSH_STATE_GOTO(CURLY_B_min, ST.B);
+                   PUSH_STATE_GOTO(CURLY_B_min, ST.B, locinput);
                }
            }
-           sayNO;
            assert(0); /* NOTREACHED */
 
 
@@ -5666,13 +5699,13 @@ NULL
            {
                UV c = 0;
                if (ST.c1 != CHRTEST_VOID)
-                   c = utf8_target ? utf8n_to_uvchr((U8*)PL_reginput,
+                   c = utf8_target ? utf8n_to_uvchr((U8*)locinput,
                                           UTF8_MAXBYTES, 0, uniflags)
-                               : (UV) UCHARAT(PL_reginput);
+                               : (UV) UCHARAT(locinput);
                /* If it could work, try it. */
                if (ST.c1 == CHRTEST_VOID || c == (UV)ST.c1 || c == (UV)ST.c2) {
                    CURLY_SETPAREN(ST.paren, ST.count);
-                   PUSH_STATE_GOTO(CURLY_B_max, ST.B);
+                   PUSH_STATE_GOTO(CURLY_B_max, ST.B, locinput);
                    assert(0); /* NOTREACHED */
                }
            }
@@ -5687,7 +5720,7 @@ NULL
            /*  back up. */
            if (--ST.count < ST.min)
                sayNO;
-           PL_reginput = locinput = HOPc(locinput, -1);
+           locinput = HOPc(locinput, -1);
            goto curly_try_B_max;
 
 #undef ST
@@ -5709,7 +5742,6 @@ NULL
                cur_curlyx = cur_eval->u.eval.prev_curlyx;
 
                REGCP_SET(st->u.eval.lastcp);
-               PL_reginput = locinput;
 
                /* Restore parens of the outer rex without popping the
                 * savestack */
@@ -5723,8 +5755,8 @@ NULL
                 if ( nochange_depth )
                    nochange_depth--;
 
-                PUSH_YES_STATE_GOTO(EVAL_AB,
-                       st->u.eval.prev_eval->u.eval.B); /* match B */
+                PUSH_YES_STATE_GOTO(EVAL_AB, st->u.eval.prev_eval->u.eval.B,
+                                    locinput); /* match B */
            }
 
            if (locinput < reginfo->till) {
@@ -5737,7 +5769,6 @@ NULL
                                                      
                sayNO_SILENT;           /* Cannot match: too short. */
            }
-           PL_reginput = locinput;     /* put where regtry can find it */
            sayYES;                     /* Success! */
 
        case SUCCEED: /* successful SUSPEND/UNLESSM/IFMATCH/CURLYM */
@@ -5745,15 +5776,17 @@ NULL
            PerlIO_printf(Perl_debug_log,
                "%*s  %ssubpattern success...%s\n",
                REPORT_CODE_OFF+depth*2, "", PL_colors[4], PL_colors[5]));
-           PL_reginput = locinput;     /* put where regtry can find it */
            sayYES;                     /* Success! */
 
 #undef  ST
 #define ST st->u.ifmatch
 
+        {
+            char *newstart;
+
        case SUSPEND:   /* (?>A) */
            ST.wanted = 1;
-           PL_reginput = locinput;
+           newstart = locinput;
            goto do_ifmatch;    
 
        case UNLESSM:   /* -ve lookaround: (?!A), or with flags, (?<!A) */
@@ -5778,10 +5811,10 @@ NULL
                        next = NULL;
                    break;
                }
-               PL_reginput = s;
+               newstart = s;
            }
            else
-               PL_reginput = locinput;
+               newstart = locinput;
 
          do_ifmatch:
            ST.me = scan;
@@ -5789,8 +5822,9 @@ NULL
            logical = 0; /* XXX: reset state of logical once it has been saved into ST */
            
            /* execute body of (?...A) */
-           PUSH_YES_STATE_GOTO(IFMATCH_A, NEXTOPER(NEXTOPER(scan)));
+           PUSH_YES_STATE_GOTO(IFMATCH_A, NEXTOPER(NEXTOPER(scan)), newstart);
            assert(0); /* NOTREACHED */
+        }
 
        case IFMATCH_A_fail: /* body of (?...A) failed */
            ST.wanted = !ST.wanted;
@@ -5803,10 +5837,9 @@ NULL
            else if (!ST.wanted)
                sayNO;
 
-           if (OP(ST.me) == SUSPEND)
-               locinput = PL_reginput;
-           else {
-               locinput = PL_reginput = st->locinput;
+           if (OP(ST.me) != SUSPEND) {
+                /* restore old position except for (?>...) */
+               locinput = st->locinput;
                nextchr = UCHARAT(locinput);
            }
            scan = ST.me + ARG(ST.me);
@@ -5825,10 +5858,9 @@ NULL
            reginfo->cutpoint = PL_regeol;
            /* FALLTHROUGH */
        case PRUNE:
-           PL_reginput = locinput;
            if (!scan->flags)
                sv_yes_mark = sv_commit = MUTABLE_SV(rexi->data->data[ ARG( scan ) ]);
-           PUSH_STATE_GOTO(COMMIT_next,next);
+           PUSH_STATE_GOTO(COMMIT_next, next, locinput);
            assert(0); /* NOTREACHED */
        case COMMIT_next_fail:
            no_final = 1;    
@@ -5843,8 +5875,8 @@ NULL
             ST.mark_name = sv_commit = sv_yes_mark 
                 = MUTABLE_SV(rexi->data->data[ ARG( scan ) ]);
             mark_state = st;
-            ST.mark_loc = PL_reginput = locinput;
-            PUSH_YES_STATE_GOTO(MARKPOINT_next,next);
+            ST.mark_loc = locinput;
+            PUSH_YES_STATE_GOTO(MARKPOINT_next, next, locinput);
             assert(0); /* NOTREACHED */
         case MARKPOINT_next:
             mark_state = ST.prev_mark;
@@ -5871,12 +5903,11 @@ NULL
             sayNO;
             assert(0); /* NOTREACHED */
         case SKIP:
-            PL_reginput = locinput;
             if (scan->flags) {
                 /* (*SKIP) : if we fail we cut here*/
                 ST.mark_name = NULL;
                 ST.mark_loc = locinput;
-                PUSH_STATE_GOTO(SKIP_next,next);    
+                PUSH_STATE_GOTO(SKIP_next,next, locinput);
             } else {
                 /* (*SKIP:NAME) : if there is a (*MARK:NAME) fail where it was, 
                    otherwise do nothing.  Meaning we need to scan 
@@ -5889,7 +5920,7 @@ NULL
                                 find ) ) 
                     {
                         ST.mark_name = find;
-                        PUSH_STATE_GOTO( SKIP_next, next );
+                        PUSH_STATE_GOTO( SKIP_next, next, locinput);
                     }
                     cur = cur->u.mark.prev_mark;
                 }
@@ -5998,7 +6029,7 @@ NULL
                newst = S_push_slab(aTHX);
            PL_regmatch_state = newst;
 
-           locinput = PL_reginput;
+           locinput = pushinput;
            nextchr = UCHARAT(locinput);
            st = newst;
            continue;
@@ -6137,19 +6168,22 @@ no_silent:
     /* clean up; in particular, free all slabs above current one */
     LEAVE_SCOPE(oldsave);
 
-    return result;
+    assert(!result ||  locinput - PL_bostr >= 0);
+    return result ?  locinput - PL_bostr : -1;
 }
 
 /*
  - regrepeat - repeatedly match something simple, report how many
- */
-/*
- * [This routine now assumes that it will only match on things of length 1.
- * That was true before, but now we assume scan - reginput is the count,
- * rather than incrementing count on every character.  [Er, except utf8.]]
+ *
+ * startposp - pointer a pointer to the start position.  This is updated
+ *             to point to the byte following the highest successful
+ *             match.
+ * p         - the regnode to be repeatedly matched against.
+ * max       - maximum number of characters to match.
+ * depth     - (for debugging) backtracking depth.
  */
 STATIC I32
-S_regrepeat(pTHX_ const regexp *prog, const regnode *p, I32 max, int depth)
+S_regrepeat(pTHX_ const regexp *prog, char **startposp, const regnode *p, I32 max, int depth)
 {
     dVAR;
     char *scan;
@@ -6164,7 +6198,7 @@ S_regrepeat(pTHX_ const regexp *prog, const regnode *p, I32 max, int depth)
 
     PERL_ARGS_ASSERT_REGREPEAT;
 
-    scan = PL_reginput;
+    scan = *startposp;
     if (max == REG_INFTY)
        max = I32_MAX;
     else if (max < loceol - scan)
@@ -6688,8 +6722,8 @@ S_regrepeat(pTHX_ const regexp *prog, const regnode *p, I32 max, int depth)
     if (hardcount)
        c = hardcount;
     else
-       c = scan - PL_reginput;
-    PL_reginput = scan;
+       c = scan - *startposp;
+    *startposp = scan;
 
     DEBUG_r({
        GET_RE_DEBUG_FLAGS_DECL;