This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Fix multicharacter titlecase (ucfirst).
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index 59c67e9..3d93f75 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -1,6 +1,6 @@
 /*    pp.c
  *
 /*    pp.c
  *
- *    Copyright (c) 1991-2000, Larry Wall
+ *    Copyright (c) 1991-2001, Larry Wall
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
 #define PERL_IN_PP_C
 #include "perl.h"
 
 #define PERL_IN_PP_C
 #include "perl.h"
 
-/*
- * The compiler on Concurrent CX/UX systems has a subtle bug which only
- * seems to show up when compiling pp.c - it generates the wrong double
- * precision constant value for (double)UV_MAX when used inline in the body
- * of the code below, so this makes a static variable up front (which the
- * compiler seems to get correct) and uses it in place of UV_MAX below.
- */
-#ifdef CXUX_BROKEN_CONSTANT_CONVERT
-static double UV_MAX_cxux = ((double)UV_MAX);
-#endif
-
-/*
- * Offset for integer pack/unpack.
- *
- * On architectures where I16 and I32 aren't really 16 and 32 bits,
- * which for now are all Crays, pack and unpack have to play games.
- */
-
-/*
- * These values are required for portability of pack() output.
- * If they're not right on your machine, then pack() and unpack()
- * wouldn't work right anyway; you'll need to apply the Cray hack.
- * (I'd like to check them with #if, but you can't use sizeof() in
- * the preprocessor.)  --???
- */
-/*
-    The appropriate SHORTSIZE, INTSIZE, LONGSIZE, and LONGLONGSIZE
-    defines are now in config.h.  --Andy Dougherty  April 1998
- */
-#define SIZE16 2
-#define SIZE32 4
-
-/* CROSSCOMPILE and MULTIARCH are going to affect pp_pack() and pp_unpack().
-   --jhi Feb 1999 */
-
-#if SHORTSIZE != SIZE16 || LONGSIZE != SIZE32
-#   define PERL_NATINT_PACK
-#endif
-
-#if LONGSIZE > 4 && defined(_CRAY)
-#  if BYTEORDER == 0x12345678
-#    define OFF16(p)   (char*)(p)
-#    define OFF32(p)   (char*)(p)
-#  else
-#    if BYTEORDER == 0x87654321
-#      define OFF16(p) ((char*)(p) + (sizeof(U16) - SIZE16))
-#      define OFF32(p) ((char*)(p) + (sizeof(U32) - SIZE32))
-#    else
-       }}}} bad cray byte order
-#    endif
-#  endif
-#  define COPY16(s,p)  (*(p) = 0, Copy(s, OFF16(p), SIZE16, char))
-#  define COPY32(s,p)  (*(p) = 0, Copy(s, OFF32(p), SIZE32, char))
-#  define COPYNN(s,p,n) (*(p) = 0, Copy(s, (char *)(p), n, char))
-#  define CAT16(sv,p)  sv_catpvn(sv, OFF16(p), SIZE16)
-#  define CAT32(sv,p)  sv_catpvn(sv, OFF32(p), SIZE32)
-#else
-#  define COPY16(s,p)  Copy(s, p, SIZE16, char)
-#  define COPY32(s,p)  Copy(s, p, SIZE32, char)
-#  define COPYNN(s,p,n) Copy(s, (char *)(p), n, char)
-#  define CAT16(sv,p)  sv_catpvn(sv, (char*)(p), SIZE16)
-#  define CAT32(sv,p)  sv_catpvn(sv, (char*)(p), SIZE32)
-#endif
-
 /* variations on pp_null */
 
 /* variations on pp_null */
 
-#ifdef I_UNISTD
-#include <unistd.h>
-#endif
-
 /* XXX I can't imagine anyone who doesn't have this actually _needs_
    it, since pid_t is an integral type.
    --AD  2/20/1998
 /* XXX I can't imagine anyone who doesn't have this actually _needs_
    it, since pid_t is an integral type.
    --AD  2/20/1998
@@ -96,7 +28,7 @@ extern Pid_t getpid (void);
 
 PP(pp_stub)
 {
 
 PP(pp_stub)
 {
-    djSP;
+    dSP;
     if (GIMME_V == G_SCALAR)
        XPUSHs(&PL_sv_undef);
     RETURN;
     if (GIMME_V == G_SCALAR)
        XPUSHs(&PL_sv_undef);
     RETURN;
@@ -111,13 +43,18 @@ PP(pp_scalar)
 
 PP(pp_padav)
 {
 
 PP(pp_padav)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     if (PL_op->op_private & OPpLVAL_INTRO)
        SAVECLEARSV(PL_curpad[PL_op->op_targ]);
     EXTEND(SP, 1);
     if (PL_op->op_flags & OPf_REF) {
        PUSHs(TARG);
        RETURN;
     if (PL_op->op_private & OPpLVAL_INTRO)
        SAVECLEARSV(PL_curpad[PL_op->op_targ]);
     EXTEND(SP, 1);
     if (PL_op->op_flags & OPf_REF) {
        PUSHs(TARG);
        RETURN;
+    } else if (LVRET) {
+       if (GIMME == G_SCALAR)
+           Perl_croak(aTHX_ "Can't return array to lvalue scalar context");
+       PUSHs(TARG);
+       RETURN;
     }
     if (GIMME == G_ARRAY) {
        I32 maxarg = AvFILL((AV*)TARG) + 1;
     }
     if (GIMME == G_ARRAY) {
        I32 maxarg = AvFILL((AV*)TARG) + 1;
@@ -145,7 +82,7 @@ PP(pp_padav)
 
 PP(pp_padhv)
 {
 
 PP(pp_padhv)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     I32 gimme;
 
     XPUSHs(TARG);
     I32 gimme;
 
     XPUSHs(TARG);
@@ -153,6 +90,11 @@ PP(pp_padhv)
        SAVECLEARSV(PL_curpad[PL_op->op_targ]);
     if (PL_op->op_flags & OPf_REF)
        RETURN;
        SAVECLEARSV(PL_curpad[PL_op->op_targ]);
     if (PL_op->op_flags & OPf_REF)
        RETURN;
+    else if (LVRET) {
+       if (GIMME == G_SCALAR)
+           Perl_croak(aTHX_ "Can't return hash to lvalue scalar context");
+       RETURN;
+    }
     gimme = GIMME_V;
     if (gimme == G_ARRAY) {
        RETURNOP(do_kv());
     gimme = GIMME_V;
     if (gimme == G_ARRAY) {
        RETURNOP(do_kv());
@@ -178,7 +120,7 @@ PP(pp_padany)
 
 PP(pp_rv2gv)
 {
 
 PP(pp_rv2gv)
 {
-    djSP; dTOPss;
+    dSP; dTOPss;
 
     if (SvROK(sv)) {
       wasref:
 
     if (SvROK(sv)) {
       wasref:
@@ -264,7 +206,7 @@ PP(pp_rv2gv)
 
 PP(pp_rv2sv)
 {
 
 PP(pp_rv2sv)
 {
-    djSP; dTOPss;
+    dSP; dTOPss;
 
     if (SvROK(sv)) {
       wasref:
 
     if (SvROK(sv)) {
       wasref:
@@ -329,13 +271,13 @@ PP(pp_rv2sv)
 
 PP(pp_av2arylen)
 {
 
 PP(pp_av2arylen)
 {
-    djSP;
+    dSP;
     AV *av = (AV*)TOPs;
     SV *sv = AvARYLEN(av);
     if (!sv) {
        AvARYLEN(av) = sv = NEWSV(0,0);
        sv_upgrade(sv, SVt_IV);
     AV *av = (AV*)TOPs;
     SV *sv = AvARYLEN(av);
     if (!sv) {
        AvARYLEN(av) = sv = NEWSV(0,0);
        sv_upgrade(sv, SVt_IV);
-       sv_magic(sv, (SV*)av, '#', Nullch, 0);
+       sv_magic(sv, (SV*)av, PERL_MAGIC_arylen, Nullch, 0);
     }
     SETs(sv);
     RETURN;
     }
     SETs(sv);
     RETURN;
@@ -343,12 +285,12 @@ PP(pp_av2arylen)
 
 PP(pp_pos)
 {
 
 PP(pp_pos)
 {
-    djSP; dTARGET; dPOPss;
+    dSP; dTARGET; dPOPss;
 
 
-    if (PL_op->op_flags & OPf_MOD) {
+    if (PL_op->op_flags & OPf_MOD || LVRET) {
        if (SvTYPE(TARG) < SVt_PVLV) {
            sv_upgrade(TARG, SVt_PVLV);
        if (SvTYPE(TARG) < SVt_PVLV) {
            sv_upgrade(TARG, SVt_PVLV);
-           sv_magic(TARG, Nullsv, '.', Nullch, 0);
+           sv_magic(TARG, Nullsv, PERL_MAGIC_pos, Nullch, 0);
        }
 
        LvTYPE(TARG) = '.';
        }
 
        LvTYPE(TARG) = '.';
@@ -364,7 +306,7 @@ PP(pp_pos)
        MAGIC* mg;
 
        if (SvTYPE(sv) >= SVt_PVMG && SvMAGIC(sv)) {
        MAGIC* mg;
 
        if (SvTYPE(sv) >= SVt_PVMG && SvMAGIC(sv)) {
-           mg = mg_find(sv, 'g');
+           mg = mg_find(sv, PERL_MAGIC_regex_global);
            if (mg && mg->mg_len >= 0) {
                I32 i = mg->mg_len;
                if (DO_UTF8(sv))
            if (mg && mg->mg_len >= 0) {
                I32 i = mg->mg_len;
                if (DO_UTF8(sv))
@@ -379,7 +321,7 @@ PP(pp_pos)
 
 PP(pp_rv2cv)
 {
 
 PP(pp_rv2cv)
 {
-    djSP;
+    dSP;
     GV *gv;
     HV *stash;
 
     GV *gv;
     HV *stash;
 
@@ -389,8 +331,12 @@ PP(pp_rv2cv)
     if (cv) {
        if (CvCLONE(cv))
            cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
     if (cv) {
        if (CvCLONE(cv))
            cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
-       if ((PL_op->op_private & OPpLVAL_INTRO) && !CvLVALUE(cv))
-           DIE(aTHX_ "Can't modify non-lvalue subroutine call");
+       if ((PL_op->op_private & OPpLVAL_INTRO)) {
+           if (gv && GvCV(gv) == cv && (gv = gv_autoload4(GvSTASH(gv), GvNAME(gv), GvNAMELEN(gv), FALSE)))
+               cv = GvCV(gv);
+           if (!CvLVALUE(cv))
+               DIE(aTHX_ "Can't modify non-lvalue subroutine call");
+       }
     }
     else
        cv = (CV*)&PL_sv_undef;
     }
     else
        cv = (CV*)&PL_sv_undef;
@@ -400,7 +346,7 @@ PP(pp_rv2cv)
 
 PP(pp_prototype)
 {
 
 PP(pp_prototype)
 {
-    djSP;
+    dSP;
     CV *cv;
     HV *stash;
     GV *gv;
     CV *cv;
     HV *stash;
     GV *gv;
@@ -431,17 +377,19 @@ PP(pp_prototype)
              found:
                oa = PL_opargs[i] >> OASHIFT;
                while (oa) {
              found:
                oa = PL_opargs[i] >> OASHIFT;
                while (oa) {
-                   if (oa & OA_OPTIONAL) {
+                   if (oa & OA_OPTIONAL && !seen_question) {
                        seen_question = 1;
                        str[n++] = ';';
                    }
                    else if (n && str[0] == ';' && seen_question)
                        goto set;       /* XXXX system, exec */
                    if ((oa & (OA_OPTIONAL - 1)) >= OA_AVREF
                        seen_question = 1;
                        str[n++] = ';';
                    }
                    else if (n && str[0] == ';' && seen_question)
                        goto set;       /* XXXX system, exec */
                    if ((oa & (OA_OPTIONAL - 1)) >= OA_AVREF
-                       && (oa & (OA_OPTIONAL - 1)) <= OA_HVREF) {
+                       && (oa & (OA_OPTIONAL - 1)) <= OA_SCALARREF
+                       /* But globs are already references (kinda) */
+                       && (oa & (OA_OPTIONAL - 1)) != OA_FILEREF
+                   ) {
                        str[n++] = '\\';
                    }
                        str[n++] = '\\';
                    }
-                   /* What to do with R ((un)tie, tied, (sys)read, recv)? */
                    str[n++] = ("?$@@%&*$")[oa & (OA_OPTIONAL - 1)];
                    oa = oa >> 4;
                }
                    str[n++] = ("?$@@%&*$")[oa & (OA_OPTIONAL - 1)];
                    oa = oa >> 4;
                }
@@ -466,7 +414,7 @@ PP(pp_prototype)
 
 PP(pp_anoncode)
 {
 
 PP(pp_anoncode)
 {
-    djSP;
+    dSP;
     CV* cv = (CV*)PL_curpad[PL_op->op_targ];
     if (CvCLONE(cv))
        cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
     CV* cv = (CV*)PL_curpad[PL_op->op_targ];
     if (CvCLONE(cv))
        cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
@@ -477,14 +425,14 @@ PP(pp_anoncode)
 
 PP(pp_srefgen)
 {
 
 PP(pp_srefgen)
 {
-    djSP;
+    dSP;
     *SP = refto(*SP);
     RETURN;
 }
 
 PP(pp_refgen)
 {
     *SP = refto(*SP);
     RETURN;
 }
 
 PP(pp_refgen)
 {
-    djSP; dMARK;
+    dSP; dMARK;
     if (GIMME != G_ARRAY) {
        if (++MARK <= SP)
            *MARK = *SP;
     if (GIMME != G_ARRAY) {
        if (++MARK <= SP)
            *MARK = *SP;
@@ -519,8 +467,8 @@ S_refto(pTHX_ SV *sv)
        SvTEMP_off(sv);
        (void)SvREFCNT_inc(sv);
     }
        SvTEMP_off(sv);
        (void)SvREFCNT_inc(sv);
     }
-    else if (SvPADTMP(sv))
-       sv = newSVsv(sv);
+    else if (SvPADTMP(sv) && !IS_PADGV(sv))
+        sv = newSVsv(sv);
     else {
        SvTEMP_off(sv);
        (void)SvREFCNT_inc(sv);
     else {
        SvTEMP_off(sv);
        (void)SvREFCNT_inc(sv);
@@ -534,7 +482,7 @@ S_refto(pTHX_ SV *sv)
 
 PP(pp_ref)
 {
 
 PP(pp_ref)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     SV *sv;
     char *pv;
 
     SV *sv;
     char *pv;
 
@@ -554,7 +502,7 @@ PP(pp_ref)
 
 PP(pp_bless)
 {
 
 PP(pp_bless)
 {
-    djSP;
+    dSP;
     HV *stash;
 
     if (MAXARG == 1)
     HV *stash;
 
     if (MAXARG == 1)
@@ -583,7 +531,7 @@ PP(pp_gelem)
     SV *sv;
     SV *tmpRef;
     char *elem;
     SV *sv;
     SV *tmpRef;
     char *elem;
-    djSP;
+    dSP;
     STRLEN n_a;
 
     sv = POPs;
     STRLEN n_a;
 
     sv = POPs;
@@ -602,8 +550,11 @@ PP(pp_gelem)
            tmpRef = (SV*)GvCVu(gv);
        break;
     case 'F':
            tmpRef = (SV*)GvCVu(gv);
        break;
     case 'F':
-       if (strEQ(elem, "FILEHANDLE")) /* XXX deprecate in 5.005 */
+       if (strEQ(elem, "FILEHANDLE")) {
+           /* finally deprecated in 5.8.0 */
+           deprecate("*glob{FILEHANDLE}");
            tmpRef = (SV*)GvIOp(gv);
            tmpRef = (SV*)GvIOp(gv);
+       }
        else
        if (strEQ(elem, "FORMAT"))
            tmpRef = (SV*)GvFORM(gv);
        else
        if (strEQ(elem, "FORMAT"))
            tmpRef = (SV*)GvFORM(gv);
@@ -647,7 +598,7 @@ PP(pp_gelem)
 
 PP(pp_study)
 {
 
 PP(pp_study)
 {
-    djSP; dPOPss;
+    dSP; dPOPss;
     register unsigned char *s;
     register I32 pos;
     register I32 ch;
     register unsigned char *s;
     register I32 pos;
     register I32 ch;
@@ -703,13 +654,14 @@ PP(pp_study)
     }
 
     SvSCREAM_on(sv);
     }
 
     SvSCREAM_on(sv);
-    sv_magic(sv, Nullsv, 'g', Nullch, 0);      /* piggyback on m//g magic */
+    /* piggyback on m//g magic */
+    sv_magic(sv, Nullsv, PERL_MAGIC_regex_global, Nullch, 0);
     RETPUSHYES;
 }
 
 PP(pp_trans)
 {
     RETPUSHYES;
 }
 
 PP(pp_trans)
 {
-    djSP; dTARG;
+    dSP; dTARG;
     SV *sv;
 
     if (PL_op->op_flags & OPf_STACKED)
     SV *sv;
 
     if (PL_op->op_flags & OPf_STACKED)
@@ -727,7 +679,7 @@ PP(pp_trans)
 
 PP(pp_schop)
 {
 
 PP(pp_schop)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     do_chop(TARG, TOPs);
     SETTARG;
     RETURN;
     do_chop(TARG, TOPs);
     SETTARG;
     RETURN;
@@ -735,23 +687,24 @@ PP(pp_schop)
 
 PP(pp_chop)
 {
 
 PP(pp_chop)
 {
-    djSP; dMARK; dTARGET;
-    while (SP > MARK)
-       do_chop(TARG, POPs);
+    dSP; dMARK; dTARGET; dORIGMARK;
+    while (MARK < SP)
+       do_chop(TARG, *++MARK);
+    SP = ORIGMARK;
     PUSHTARG;
     RETURN;
 }
 
 PP(pp_schomp)
 {
     PUSHTARG;
     RETURN;
 }
 
 PP(pp_schomp)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     SETi(do_chomp(TOPs));
     RETURN;
 }
 
 PP(pp_chomp)
 {
     SETi(do_chomp(TOPs));
     RETURN;
 }
 
 PP(pp_chomp)
 {
-    djSP; dMARK; dTARGET;
+    dSP; dMARK; dTARGET;
     register I32 count = 0;
 
     while (SP > MARK)
     register I32 count = 0;
 
     while (SP > MARK)
@@ -762,7 +715,7 @@ PP(pp_chomp)
 
 PP(pp_defined)
 {
 
 PP(pp_defined)
 {
-    djSP;
+    dSP;
     register SV* sv;
 
     sv = POPs;
     register SV* sv;
 
     sv = POPs;
@@ -770,11 +723,13 @@ PP(pp_defined)
        RETPUSHNO;
     switch (SvTYPE(sv)) {
     case SVt_PVAV:
        RETPUSHNO;
     switch (SvTYPE(sv)) {
     case SVt_PVAV:
-       if (AvMAX(sv) >= 0 || SvGMAGICAL(sv) || (SvRMAGICAL(sv) && mg_find(sv,'P')))
+       if (AvMAX(sv) >= 0 || SvGMAGICAL(sv)
+               || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
            RETPUSHYES;
        break;
     case SVt_PVHV:
            RETPUSHYES;
        break;
     case SVt_PVHV:
-       if (HvARRAY(sv) || SvGMAGICAL(sv) || (SvRMAGICAL(sv) && mg_find(sv,'P')))
+       if (HvARRAY(sv) || SvGMAGICAL(sv)
+               || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied)))
            RETPUSHYES;
        break;
     case SVt_PVCV:
            RETPUSHYES;
        break;
     case SVt_PVCV:
@@ -792,7 +747,7 @@ PP(pp_defined)
 
 PP(pp_undef)
 {
 
 PP(pp_undef)
 {
-    djSP;
+    dSP;
     SV *sv;
 
     if (!PL_op->op_private) {
     SV *sv;
 
     if (!PL_op->op_private) {
@@ -824,7 +779,7 @@ PP(pp_undef)
     case SVt_PVFM:
        {
            /* let user-undef'd sub keep its identity */
     case SVt_PVFM:
        {
            /* let user-undef'd sub keep its identity */
-           GV* gv = (GV*)SvREFCNT_inc(CvGV((CV*)sv));
+           GV* gv = CvGV((CV*)sv);
            cv_undef((CV*)sv);
            CvGV((CV*)sv) = gv;
        }
            cv_undef((CV*)sv);
            CvGV((CV*)sv) = gv;
        }
@@ -859,7 +814,7 @@ PP(pp_undef)
 
 PP(pp_predec)
 {
 
 PP(pp_predec)
 {
-    djSP;
+    dSP;
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
@@ -876,7 +831,7 @@ PP(pp_predec)
 
 PP(pp_postinc)
 {
 
 PP(pp_postinc)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
@@ -897,7 +852,7 @@ PP(pp_postinc)
 
 PP(pp_postdec)
 {
 
 PP(pp_postdec)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
     if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
@@ -918,7 +873,7 @@ PP(pp_postdec)
 
 PP(pp_pow)
 {
 
 PP(pp_pow)
 {
-    djSP; dATARGET; tryAMAGICbin(pow,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(pow,opASSIGN);
     {
       dPOPTOPnnrl;
       SETn( Perl_pow( left, right) );
     {
       dPOPTOPnnrl;
       SETn( Perl_pow( left, right) );
@@ -928,7 +883,115 @@ PP(pp_pow)
 
 PP(pp_multiply)
 {
 
 PP(pp_multiply)
 {
-    djSP; dATARGET; tryAMAGICbin(mult,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(mult,opASSIGN);
+#ifdef PERL_PRESERVE_IVUV
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       /* Unless the left argument is integer in range we are going to have to
+          use NV maths. Hence only attempt to coerce the right argument if
+          we know the left is integer.  */
+       /* Left operand is defined, so is it IV? */
+       SvIV_please(TOPm1s);
+       if (SvIOK(TOPm1s)) {
+           bool auvok = SvUOK(TOPm1s);
+           bool buvok = SvUOK(TOPs);
+           const UV topmask = (~ (UV)0) << (4 * sizeof (UV));
+           const UV botmask = ~((~ (UV)0) << (4 * sizeof (UV)));
+           UV alow;
+           UV ahigh;
+           UV blow;
+           UV bhigh;
+
+           if (auvok) {
+               alow = SvUVX(TOPm1s);
+           } else {
+               IV aiv = SvIVX(TOPm1s);
+               if (aiv >= 0) {
+                   alow = aiv;
+                   auvok = TRUE; /* effectively it's a UV now */
+               } else {
+                   alow = -aiv; /* abs, auvok == false records sign */
+               }
+           }
+           if (buvok) {
+               blow = SvUVX(TOPs);
+           } else {
+               IV biv = SvIVX(TOPs);
+               if (biv >= 0) {
+                   blow = biv;
+                   buvok = TRUE; /* effectively it's a UV now */
+               } else {
+                   blow = -biv; /* abs, buvok == false records sign */
+               }
+           }
+
+           /* If this does sign extension on unsigned it's time for plan B  */
+           ahigh = alow >> (4 * sizeof (UV));
+           alow &= botmask;
+           bhigh = blow >> (4 * sizeof (UV));
+           blow &= botmask;
+           if (ahigh && bhigh) {
+               /* eg 32 bit is at least 0x10000 * 0x10000 == 0x100000000
+                  which is overflow. Drop to NVs below.  */
+           } else if (!ahigh && !bhigh) {
+               /* eg 32 bit is at most 0xFFFF * 0xFFFF == 0xFFFE0001
+                  so the unsigned multiply cannot overflow.  */
+               UV product = alow * blow;
+               if (auvok == buvok) {
+                   /* -ve * -ve or +ve * +ve gives a +ve result.  */
+                   SP--;
+                   SETu( product );
+                   RETURN;
+               } else if (product <= (UV)IV_MIN) {
+                   /* 2s complement assumption that (UV)-IV_MIN is correct.  */
+                   /* -ve result, which could overflow an IV  */
+                   SP--;
+                   SETi( -(IV)product );
+                   RETURN;
+               } /* else drop to NVs below. */
+           } else {
+               /* One operand is large, 1 small */
+               UV product_middle;
+               if (bhigh) {
+                   /* swap the operands */
+                   ahigh = bhigh;
+                   bhigh = blow; /* bhigh now the temp var for the swap */
+                   blow = alow;
+                   alow = bhigh;
+               }
+               /* now, ((ahigh * blow) << half_UV_len) + (alow * blow)
+                  multiplies can't overflow. shift can, add can, -ve can.  */
+               product_middle = ahigh * blow;
+               if (!(product_middle & topmask)) {
+                   /* OK, (ahigh * blow) won't lose bits when we shift it.  */
+                   UV product_low;
+                   product_middle <<= (4 * sizeof (UV));
+                   product_low = alow * blow;
+
+                   /* as for pp_add, UV + something mustn't get smaller.
+                      IIRC ANSI mandates this wrapping *behaviour* for
+                      unsigned whatever the actual representation*/
+                   product_low += product_middle;
+                   if (product_low >= product_middle) {
+                       /* didn't overflow */
+                       if (auvok == buvok) {
+                           /* -ve * -ve or +ve * +ve gives a +ve result.  */
+                           SP--;
+                           SETu( product_low );
+                           RETURN;
+                       } else if (product_low <= (UV)IV_MIN) {
+                           /* 2s complement assumption again  */
+                           /* -ve result, which could overflow an IV  */
+                           SP--;
+                           SETi( -(IV)product_low );
+                           RETURN;
+                       } /* else drop to NVs below. */
+                   }
+               } /* product_middle too large */
+           } /* ahigh && bhigh */
+       } /* SvIOK(TOPm1s) */
+    } /* SvIOK(TOPs) */
+#endif
     {
       dPOPTOPnnrl;
       SETn( left * right );
     {
       dPOPTOPnnrl;
       SETn( left * right );
@@ -938,97 +1001,208 @@ PP(pp_multiply)
 
 PP(pp_divide)
 {
 
 PP(pp_divide)
 {
-    djSP; dATARGET; tryAMAGICbin(div,opASSIGN);
-    {
-      dPOPPOPnnrl;
-      NV value;
-      if (right == 0.0)
-       DIE(aTHX_ "Illegal division by zero");
+    dSP; dATARGET; tryAMAGICbin(div,opASSIGN);
+    /* Only try to do UV divide first
+       if ((SLOPPYDIVIDE is true) or 
+           (PERL_PRESERVE_IVUV is true and one or both SV is a UV too large
+            to preserve))
+       The assumption is that it is better to use floating point divide
+       whenever possible, only doing integer divide first if we can't be sure.
+       If NV_PRESERVES_UV is true then we know at compile time that no UV
+       can be too large to preserve, so don't need to compile the code to
+       test the size of UVs.  */
+
 #ifdef SLOPPYDIVIDE
 #ifdef SLOPPYDIVIDE
-      /* insure that 20./5. == 4. */
-      {
-       IV k;
-       if ((NV)I_V(left)  == left &&
-           (NV)I_V(right) == right &&
-           (k = I_V(left)/I_V(right))*I_V(right) == I_V(left)) {
-           value = k;
-       }
-       else {
-           value = left / right;
-       }
-      }
+#  define PERL_TRY_UV_DIVIDE
+    /* ensure that 20./5. == 4. */
 #else
 #else
-      value = left / right;
+#  ifdef PERL_PRESERVE_IVUV
+#    ifndef NV_PRESERVES_UV
+#      define PERL_TRY_UV_DIVIDE
+#    endif
+#  endif
 #endif
 #endif
-      PUSHn( value );
-      RETURN;
+
+#ifdef PERL_TRY_UV_DIVIDE
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+        SvIV_please(TOPm1s);
+        if (SvIOK(TOPm1s)) {
+            bool left_non_neg = SvUOK(TOPm1s);
+            bool right_non_neg = SvUOK(TOPs);
+            UV left;
+            UV right;
+
+            if (right_non_neg) {
+                right = SvUVX(TOPs);
+            }
+           else {
+                IV biv = SvIVX(TOPs);
+                if (biv >= 0) {
+                    right = biv;
+                    right_non_neg = TRUE; /* effectively it's a UV now */
+                }
+               else {
+                    right = -biv;
+                }
+            }
+            /* historically undef()/0 gives a "Use of uninitialized value"
+               warning before dieing, hence this test goes here.
+               If it were immediately before the second SvIV_please, then
+               DIE() would be invoked before left was even inspected, so
+               no inpsection would give no warning.  */
+            if (right == 0)
+                DIE(aTHX_ "Illegal division by zero");
+
+            if (left_non_neg) {
+                left = SvUVX(TOPm1s);
+            }
+           else {
+                IV aiv = SvIVX(TOPm1s);
+                if (aiv >= 0) {
+                    left = aiv;
+                    left_non_neg = TRUE; /* effectively it's a UV now */
+                }
+               else {
+                    left = -aiv;
+                }
+            }
+
+            if (left >= right
+#ifdef SLOPPYDIVIDE
+                /* For sloppy divide we always attempt integer division.  */
+#else
+                /* Otherwise we only attempt it if either or both operands
+                   would not be preserved by an NV.  If both fit in NVs
+                   we fall through to the NV divide code below.  */
+                && ((left > ((UV)1 << NV_PRESERVES_UV_BITS))
+                    || (right > ((UV)1 << NV_PRESERVES_UV_BITS)))
+#endif
+                ) {
+                /* Integer division can't overflow, but it can be imprecise.  */
+                UV result = left / right;
+                if (result * right == left) {
+                    SP--; /* result is valid */
+                    if (left_non_neg == right_non_neg) {
+                        /* signs identical, result is positive.  */
+                        SETu( result );
+                        RETURN;
+                    }
+                    /* 2s complement assumption */
+                    if (result <= (UV)IV_MIN)
+                        SETi( -result );
+                    else {
+                        /* It's exact but too negative for IV. */
+                        SETn( -(NV)result );
+                    }
+                    RETURN;
+                } /* tried integer divide but it was not an integer result */
+            } /* else (abs(result) < 1.0) or (both UVs in range for NV) */
+        } /* left wasn't SvIOK */
+    } /* right wasn't SvIOK */
+#endif /* PERL_TRY_UV_DIVIDE */
+    {
+       dPOPPOPnnrl;
+       if (right == 0.0)
+           DIE(aTHX_ "Illegal division by zero");
+       PUSHn( left / right );
+       RETURN;
     }
 }
 
 PP(pp_modulo)
 {
     }
 }
 
 PP(pp_modulo)
 {
-    djSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
     {
     {
-       UV left;
-       UV right;
+       UV left  = 0;
+       UV right = 0;
        bool left_neg;
        bool right_neg;
        bool left_neg;
        bool right_neg;
-       bool use_double = 0;
-       NV dright;
-       NV dleft;
-
-       if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)) {
-           IV i = SvIVX(POPs);
-           right = (right_neg = (i < 0)) ? -i : i;
-       }
-       else {
+       bool use_double = FALSE;
+       bool dright_valid = FALSE;
+       NV dright = 0.0;
+       NV dleft  = 0.0;
+
+        SvIV_please(TOPs);
+        if (SvIOK(TOPs)) {
+            right_neg = !SvUOK(TOPs);
+            if (!right_neg) {
+                right = SvUVX(POPs);
+            } else {
+                IV biv = SvIVX(POPs);
+                if (biv >= 0) {
+                    right = biv;
+                    right_neg = FALSE; /* effectively it's a UV now */
+                } else {
+                    right = -biv;
+                }
+            }
+        }
+        else {
            dright = POPn;
            dright = POPn;
-           use_double = 1;
            right_neg = dright < 0;
            if (right_neg)
                dright = -dright;
            right_neg = dright < 0;
            if (right_neg)
                dright = -dright;
+            if (dright < UV_MAX_P1) {
+                right = U_V(dright);
+                dright_valid = TRUE; /* In case we need to use double below.  */
+            } else {
+                use_double = TRUE;
+            }
        }
 
        }
 
-       if (!use_double && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)) {
-           IV i = SvIVX(POPs);
-           left = (left_neg = (i < 0)) ? -i : i;
-       }
+        /* At this point use_double is only true if right is out of range for
+           a UV.  In range NV has been rounded down to nearest UV and
+           use_double false.  */
+        SvIV_please(TOPs);
+       if (!use_double && SvIOK(TOPs)) {
+            if (SvIOK(TOPs)) {
+                left_neg = !SvUOK(TOPs);
+                if (!left_neg) {
+                    left = SvUVX(POPs);
+                } else {
+                    IV aiv = SvIVX(POPs);
+                    if (aiv >= 0) {
+                        left = aiv;
+                        left_neg = FALSE; /* effectively it's a UV now */
+                    } else {
+                        left = -aiv;
+                    }
+                }
+            }
+        }
        else {
            dleft = POPn;
        else {
            dleft = POPn;
-           if (!use_double) {
-               use_double = 1;
-               dright = right;
-           }
            left_neg = dleft < 0;
            if (left_neg)
                dleft = -dleft;
            left_neg = dleft < 0;
            if (left_neg)
                dleft = -dleft;
-       }
 
 
+            /* This should be exactly the 5.6 behaviour - if left and right are
+               both in range for UV then use U_V() rather than floor.  */
+           if (!use_double) {
+                if (dleft < UV_MAX_P1) {
+                    /* right was in range, so is dleft, so use UVs not double.
+                     */
+                    left = U_V(dleft);
+                }
+                /* left is out of range for UV, right was in range, so promote
+                   right (back) to double.  */
+                else {
+                    /* The +0.5 is used in 5.6 even though it is not strictly
+                       consistent with the implicit +0 floor in the U_V()
+                       inside the #if 1. */
+                    dleft = Perl_floor(dleft + 0.5);
+                    use_double = TRUE;
+                    if (dright_valid)
+                        dright = Perl_floor(dright + 0.5);
+                    else
+                        dright = right;
+                }
+            }
+        }
        if (use_double) {
            NV dans;
 
        if (use_double) {
            NV dans;
 
-#if 1
-/* Somehow U_V is pessimized even if CASTFLAGS is 0 */
-#  if CASTFLAGS & 2
-#    define CAST_D2UV(d) U_V(d)
-#  else
-#    define CAST_D2UV(d) ((UV)(d))
-#  endif
-           /* Tried to do this only in the case DOUBLESIZE <= UV_SIZE,
-            * or, in other words, precision of UV more than of NV.
-            * But in fact the approach below turned out to be an
-            * optimization - floor() may be slow */
-           if (dright <= UV_MAX && dleft <= UV_MAX) {
-               right = CAST_D2UV(dright);
-               left  = CAST_D2UV(dleft);
-               goto do_uv;
-           }
-#endif
-
-           /* Backward-compatibility clause: */
-           dright = Perl_floor(dright + 0.5);
-           dleft  = Perl_floor(dleft + 0.5);
-
            if (!dright)
                DIE(aTHX_ "Illegal modulus zero");
 
            if (!dright)
                DIE(aTHX_ "Illegal modulus zero");
 
@@ -1042,7 +1216,6 @@ PP(pp_modulo)
        else {
            UV ans;
 
        else {
            UV ans;
 
-       do_uv:
            if (!right)
                DIE(aTHX_ "Illegal modulus zero");
 
            if (!right)
                DIE(aTHX_ "Illegal modulus zero");
 
@@ -1067,7 +1240,7 @@ PP(pp_modulo)
 
 PP(pp_repeat)
 {
 
 PP(pp_repeat)
 {
-  djSP; dATARGET; tryAMAGICbin(repeat,opASSIGN);
+  dSP; dATARGET; tryAMAGICbin(repeat,opASSIGN);
   {
     register IV count = POPi;
     if (GIMME == G_ARRAY && PL_op->op_private & OPpREPEAT_DOLIST) {
   {
     register IV count = POPi;
     if (GIMME == G_ARRAY && PL_op->op_private & OPpREPEAT_DOLIST) {
@@ -1079,8 +1252,10 @@ PP(pp_repeat)
        MEXTEND(MARK, max);
        if (count > 1) {
            while (SP > MARK) {
        MEXTEND(MARK, max);
        if (count > 1) {
            while (SP > MARK) {
-               if (*SP)
-                   SvTEMP_off((*SP));
+               if (*SP) {
+                   *SP = sv_2mortal(newSVsv(*SP));
+                   SvREADONLY_on(*SP);
+               }
                SP--;
            }
            MARK++;
                SP--;
            }
            MARK++;
@@ -1094,10 +1269,11 @@ PP(pp_repeat)
     else {     /* Note: mark already snarfed by pp_list */
        SV *tmpstr = POPs;
        STRLEN len;
     else {     /* Note: mark already snarfed by pp_list */
        SV *tmpstr = POPs;
        STRLEN len;
-       bool isutf = DO_UTF8(tmpstr);
+       bool isutf;
 
        SvSetSV(TARG, tmpstr);
        SvPV_force(TARG, len);
 
        SvSetSV(TARG, tmpstr);
        SvPV_force(TARG, len);
+       isutf = DO_UTF8(TARG);
        if (count != 1) {
            if (count < 1)
                SvCUR_set(TARG, 0);
        if (count != 1) {
            if (count < 1)
                SvCUR_set(TARG, 0);
@@ -1112,6 +1288,16 @@ PP(pp_repeat)
            (void)SvPOK_only_UTF8(TARG);
        else
            (void)SvPOK_only(TARG);
            (void)SvPOK_only_UTF8(TARG);
        else
            (void)SvPOK_only(TARG);
+
+       if (PL_op->op_private & OPpREPEAT_DOLIST) {
+           /* The parser saw this as a list repeat, and there
+              are probably several items on the stack. But we're
+              in scalar context, and there's no pp_list to save us
+              now. So drop the rest of the items -- robin@kitsite.com
+            */
+           dMARK;
+           SP = MARK;
+       }
        PUSHTARG;
     }
     RETURN;
        PUSHTARG;
     }
     RETURN;
@@ -1120,17 +1306,124 @@ PP(pp_repeat)
 
 PP(pp_subtract)
 {
 
 PP(pp_subtract)
 {
-    djSP; dATARGET; tryAMAGICbin(subtr,opASSIGN);
+    dSP; dATARGET; bool useleft; tryAMAGICbin(subtr,opASSIGN);
+    useleft = USE_LEFT(TOPm1s);
+#ifdef PERL_PRESERVE_IVUV
+    /* See comments in pp_add (in pp_hot.c) about Overflow, and how
+       "bad things" happen if you rely on signed integers wrapping.  */
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       /* Unless the left argument is integer in range we are going to have to
+          use NV maths. Hence only attempt to coerce the right argument if
+          we know the left is integer.  */
+       register UV auv = 0;
+       bool auvok = FALSE;
+       bool a_valid = 0;
+
+       if (!useleft) {
+           auv = 0;
+           a_valid = auvok = 1;
+           /* left operand is undef, treat as zero.  */
+       } else {
+           /* Left operand is defined, so is it IV? */
+           SvIV_please(TOPm1s);
+           if (SvIOK(TOPm1s)) {
+               if ((auvok = SvUOK(TOPm1s)))
+                   auv = SvUVX(TOPm1s);
+               else {
+                   register IV aiv = SvIVX(TOPm1s);
+                   if (aiv >= 0) {
+                       auv = aiv;
+                       auvok = 1;      /* Now acting as a sign flag.  */
+                   } else { /* 2s complement assumption for IV_MIN */
+                       auv = (UV)-aiv;
+                   }
+               }
+               a_valid = 1;
+           }
+       }
+       if (a_valid) {
+           bool result_good = 0;
+           UV result;
+           register UV buv;
+           bool buvok = SvUOK(TOPs);
+       
+           if (buvok)
+               buv = SvUVX(TOPs);
+           else {
+               register IV biv = SvIVX(TOPs);
+               if (biv >= 0) {
+                   buv = biv;
+                   buvok = 1;
+               } else
+                   buv = (UV)-biv;
+           }
+           /* ?uvok if value is >= 0. basically, flagged as UV if it's +ve,
+              else "IV" now, independant of how it came in.
+              if a, b represents positive, A, B negative, a maps to -A etc
+              a - b =>  (a - b)
+              A - b => -(a + b)
+              a - B =>  (a + b)
+              A - B => -(a - b)
+              all UV maths. negate result if A negative.
+              subtract if signs same, add if signs differ. */
+
+           if (auvok ^ buvok) {
+               /* Signs differ.  */
+               result = auv + buv;
+               if (result >= auv)
+                   result_good = 1;
+           } else {
+               /* Signs same */
+               if (auv >= buv) {
+                   result = auv - buv;
+                   /* Must get smaller */
+                   if (result <= auv)
+                       result_good = 1;
+               } else {
+                   result = buv - auv;
+                   if (result <= buv) {
+                       /* result really should be -(auv-buv). as its negation
+                          of true value, need to swap our result flag  */
+                       auvok = !auvok;
+                       result_good = 1;
+                   }
+               }
+           }
+           if (result_good) {
+               SP--;
+               if (auvok)
+                   SETu( result );
+               else {
+                   /* Negate result */
+                   if (result <= (UV)IV_MIN)
+                       SETi( -(IV)result );
+                   else {
+                       /* result valid, but out of range for IV.  */
+                       SETn( -(NV)result );
+                   }
+               }
+               RETURN;
+           } /* Overflow, drop through to NVs.  */
+       }
+    }
+#endif
+    useleft = USE_LEFT(TOPm1s);
     {
     {
-      dPOPTOPnnrl_ul;
-      SETn( left - right );
-      RETURN;
+       dPOPnv;
+       if (!useleft) {
+           /* left operand is undef, treat as zero - value */
+           SETn(-value);
+           RETURN;
+       }
+       SETn( TOPn - value );
+       RETURN;
     }
 }
 
 PP(pp_left_shift)
 {
     }
 }
 
 PP(pp_left_shift)
 {
-    djSP; dATARGET; tryAMAGICbin(lshift,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(lshift,opASSIGN);
     {
       IV shift = POPi;
       if (PL_op->op_private & HINT_INTEGER) {
     {
       IV shift = POPi;
       if (PL_op->op_private & HINT_INTEGER) {
@@ -1147,7 +1440,7 @@ PP(pp_left_shift)
 
 PP(pp_right_shift)
 {
 
 PP(pp_right_shift)
 {
-    djSP; dATARGET; tryAMAGICbin(rshift,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(rshift,opASSIGN);
     {
       IV shift = POPi;
       if (PL_op->op_private & HINT_INTEGER) {
     {
       IV shift = POPi;
       if (PL_op->op_private & HINT_INTEGER) {
@@ -1164,7 +1457,75 @@ PP(pp_right_shift)
 
 PP(pp_lt)
 {
 
 PP(pp_lt)
 {
-    djSP; tryAMAGICbinSET(lt,0);
+    dSP; tryAMAGICbinSET(lt,0);
+#ifdef PERL_PRESERVE_IVUV
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       SvIV_please(TOPm1s);
+       if (SvIOK(TOPm1s)) {
+           bool auvok = SvUOK(TOPm1s);
+           bool buvok = SvUOK(TOPs);
+       
+           if (!auvok && !buvok) { /* ## IV < IV ## */
+               IV aiv = SvIVX(TOPm1s);
+               IV biv = SvIVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(aiv < biv));
+               RETURN;
+           }
+           if (auvok && buvok) { /* ## UV < UV ## */
+               UV auv = SvUVX(TOPm1s);
+               UV buv = SvUVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(auv < buv));
+               RETURN;
+           }
+           if (auvok) { /* ## UV < IV ## */
+               UV auv;
+               IV biv;
+               
+               biv = SvIVX(TOPs);
+               SP--;
+               if (biv < 0) {
+                   /* As (a) is a UV, it's >=0, so it cannot be < */
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               auv = SvUVX(TOPs);
+               if (auv >= (UV) IV_MAX) {
+                   /* As (b) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               SETs(boolSV(auv < (UV)biv));
+               RETURN;
+           }
+           { /* ## IV < UV ## */
+               IV aiv;
+               UV buv;
+               
+               aiv = SvIVX(TOPm1s);
+               if (aiv < 0) {
+                   /* As (b) is a UV, it's >=0, so it must be < */
+                   SP--;
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               buv = SvUVX(TOPs);
+               SP--;
+               if (buv > (UV) IV_MAX) {
+                   /* As (a) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               SETs(boolSV((UV)aiv < buv));
+               RETURN;
+           }
+       }
+    }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn < value));
     {
       dPOPnv;
       SETs(boolSV(TOPn < value));
@@ -1174,7 +1535,75 @@ PP(pp_lt)
 
 PP(pp_gt)
 {
 
 PP(pp_gt)
 {
-    djSP; tryAMAGICbinSET(gt,0);
+    dSP; tryAMAGICbinSET(gt,0);
+#ifdef PERL_PRESERVE_IVUV
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       SvIV_please(TOPm1s);
+       if (SvIOK(TOPm1s)) {
+           bool auvok = SvUOK(TOPm1s);
+           bool buvok = SvUOK(TOPs);
+       
+           if (!auvok && !buvok) { /* ## IV > IV ## */
+               IV aiv = SvIVX(TOPm1s);
+               IV biv = SvIVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(aiv > biv));
+               RETURN;
+           }
+           if (auvok && buvok) { /* ## UV > UV ## */
+               UV auv = SvUVX(TOPm1s);
+               UV buv = SvUVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(auv > buv));
+               RETURN;
+           }
+           if (auvok) { /* ## UV > IV ## */
+               UV auv;
+               IV biv;
+               
+               biv = SvIVX(TOPs);
+               SP--;
+               if (biv < 0) {
+                   /* As (a) is a UV, it's >=0, so it must be > */
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               auv = SvUVX(TOPs);
+               if (auv > (UV) IV_MAX) {
+                   /* As (b) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               SETs(boolSV(auv > (UV)biv));
+               RETURN;
+           }
+           { /* ## IV > UV ## */
+               IV aiv;
+               UV buv;
+               
+               aiv = SvIVX(TOPm1s);
+               if (aiv < 0) {
+                   /* As (b) is a UV, it's >=0, so it cannot be > */
+                   SP--;
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               buv = SvUVX(TOPs);
+               SP--;
+               if (buv >= (UV) IV_MAX) {
+                   /* As (a) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               SETs(boolSV((UV)aiv > buv));
+               RETURN;
+           }
+       }
+    }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn > value));
     {
       dPOPnv;
       SETs(boolSV(TOPn > value));
@@ -1184,7 +1613,75 @@ PP(pp_gt)
 
 PP(pp_le)
 {
 
 PP(pp_le)
 {
-    djSP; tryAMAGICbinSET(le,0);
+    dSP; tryAMAGICbinSET(le,0);
+#ifdef PERL_PRESERVE_IVUV
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       SvIV_please(TOPm1s);
+       if (SvIOK(TOPm1s)) {
+           bool auvok = SvUOK(TOPm1s);
+           bool buvok = SvUOK(TOPs);
+       
+           if (!auvok && !buvok) { /* ## IV <= IV ## */
+               IV aiv = SvIVX(TOPm1s);
+               IV biv = SvIVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(aiv <= biv));
+               RETURN;
+           }
+           if (auvok && buvok) { /* ## UV <= UV ## */
+               UV auv = SvUVX(TOPm1s);
+               UV buv = SvUVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(auv <= buv));
+               RETURN;
+           }
+           if (auvok) { /* ## UV <= IV ## */
+               UV auv;
+               IV biv;
+               
+               biv = SvIVX(TOPs);
+               SP--;
+               if (biv < 0) {
+                   /* As (a) is a UV, it's >=0, so a cannot be <= */
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               auv = SvUVX(TOPs);
+               if (auv > (UV) IV_MAX) {
+                   /* As (b) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               SETs(boolSV(auv <= (UV)biv));
+               RETURN;
+           }
+           { /* ## IV <= UV ## */
+               IV aiv;
+               UV buv;
+               
+               aiv = SvIVX(TOPm1s);
+               if (aiv < 0) {
+                   /* As (b) is a UV, it's >=0, so a must be <= */
+                   SP--;
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               buv = SvUVX(TOPs);
+               SP--;
+               if (buv >= (UV) IV_MAX) {
+                   /* As (a) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               SETs(boolSV((UV)aiv <= buv));
+               RETURN;
+           }
+       }
+    }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn <= value));
     {
       dPOPnv;
       SETs(boolSV(TOPn <= value));
@@ -1194,7 +1691,75 @@ PP(pp_le)
 
 PP(pp_ge)
 {
 
 PP(pp_ge)
 {
-    djSP; tryAMAGICbinSET(ge,0);
+    dSP; tryAMAGICbinSET(ge,0);
+#ifdef PERL_PRESERVE_IVUV
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       SvIV_please(TOPm1s);
+       if (SvIOK(TOPm1s)) {
+           bool auvok = SvUOK(TOPm1s);
+           bool buvok = SvUOK(TOPs);
+       
+           if (!auvok && !buvok) { /* ## IV >= IV ## */
+               IV aiv = SvIVX(TOPm1s);
+               IV biv = SvIVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(aiv >= biv));
+               RETURN;
+           }
+           if (auvok && buvok) { /* ## UV >= UV ## */
+               UV auv = SvUVX(TOPm1s);
+               UV buv = SvUVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(auv >= buv));
+               RETURN;
+           }
+           if (auvok) { /* ## UV >= IV ## */
+               UV auv;
+               IV biv;
+               
+               biv = SvIVX(TOPs);
+               SP--;
+               if (biv < 0) {
+                   /* As (a) is a UV, it's >=0, so it must be >= */
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               auv = SvUVX(TOPs);
+               if (auv >= (UV) IV_MAX) {
+                   /* As (b) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               SETs(boolSV(auv >= (UV)biv));
+               RETURN;
+           }
+           { /* ## IV >= UV ## */
+               IV aiv;
+               UV buv;
+               
+               aiv = SvIVX(TOPm1s);
+               if (aiv < 0) {
+                   /* As (b) is a UV, it's >=0, so a cannot be >= */
+                   SP--;
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               buv = SvUVX(TOPs);
+               SP--;
+               if (buv > (UV) IV_MAX) {
+                   /* As (a) is an IV, it cannot be > IV_MAX */
+                   SETs(&PL_sv_no);
+                   RETURN;
+               }
+               SETs(boolSV((UV)aiv >= buv));
+               RETURN;
+           }
+       }
+    }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn >= value));
     {
       dPOPnv;
       SETs(boolSV(TOPn >= value));
@@ -1204,27 +1769,177 @@ PP(pp_ge)
 
 PP(pp_ne)
 {
 
 PP(pp_ne)
 {
-    djSP; tryAMAGICbinSET(ne,0);
-    {
-      dPOPnv;
-      SETs(boolSV(TOPn != value));
-      RETURN;
+    dSP; tryAMAGICbinSET(ne,0);
+#ifndef NV_PRESERVES_UV
+    if (SvROK(TOPs) && SvROK(TOPm1s)) {
+       SETs(boolSV(SvRV(TOPs) != SvRV(TOPm1s)));
+       RETURN;
     }
     }
-}
-
-PP(pp_ncmp)
-{
-    djSP; dTARGET; tryAMAGICbin(ncmp,0);
-    {
-      dPOPTOPnnrl;
-      I32 value;
-
-#ifdef Perl_isnan
-      if (Perl_isnan(left) || Perl_isnan(right)) {
-         SETs(&PL_sv_undef);
-         RETURN;
-       }
-      value = (left > right) - (left < right);
+#endif
+#ifdef PERL_PRESERVE_IVUV
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       SvIV_please(TOPm1s);
+       if (SvIOK(TOPm1s)) {
+           bool auvok = SvUOK(TOPm1s);
+           bool buvok = SvUOK(TOPs);
+       
+           if (!auvok && !buvok) { /* ## IV <=> IV ## */
+               IV aiv = SvIVX(TOPm1s);
+               IV biv = SvIVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(aiv != biv));
+               RETURN;
+           }
+           if (auvok && buvok) { /* ## UV != UV ## */
+               UV auv = SvUVX(TOPm1s);
+               UV buv = SvUVX(TOPs);
+               
+               SP--;
+               SETs(boolSV(auv != buv));
+               RETURN;
+           }
+           {                   /* ## Mixed IV,UV ## */
+               IV iv;
+               UV uv;
+               
+               /* != is commutative so swap if needed (save code) */
+               if (auvok) {
+                   /* swap. top of stack (b) is the iv */
+                   iv = SvIVX(TOPs);
+                   SP--;
+                   if (iv < 0) {
+                       /* As (a) is a UV, it's >0, so it cannot be == */
+                       SETs(&PL_sv_yes);
+                       RETURN;
+                   }
+                   uv = SvUVX(TOPs);
+               } else {
+                   iv = SvIVX(TOPm1s);
+                   SP--;
+                   if (iv < 0) {
+                       /* As (b) is a UV, it's >0, so it cannot be == */
+                       SETs(&PL_sv_yes);
+                       RETURN;
+                   }
+                   uv = SvUVX(*(SP+1)); /* Do I want TOPp1s() ? */
+               }
+               /* we know iv is >= 0 */
+               if (uv > (UV) IV_MAX) {
+                   SETs(&PL_sv_yes);
+                   RETURN;
+               }
+               SETs(boolSV((UV)iv != uv));
+               RETURN;
+           }
+       }
+    }
+#endif
+    {
+      dPOPnv;
+      SETs(boolSV(TOPn != value));
+      RETURN;
+    }
+}
+
+PP(pp_ncmp)
+{
+    dSP; dTARGET; tryAMAGICbin(ncmp,0);
+#ifndef NV_PRESERVES_UV
+    if (SvROK(TOPs) && SvROK(TOPm1s)) {
+       SETi(PTR2UV(SvRV(TOPs)) - PTR2UV(SvRV(TOPm1s)));
+       RETURN;
+    }
+#endif
+#ifdef PERL_PRESERVE_IVUV
+    /* Fortunately it seems NaN isn't IOK */
+    SvIV_please(TOPs);
+    if (SvIOK(TOPs)) {
+       SvIV_please(TOPm1s);
+       if (SvIOK(TOPm1s)) {
+           bool leftuvok = SvUOK(TOPm1s);
+           bool rightuvok = SvUOK(TOPs);
+           I32 value;
+           if (!leftuvok && !rightuvok) { /* ## IV <=> IV ## */
+               IV leftiv = SvIVX(TOPm1s);
+               IV rightiv = SvIVX(TOPs);
+               
+               if (leftiv > rightiv)
+                   value = 1;
+               else if (leftiv < rightiv)
+                   value = -1;
+               else
+                   value = 0;
+           } else if (leftuvok && rightuvok) { /* ## UV <=> UV ## */
+               UV leftuv = SvUVX(TOPm1s);
+               UV rightuv = SvUVX(TOPs);
+               
+               if (leftuv > rightuv)
+                   value = 1;
+               else if (leftuv < rightuv)
+                   value = -1;
+               else
+                   value = 0;
+           } else if (leftuvok) { /* ## UV <=> IV ## */
+               UV leftuv;
+               IV rightiv;
+               
+               rightiv = SvIVX(TOPs);
+               if (rightiv < 0) {
+                   /* As (a) is a UV, it's >=0, so it cannot be < */
+                   value = 1;
+               } else {
+                   leftuv = SvUVX(TOPm1s);
+                   if (leftuv > (UV) IV_MAX) {
+                       /* As (b) is an IV, it cannot be > IV_MAX */
+                       value = 1;
+                   } else if (leftuv > (UV)rightiv) {
+                       value = 1;
+                   } else if (leftuv < (UV)rightiv) {
+                       value = -1;
+                   } else {
+                       value = 0;
+                   }
+               }
+           } else { /* ## IV <=> UV ## */
+               IV leftiv;
+               UV rightuv;
+               
+               leftiv = SvIVX(TOPm1s);
+               if (leftiv < 0) {
+                   /* As (b) is a UV, it's >=0, so it must be < */
+                   value = -1;
+               } else {
+                   rightuv = SvUVX(TOPs);
+                   if (rightuv > (UV) IV_MAX) {
+                       /* As (a) is an IV, it cannot be > IV_MAX */
+                       value = -1;
+                   } else if (leftiv > (UV)rightuv) {
+                       value = 1;
+                   } else if (leftiv < (UV)rightuv) {
+                       value = -1;
+                   } else {
+                       value = 0;
+                   }
+               }
+           }
+           SP--;
+           SETi(value);
+           RETURN;
+       }
+    }
+#endif
+    {
+      dPOPTOPnnrl;
+      I32 value;
+
+#ifdef Perl_isnan
+      if (Perl_isnan(left) || Perl_isnan(right)) {
+         SETs(&PL_sv_undef);
+         RETURN;
+       }
+      value = (left > right) - (left < right);
 #else
       if (left == right)
        value = 0;
 #else
       if (left == right)
        value = 0;
@@ -1244,10 +1959,10 @@ PP(pp_ncmp)
 
 PP(pp_slt)
 {
 
 PP(pp_slt)
 {
-    djSP; tryAMAGICbinSET(slt,0);
+    dSP; tryAMAGICbinSET(slt,0);
     {
       dPOPTOPssrl;
     {
       dPOPTOPssrl;
-      int cmp = ((PL_op->op_private & OPpLOCALE)
+      int cmp = (IN_LOCALE_RUNTIME
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp < 0));
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp < 0));
@@ -1257,10 +1972,10 @@ PP(pp_slt)
 
 PP(pp_sgt)
 {
 
 PP(pp_sgt)
 {
-    djSP; tryAMAGICbinSET(sgt,0);
+    dSP; tryAMAGICbinSET(sgt,0);
     {
       dPOPTOPssrl;
     {
       dPOPTOPssrl;
-      int cmp = ((PL_op->op_private & OPpLOCALE)
+      int cmp = (IN_LOCALE_RUNTIME
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp > 0));
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp > 0));
@@ -1270,10 +1985,10 @@ PP(pp_sgt)
 
 PP(pp_sle)
 {
 
 PP(pp_sle)
 {
-    djSP; tryAMAGICbinSET(sle,0);
+    dSP; tryAMAGICbinSET(sle,0);
     {
       dPOPTOPssrl;
     {
       dPOPTOPssrl;
-      int cmp = ((PL_op->op_private & OPpLOCALE)
+      int cmp = (IN_LOCALE_RUNTIME
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp <= 0));
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp <= 0));
@@ -1283,10 +1998,10 @@ PP(pp_sle)
 
 PP(pp_sge)
 {
 
 PP(pp_sge)
 {
-    djSP; tryAMAGICbinSET(sge,0);
+    dSP; tryAMAGICbinSET(sge,0);
     {
       dPOPTOPssrl;
     {
       dPOPTOPssrl;
-      int cmp = ((PL_op->op_private & OPpLOCALE)
+      int cmp = (IN_LOCALE_RUNTIME
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp >= 0));
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETs(boolSV(cmp >= 0));
@@ -1296,7 +2011,7 @@ PP(pp_sge)
 
 PP(pp_seq)
 {
 
 PP(pp_seq)
 {
-    djSP; tryAMAGICbinSET(seq,0);
+    dSP; tryAMAGICbinSET(seq,0);
     {
       dPOPTOPssrl;
       SETs(boolSV(sv_eq(left, right)));
     {
       dPOPTOPssrl;
       SETs(boolSV(sv_eq(left, right)));
@@ -1306,7 +2021,7 @@ PP(pp_seq)
 
 PP(pp_sne)
 {
 
 PP(pp_sne)
 {
-    djSP; tryAMAGICbinSET(sne,0);
+    dSP; tryAMAGICbinSET(sne,0);
     {
       dPOPTOPssrl;
       SETs(boolSV(!sv_eq(left, right)));
     {
       dPOPTOPssrl;
       SETs(boolSV(!sv_eq(left, right)));
@@ -1316,10 +2031,10 @@ PP(pp_sne)
 
 PP(pp_scmp)
 {
 
 PP(pp_scmp)
 {
-    djSP; dTARGET;  tryAMAGICbin(scmp,0);
+    dSP; dTARGET;  tryAMAGICbin(scmp,0);
     {
       dPOPTOPssrl;
     {
       dPOPTOPssrl;
-      int cmp = ((PL_op->op_private & OPpLOCALE)
+      int cmp = (IN_LOCALE_RUNTIME
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETi( cmp );
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
       SETi( cmp );
@@ -1329,7 +2044,7 @@ PP(pp_scmp)
 
 PP(pp_bit_and)
 {
 
 PP(pp_bit_and)
 {
-    djSP; dATARGET; tryAMAGICbin(band,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(band,opASSIGN);
     {
       dPOPTOPssrl;
       if (SvNIOKp(left) || SvNIOKp(right)) {
     {
       dPOPTOPssrl;
       if (SvNIOKp(left) || SvNIOKp(right)) {
@@ -1352,7 +2067,7 @@ PP(pp_bit_and)
 
 PP(pp_bit_xor)
 {
 
 PP(pp_bit_xor)
 {
-    djSP; dATARGET; tryAMAGICbin(bxor,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(bxor,opASSIGN);
     {
       dPOPTOPssrl;
       if (SvNIOKp(left) || SvNIOKp(right)) {
     {
       dPOPTOPssrl;
       if (SvNIOKp(left) || SvNIOKp(right)) {
@@ -1375,7 +2090,7 @@ PP(pp_bit_xor)
 
 PP(pp_bit_or)
 {
 
 PP(pp_bit_or)
 {
-    djSP; dATARGET; tryAMAGICbin(bor,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(bor,opASSIGN);
     {
       dPOPTOPssrl;
       if (SvNIOKp(left) || SvNIOKp(right)) {
     {
       dPOPTOPssrl;
       if (SvNIOKp(left) || SvNIOKp(right)) {
@@ -1398,14 +2113,18 @@ PP(pp_bit_or)
 
 PP(pp_negate)
 {
 
 PP(pp_negate)
 {
-    djSP; dTARGET; tryAMAGICun(neg);
+    dSP; dTARGET; tryAMAGICun(neg);
     {
        dTOPss;
     {
        dTOPss;
+       int flags = SvFLAGS(sv);
        if (SvGMAGICAL(sv))
            mg_get(sv);
        if (SvGMAGICAL(sv))
            mg_get(sv);
-       if (SvIOKp(sv) && !SvNOKp(sv) && !SvPOKp(sv)) {
+       if ((flags & SVf_IOK) || ((flags & (SVp_IOK | SVp_NOK)) == SVp_IOK)) {
+           /* It's publicly an integer, or privately an integer-not-float */
+       oops_its_an_int:
            if (SvIsUV(sv)) {
                if (SvIVX(sv) == IV_MIN) {
            if (SvIsUV(sv)) {
                if (SvIVX(sv) == IV_MIN) {
+                   /* 2s complement assumption. */
                    SETi(SvIVX(sv));    /* special case: -((UV)IV_MAX+1) == IV_MIN */
                    RETURN;
                }
                    SETi(SvIVX(sv));    /* special case: -((UV)IV_MAX+1) == IV_MIN */
                    RETURN;
                }
@@ -1418,6 +2137,12 @@ PP(pp_negate)
                SETi(-SvIVX(sv));
                RETURN;
            }
                SETi(-SvIVX(sv));
                RETURN;
            }
+#ifdef PERL_PRESERVE_IVUV
+           else {
+               SETu((UV)IV_MIN);
+               RETURN;
+           }
+#endif
        }
        if (SvNIOKp(sv))
            SETn(-SvNV(sv));
        }
        if (SvNIOKp(sv))
            SETn(-SvNV(sv));
@@ -1432,12 +2157,16 @@ PP(pp_negate)
                sv_setsv(TARG, sv);
                *SvPV_force(TARG, len) = *s == '-' ? '+' : '-';
            }
                sv_setsv(TARG, sv);
                *SvPV_force(TARG, len) = *s == '-' ? '+' : '-';
            }
-           else if (DO_UTF8(sv) && *(U8*)s >= 0xc0 && isIDFIRST_utf8((U8*)s)) {
+           else if (DO_UTF8(sv) && UTF8_IS_START(*s) && isIDFIRST_utf8((U8*)s)) {
                sv_setpvn(TARG, "-", 1);
                sv_catsv(TARG, sv);
            }
                sv_setpvn(TARG, "-", 1);
                sv_catsv(TARG, sv);
            }
-           else
-               sv_setnv(TARG, -SvNV(sv));
+           else {
+             SvIV_please(sv);
+             if (SvIOK(sv))
+               goto oops_its_an_int;
+             sv_setnv(TARG, -SvNV(sv));
+           }
            SETTARG;
        }
        else
            SETTARG;
        }
        else
@@ -1448,14 +2177,14 @@ PP(pp_negate)
 
 PP(pp_not)
 {
 
 PP(pp_not)
 {
-    djSP; tryAMAGICunSET(not);
+    dSP; tryAMAGICunSET(not);
     *PL_stack_sp = boolSV(!SvTRUE(*PL_stack_sp));
     return NORMAL;
 }
 
 PP(pp_complement)
 {
     *PL_stack_sp = boolSV(!SvTRUE(*PL_stack_sp));
     return NORMAL;
 }
 
 PP(pp_complement)
 {
-    djSP; dTARGET; tryAMAGICun(compl);
+    dSP; dTARGET; tryAMAGICun(compl);
     {
       dTOPss;
       if (SvNIOKp(sv)) {
     {
       dTOPss;
       if (SvNIOKp(sv)) {
@@ -1487,7 +2216,7 @@ PP(pp_complement)
 
          send = tmps + len;
          while (tmps < send) {
 
          send = tmps + len;
          while (tmps < send) {
-           UV c = utf8_to_uv(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
+           UV c = utf8n_to_uvchr(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
            tmps += UTF8SKIP(tmps);
            targlen += UNISKIP(~c);
            nchar++;
            tmps += UTF8SKIP(tmps);
            targlen += UNISKIP(~c);
            nchar++;
@@ -1501,9 +2230,9 @@ PP(pp_complement)
          if (nwide) {
              Newz(0, result, targlen + 1, U8);
              while (tmps < send) {
          if (nwide) {
              Newz(0, result, targlen + 1, U8);
              while (tmps < send) {
-                 UV c = utf8_to_uv(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
+                 UV c = utf8n_to_uvchr(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
                  tmps += UTF8SKIP(tmps);
                  tmps += UTF8SKIP(tmps);
-                 result = uv_to_utf8(result, ~c);
+                 result = uvchr_to_utf8(result, ~c);
              }
              *result = '\0';
              result -= targlen;
              }
              *result = '\0';
              result -= targlen;
@@ -1513,7 +2242,7 @@ PP(pp_complement)
          else {
              Newz(0, result, nchar + 1, U8);
              while (tmps < send) {
          else {
              Newz(0, result, nchar + 1, U8);
              while (tmps < send) {
-                 U8 c = (U8)utf8_to_uv(tmps, 0, &l, UTF8_ALLOW_ANY);
+                 U8 c = (U8)utf8n_to_uvchr(tmps, 0, &l, UTF8_ALLOW_ANY);
                  tmps += UTF8SKIP(tmps);
                  *result++ = ~c;
              }
                  tmps += UTF8SKIP(tmps);
                  *result++ = ~c;
              }
@@ -1549,7 +2278,7 @@ PP(pp_complement)
 
 PP(pp_i_multiply)
 {
 
 PP(pp_i_multiply)
 {
-    djSP; dATARGET; tryAMAGICbin(mult,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(mult,opASSIGN);
     {
       dPOPTOPiirl;
       SETi( left * right );
     {
       dPOPTOPiirl;
       SETi( left * right );
@@ -1559,7 +2288,7 @@ PP(pp_i_multiply)
 
 PP(pp_i_divide)
 {
 
 PP(pp_i_divide)
 {
-    djSP; dATARGET; tryAMAGICbin(div,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(div,opASSIGN);
     {
       dPOPiv;
       if (value == 0)
     {
       dPOPiv;
       if (value == 0)
@@ -1572,7 +2301,7 @@ PP(pp_i_divide)
 
 PP(pp_i_modulo)
 {
 
 PP(pp_i_modulo)
 {
-    djSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(modulo,opASSIGN);
     {
       dPOPTOPiirl;
       if (!right)
     {
       dPOPTOPiirl;
       if (!right)
@@ -1584,7 +2313,7 @@ PP(pp_i_modulo)
 
 PP(pp_i_add)
 {
 
 PP(pp_i_add)
 {
-    djSP; dATARGET; tryAMAGICbin(add,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(add,opASSIGN);
     {
       dPOPTOPiirl_ul;
       SETi( left + right );
     {
       dPOPTOPiirl_ul;
       SETi( left + right );
@@ -1594,7 +2323,7 @@ PP(pp_i_add)
 
 PP(pp_i_subtract)
 {
 
 PP(pp_i_subtract)
 {
-    djSP; dATARGET; tryAMAGICbin(subtr,opASSIGN);
+    dSP; dATARGET; tryAMAGICbin(subtr,opASSIGN);
     {
       dPOPTOPiirl_ul;
       SETi( left - right );
     {
       dPOPTOPiirl_ul;
       SETi( left - right );
@@ -1604,7 +2333,7 @@ PP(pp_i_subtract)
 
 PP(pp_i_lt)
 {
 
 PP(pp_i_lt)
 {
-    djSP; tryAMAGICbinSET(lt,0);
+    dSP; tryAMAGICbinSET(lt,0);
     {
       dPOPTOPiirl;
       SETs(boolSV(left < right));
     {
       dPOPTOPiirl;
       SETs(boolSV(left < right));
@@ -1614,7 +2343,7 @@ PP(pp_i_lt)
 
 PP(pp_i_gt)
 {
 
 PP(pp_i_gt)
 {
-    djSP; tryAMAGICbinSET(gt,0);
+    dSP; tryAMAGICbinSET(gt,0);
     {
       dPOPTOPiirl;
       SETs(boolSV(left > right));
     {
       dPOPTOPiirl;
       SETs(boolSV(left > right));
@@ -1624,7 +2353,7 @@ PP(pp_i_gt)
 
 PP(pp_i_le)
 {
 
 PP(pp_i_le)
 {
-    djSP; tryAMAGICbinSET(le,0);
+    dSP; tryAMAGICbinSET(le,0);
     {
       dPOPTOPiirl;
       SETs(boolSV(left <= right));
     {
       dPOPTOPiirl;
       SETs(boolSV(left <= right));
@@ -1634,7 +2363,7 @@ PP(pp_i_le)
 
 PP(pp_i_ge)
 {
 
 PP(pp_i_ge)
 {
-    djSP; tryAMAGICbinSET(ge,0);
+    dSP; tryAMAGICbinSET(ge,0);
     {
       dPOPTOPiirl;
       SETs(boolSV(left >= right));
     {
       dPOPTOPiirl;
       SETs(boolSV(left >= right));
@@ -1644,7 +2373,7 @@ PP(pp_i_ge)
 
 PP(pp_i_eq)
 {
 
 PP(pp_i_eq)
 {
-    djSP; tryAMAGICbinSET(eq,0);
+    dSP; tryAMAGICbinSET(eq,0);
     {
       dPOPTOPiirl;
       SETs(boolSV(left == right));
     {
       dPOPTOPiirl;
       SETs(boolSV(left == right));
@@ -1654,7 +2383,7 @@ PP(pp_i_eq)
 
 PP(pp_i_ne)
 {
 
 PP(pp_i_ne)
 {
-    djSP; tryAMAGICbinSET(ne,0);
+    dSP; tryAMAGICbinSET(ne,0);
     {
       dPOPTOPiirl;
       SETs(boolSV(left != right));
     {
       dPOPTOPiirl;
       SETs(boolSV(left != right));
@@ -1664,7 +2393,7 @@ PP(pp_i_ne)
 
 PP(pp_i_ncmp)
 {
 
 PP(pp_i_ncmp)
 {
-    djSP; dTARGET; tryAMAGICbin(ncmp,0);
+    dSP; dTARGET; tryAMAGICbin(ncmp,0);
     {
       dPOPTOPiirl;
       I32 value;
     {
       dPOPTOPiirl;
       I32 value;
@@ -1682,7 +2411,7 @@ PP(pp_i_ncmp)
 
 PP(pp_i_negate)
 {
 
 PP(pp_i_negate)
 {
-    djSP; dTARGET; tryAMAGICun(neg);
+    dSP; dTARGET; tryAMAGICun(neg);
     SETi(-TOPi);
     RETURN;
 }
     SETi(-TOPi);
     RETURN;
 }
@@ -1691,7 +2420,7 @@ PP(pp_i_negate)
 
 PP(pp_atan2)
 {
 
 PP(pp_atan2)
 {
-    djSP; dTARGET; tryAMAGICbin(atan2,0);
+    dSP; dTARGET; tryAMAGICbin(atan2,0);
     {
       dPOPTOPnnrl;
       SETn(Perl_atan2(left, right));
     {
       dPOPTOPnnrl;
       SETn(Perl_atan2(left, right));
@@ -1701,7 +2430,7 @@ PP(pp_atan2)
 
 PP(pp_sin)
 {
 
 PP(pp_sin)
 {
-    djSP; dTARGET; tryAMAGICun(sin);
+    dSP; dTARGET; tryAMAGICun(sin);
     {
       NV value;
       value = POPn;
     {
       NV value;
       value = POPn;
@@ -1713,7 +2442,7 @@ PP(pp_sin)
 
 PP(pp_cos)
 {
 
 PP(pp_cos)
 {
-    djSP; dTARGET; tryAMAGICun(cos);
+    dSP; dTARGET; tryAMAGICun(cos);
     {
       NV value;
       value = POPn;
     {
       NV value;
       value = POPn;
@@ -1740,7 +2469,7 @@ extern double drand48 (void);
 
 PP(pp_rand)
 {
 
 PP(pp_rand)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     NV value;
     if (MAXARG < 1)
        value = 1.0;
     NV value;
     if (MAXARG < 1)
        value = 1.0;
@@ -1759,7 +2488,7 @@ PP(pp_rand)
 
 PP(pp_srand)
 {
 
 PP(pp_srand)
 {
-    djSP;
+    dSP;
     UV anum;
     if (MAXARG < 1)
        anum = seed();
     UV anum;
     if (MAXARG < 1)
        anum = seed();
@@ -1796,7 +2525,6 @@ S_seed(pTHX)
 #define   SEED_C3      269
 #define   SEED_C5      26107
 
 #define   SEED_C3      269
 #define   SEED_C5      26107
 
-    dTHR;
 #ifndef PERL_NO_DEV_RANDOM
     int fd;
 #endif
 #ifndef PERL_NO_DEV_RANDOM
     int fd;
 #endif
@@ -1855,7 +2583,7 @@ S_seed(pTHX)
 
 PP(pp_exp)
 {
 
 PP(pp_exp)
 {
-    djSP; dTARGET; tryAMAGICun(exp);
+    dSP; dTARGET; tryAMAGICun(exp);
     {
       NV value;
       value = POPn;
     {
       NV value;
       value = POPn;
@@ -1867,7 +2595,7 @@ PP(pp_exp)
 
 PP(pp_log)
 {
 
 PP(pp_log)
 {
-    djSP; dTARGET; tryAMAGICun(log);
+    dSP; dTARGET; tryAMAGICun(log);
     {
       NV value;
       value = POPn;
     {
       NV value;
       value = POPn;
@@ -1883,7 +2611,7 @@ PP(pp_log)
 
 PP(pp_sqrt)
 {
 
 PP(pp_sqrt)
 {
-    djSP; dTARGET; tryAMAGICun(sqrt);
+    dSP; dTARGET; tryAMAGICun(sqrt);
     {
       NV value;
       value = POPn;
     {
       NV value;
       value = POPn;
@@ -1899,40 +2627,70 @@ PP(pp_sqrt)
 
 PP(pp_int)
 {
 
 PP(pp_int)
 {
-    djSP; dTARGET;
+    dSP; dTARGET; tryAMAGICun(int);
     {
     {
-      NV value = TOPn;
-      IV iv;
-
-      if (SvIOKp(TOPs) && !SvNOKp(TOPs) && !SvPOKp(TOPs)) {
-       iv = SvIVX(TOPs);
-       SETi(iv);
-      }
-      else {
+      NV value;
+      IV iv = TOPi; /* attempt to convert to IV if possible. */
+      /* XXX it's arguable that compiler casting to IV might be subtly
+        different from modf (for numbers inside (IV_MIN,UV_MAX)) in which
+        else preferring IV has introduced a subtle behaviour change bug. OTOH
+        relying on floating point to be accurate is a bug.  */
+
+      if (SvIOK(TOPs)) {
+       if (SvIsUV(TOPs)) {
+           UV uv = TOPu;
+           SETu(uv);
+       } else
+           SETi(iv);
+      } else {
+         value = TOPn;
          if (value >= 0.0) {
          if (value >= 0.0) {
+             if (value < (NV)UV_MAX + 0.5) {
+                 SETu(U_V(value));
+             } else {
 #if defined(HAS_MODFL) || defined(LONG_DOUBLE_EQUALS_DOUBLE)
 #if defined(HAS_MODFL) || defined(LONG_DOUBLE_EQUALS_DOUBLE)
-             (void)Perl_modf(value, &value);
+#   ifdef HAS_MODFL_POW32_BUG
+/* some versions of glibc split (i + d) into (i-1, d+1) for 2^32 <= i < 2^64 */
+                { 
+                    NV offset = Perl_modf(value, &value);
+                    (void)Perl_modf(offset, &offset);
+                    value += offset;
+                }
+#   else
+                 (void)Perl_modf(value, &value);
+#   endif
 #else
 #else
-             double tmp = (double)value;
-             (void)Perl_modf(tmp, &tmp);
-             value = (NV)tmp;
+                 double tmp = (double)value;
+                 (void)Perl_modf(tmp, &tmp);
+                 value = (NV)tmp;
 #endif
 #endif
+                 SETn(value);
+             }
          }
          }
-       else {
+         else {
+             if (value > (NV)IV_MIN - 0.5) {
+                 SETi(I_V(value));
+             } else {
 #if defined(HAS_MODFL) || defined(LONG_DOUBLE_EQUALS_DOUBLE)
 #if defined(HAS_MODFL) || defined(LONG_DOUBLE_EQUALS_DOUBLE)
-           (void)Perl_modf(-value, &value);
-           value = -value;
+#   ifdef HAS_MODFL_POW32_BUG
+/* some versions of glibc split (i + d) into (i-1, d+1) for 2^32 <= i < 2^64 */
+                 {
+                     NV offset = Perl_modf(-value, &value);
+                     (void)Perl_modf(offset, &offset);
+                     value += offset;
+                 }
+#   else
+                 (void)Perl_modf(-value, &value);
+#   endif
+                 value = -value;
 #else
 #else
-           double tmp = (double)value;
-           (void)Perl_modf(-tmp, &tmp);
-           value = -(NV)tmp;
+                 double tmp = (double)value;
+                 (void)Perl_modf(-tmp, &tmp);
+                 value = -(NV)tmp;
 #endif
 #endif
-       }
-       iv = I_V(value);
-       if (iv == value)
-         SETi(iv);
-       else
-         SETn(value);
+                 SETn(value);
+             }
+         }
       }
     }
     RETURN;
       }
     }
     RETURN;
@@ -1940,60 +2698,86 @@ PP(pp_int)
 
 PP(pp_abs)
 {
 
 PP(pp_abs)
 {
-    djSP; dTARGET; tryAMAGICun(abs);
+    dSP; dTARGET; tryAMAGICun(abs);
     {
     {
-      NV value = TOPn;
-      IV iv;
-
-      if (SvIOKp(TOPs) && !SvNOKp(TOPs) && !SvPOKp(TOPs) &&
-         (iv = SvIVX(TOPs)) != IV_MIN) {
-       if (iv < 0)
-         iv = -iv;
-       SETi(iv);
-      }
-      else {
+      /* This will cache the NV value if string isn't actually integer  */
+      IV iv = TOPi;
+
+      if (SvIOK(TOPs)) {
+       /* IVX is precise  */
+       if (SvIsUV(TOPs)) {
+         SETu(TOPu);   /* force it to be numeric only */
+       } else {
+         if (iv >= 0) {
+           SETi(iv);
+         } else {
+           if (iv != IV_MIN) {
+             SETi(-iv);
+           } else {
+             /* 2s complement assumption. Also, not really needed as
+                IV_MIN and -IV_MIN should both be %100...00 and NV-able  */
+             SETu(IV_MIN);
+           }
+         }
+       }
+      } else{
+       NV value = TOPn;
        if (value < 0.0)
        if (value < 0.0)
-           value = -value;
+         value = -value;
        SETn(value);
       }
     }
     RETURN;
 }
 
        SETn(value);
       }
     }
     RETURN;
 }
 
+
 PP(pp_hex)
 {
 PP(pp_hex)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     char *tmps;
     char *tmps;
-    STRLEN argtype;
-    STRLEN n_a;
+    I32 flags = PERL_SCAN_ALLOW_UNDERSCORES;
+    STRLEN len;
+    NV result_nv;
+    UV result_uv;
 
 
-    tmps = POPpx;
-    argtype = 1;               /* allow underscores */
-    XPUSHn(scan_hex(tmps, 99, &argtype));
+    tmps = (SvPVx(POPs, len));
+    result_uv = grok_hex (tmps, &len, &flags, &result_nv);
+    if (flags & PERL_SCAN_GREATER_THAN_UV_MAX) {
+        XPUSHn(result_nv);
+    }
+    else {
+        XPUSHu(result_uv);
+    }
     RETURN;
 }
 
 PP(pp_oct)
 {
     RETURN;
 }
 
 PP(pp_oct)
 {
-    djSP; dTARGET;
-    NV value;
-    STRLEN argtype;
+    dSP; dTARGET;
     char *tmps;
     char *tmps;
-    STRLEN n_a;
+    I32 flags = PERL_SCAN_ALLOW_UNDERSCORES;
+    STRLEN len;
+    NV result_nv;
+    UV result_uv;
 
 
-    tmps = POPpx;
-    while (*tmps && isSPACE(*tmps))
-       tmps++;
+    tmps = (SvPVx(POPs, len));
+    while (*tmps && len && isSPACE(*tmps))
+        tmps++, len--;
     if (*tmps == '0')
     if (*tmps == '0')
-       tmps++;
-    argtype = 1;               /* allow underscores */
+        tmps++, len--;
     if (*tmps == 'x')
     if (*tmps == 'x')
-       value = scan_hex(++tmps, 99, &argtype);
+        result_uv = grok_hex (tmps, &len, &flags, &result_nv);
     else if (*tmps == 'b')
     else if (*tmps == 'b')
-       value = scan_bin(++tmps, 99, &argtype);
+        result_uv = grok_bin (tmps, &len, &flags, &result_nv);
     else
     else
-       value = scan_oct(tmps, 99, &argtype);
-    XPUSHn(value);
+        result_uv = grok_oct (tmps, &len, &flags, &result_nv);
+
+    if (flags & PERL_SCAN_GREATER_THAN_UV_MAX) {
+        XPUSHn(result_nv);
+    }
+    else {
+        XPUSHu(result_uv);
+    }
     RETURN;
 }
 
     RETURN;
 }
 
@@ -2001,7 +2785,7 @@ PP(pp_oct)
 
 PP(pp_length)
 {
 
 PP(pp_length)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     SV *sv = TOPs;
 
     if (DO_UTF8(sv))
     SV *sv = TOPs;
 
     if (DO_UTF8(sv))
@@ -2013,48 +2797,61 @@ PP(pp_length)
 
 PP(pp_substr)
 {
 
 PP(pp_substr)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     SV *sv;
     SV *sv;
-    I32 len;
+    I32 len = 0;
     STRLEN curlen;
     STRLEN curlen;
-    STRLEN utfcurlen;
+    STRLEN utf8_curlen;
     I32 pos;
     I32 rem;
     I32 fail;
     I32 pos;
     I32 rem;
     I32 fail;
-    I32 lvalue = PL_op->op_flags & OPf_MOD;
+    I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;
     char *tmps;
     I32 arybase = PL_curcop->cop_arybase;
     char *tmps;
     I32 arybase = PL_curcop->cop_arybase;
+    SV *repl_sv = NULL;
     char *repl = 0;
     STRLEN repl_len;
     char *repl = 0;
     STRLEN repl_len;
+    int num_args = PL_op->op_private & 7;
+    bool repl_need_utf8_upgrade = FALSE;
+    bool repl_is_utf8 = FALSE;
 
     SvTAINTED_off(TARG);                       /* decontaminate */
     SvUTF8_off(TARG);                          /* decontaminate */
 
     SvTAINTED_off(TARG);                       /* decontaminate */
     SvUTF8_off(TARG);                          /* decontaminate */
-    if (MAXARG > 2) {
-       if (MAXARG > 3) {
-           sv = POPs;
-           repl = SvPV(sv, repl_len);
+    if (num_args > 2) {
+       if (num_args > 3) {
+           repl_sv = POPs;
+           repl = SvPV(repl_sv, repl_len);
+           repl_is_utf8 = DO_UTF8(repl_sv) && SvCUR(repl_sv);
        }
        len = POPi;
     }
     pos = POPi;
     sv = POPs;
     PUTBACK;
        }
        len = POPi;
     }
     pos = POPi;
     sv = POPs;
     PUTBACK;
+    if (repl_sv) {
+       if (repl_is_utf8) {
+           if (!DO_UTF8(sv))
+               sv_utf8_upgrade(sv);
+       }
+       else if (DO_UTF8(sv))
+           repl_need_utf8_upgrade = TRUE;
+    }
     tmps = SvPV(sv, curlen);
     if (DO_UTF8(sv)) {
     tmps = SvPV(sv, curlen);
     if (DO_UTF8(sv)) {
-        utfcurlen = sv_len_utf8(sv);
-       if (utfcurlen == curlen)
-           utfcurlen = 0;
+        utf8_curlen = sv_len_utf8(sv);
+       if (utf8_curlen == curlen)
+           utf8_curlen = 0;
        else
        else
-           curlen = utfcurlen;
+           curlen = utf8_curlen;
     }
     else
     }
     else
-       utfcurlen = 0;
+       utf8_curlen = 0;
 
     if (pos >= arybase) {
        pos -= arybase;
        rem = curlen-pos;
        fail = rem;
 
     if (pos >= arybase) {
        pos -= arybase;
        rem = curlen-pos;
        fail = rem;
-       if (MAXARG > 2) {
+       if (num_args > 2) {
            if (len < 0) {
                rem += len;
                if (rem < 0)
            if (len < 0) {
                rem += len;
                if (rem < 0)
@@ -2066,7 +2863,7 @@ PP(pp_substr)
     }
     else {
        pos += curlen;
     }
     else {
        pos += curlen;
-       if (MAXARG < 3)
+       if (num_args < 3)
            rem = curlen;
        else if (len >= 0) {
            rem = pos+len;
            rem = curlen;
        else if (len >= 0) {
            rem = pos+len;
@@ -2091,14 +2888,32 @@ PP(pp_substr)
        RETPUSHUNDEF;
     }
     else {
        RETPUSHUNDEF;
     }
     else {
-       if (utfcurlen)
+       I32 upos = pos;
+       I32 urem = rem;
+       if (utf8_curlen)
            sv_pos_u2b(sv, &pos, &rem);
        tmps += pos;
        sv_setpvn(TARG, tmps, rem);
            sv_pos_u2b(sv, &pos, &rem);
        tmps += pos;
        sv_setpvn(TARG, tmps, rem);
-       if (utfcurlen)
+#ifdef USE_LOCALE_COLLATE
+       sv_unmagic(TARG, PERL_MAGIC_collxfrm);
+#endif
+       if (utf8_curlen)
            SvUTF8_on(TARG);
            SvUTF8_on(TARG);
-       if (repl)
+       if (repl) {
+           SV* repl_sv_copy = NULL;
+
+           if (repl_need_utf8_upgrade) {
+               repl_sv_copy = newSVsv(repl_sv);
+               sv_utf8_upgrade(repl_sv_copy);
+               repl = SvPV(repl_sv_copy, repl_len);
+               repl_is_utf8 = DO_UTF8(repl_sv_copy) && SvCUR(sv);
+           }
            sv_insert(sv, pos, rem, repl, repl_len);
            sv_insert(sv, pos, rem, repl, repl_len);
+           if (repl_is_utf8)
+               SvUTF8_on(sv);
+           if (repl_sv_copy)
+               SvREFCNT_dec(repl_sv_copy);
+       }
        else if (lvalue) {              /* it's an lvalue! */
            if (!SvGMAGICAL(sv)) {
                if (SvROK(sv)) {
        else if (lvalue) {              /* it's an lvalue! */
            if (!SvGMAGICAL(sv)) {
                if (SvROK(sv)) {
@@ -2116,7 +2931,7 @@ PP(pp_substr)
 
            if (SvTYPE(TARG) < SVt_PVLV) {
                sv_upgrade(TARG, SVt_PVLV);
 
            if (SvTYPE(TARG) < SVt_PVLV) {
                sv_upgrade(TARG, SVt_PVLV);
-               sv_magic(TARG, Nullsv, 'x', Nullch, 0);
+               sv_magic(TARG, Nullsv, PERL_MAGIC_substr, Nullch, 0);
            }
 
            LvTYPE(TARG) = 'x';
            }
 
            LvTYPE(TARG) = 'x';
@@ -2125,8 +2940,8 @@ PP(pp_substr)
                    SvREFCNT_dec(LvTARG(TARG));
                LvTARG(TARG) = SvREFCNT_inc(sv);
            }
                    SvREFCNT_dec(LvTARG(TARG));
                LvTARG(TARG) = SvREFCNT_inc(sv);
            }
-           LvTARGOFF(TARG) = pos;
-           LvTARGLEN(TARG) = rem;
+           LvTARGOFF(TARG) = upos;
+           LvTARGLEN(TARG) = urem;
        }
     }
     SPAGAIN;
        }
     }
     SPAGAIN;
@@ -2136,17 +2951,17 @@ PP(pp_substr)
 
 PP(pp_vec)
 {
 
 PP(pp_vec)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     register IV size   = POPi;
     register IV offset = POPi;
     register SV *src = POPs;
     register IV size   = POPi;
     register IV offset = POPi;
     register SV *src = POPs;
-    I32 lvalue = PL_op->op_flags & OPf_MOD;
+    I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;
 
     SvTAINTED_off(TARG);               /* decontaminate */
     if (lvalue) {                      /* it's an lvalue! */
        if (SvTYPE(TARG) < SVt_PVLV) {
            sv_upgrade(TARG, SVt_PVLV);
 
     SvTAINTED_off(TARG);               /* decontaminate */
     if (lvalue) {                      /* it's an lvalue! */
        if (SvTYPE(TARG) < SVt_PVLV) {
            sv_upgrade(TARG, SVt_PVLV);
-           sv_magic(TARG, Nullsv, 'v', Nullch, 0);
+           sv_magic(TARG, Nullsv, PERL_MAGIC_vec, Nullch, 0);
        }
        LvTYPE(TARG) = 'v';
        if (LvTARG(TARG) != src) {
        }
        LvTYPE(TARG) = 'v';
        if (LvTARG(TARG) != src) {
@@ -2165,7 +2980,7 @@ PP(pp_vec)
 
 PP(pp_index)
 {
 
 PP(pp_index)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     SV *big;
     SV *little;
     I32 offset;
     SV *big;
     SV *little;
     I32 offset;
@@ -2201,7 +3016,7 @@ PP(pp_index)
 
 PP(pp_rindex)
 {
 
 PP(pp_rindex)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     SV *big;
     SV *little;
     STRLEN blen;
     SV *big;
     SV *little;
     STRLEN blen;
@@ -2242,9 +3057,11 @@ PP(pp_rindex)
 
 PP(pp_sprintf)
 {
 
 PP(pp_sprintf)
 {
-    djSP; dMARK; dORIGMARK; dTARGET;
+    dSP; dMARK; dORIGMARK; dTARGET;
     do_sprintf(TARG, SP-MARK, MARK+1);
     TAINT_IF(SvTAINTED(TARG));
     do_sprintf(TARG, SP-MARK, MARK+1);
     TAINT_IF(SvTAINTED(TARG));
+    if (DO_UTF8(*(MARK+1)))
+       SvUTF8_on(TARG);
     SP = ORIGMARK;
     PUSHTARG;
     RETURN;
     SP = ORIGMARK;
     PUSHTARG;
     RETURN;
@@ -2252,33 +3069,26 @@ PP(pp_sprintf)
 
 PP(pp_ord)
 {
 
 PP(pp_ord)
 {
-    djSP; dTARGET;
-    UV value;
-    SV *tmpsv = POPs;
+    dSP; dTARGET;
+    SV *argsv = POPs;
     STRLEN len;
     STRLEN len;
-    U8 *tmps = (U8*)SvPVx(tmpsv, len);
-    STRLEN retlen;
+    U8 *s = (U8*)SvPVx(argsv, len);
 
 
-    if ((*tmps & 0x80) && DO_UTF8(tmpsv))
-       value = utf8_to_uv(tmps, len, &retlen, 0);
-    else
-       value = (UV)(*tmps & 255);
-    XPUSHu(value);
+    XPUSHu(DO_UTF8(argsv) ? utf8_to_uvchr(s, 0) : (*s & 0xff));
     RETURN;
 }
 
 PP(pp_chr)
 {
     RETURN;
 }
 
 PP(pp_chr)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     char *tmps;
     UV value = POPu;
 
     (void)SvUPGRADE(TARG,SVt_PV);
 
     char *tmps;
     UV value = POPu;
 
     (void)SvUPGRADE(TARG,SVt_PV);
 
-    if ((value > 255 && !IN_BYTE) || (value & 0x80 && PL_hints & HINT_UTF8) ) {
-       SvGROW(TARG, UTF8_MAXLEN+1);
-       tmps = SvPVX(TARG);
-       tmps = (char*)uv_to_utf8((U8*)tmps, (UV)value);
+    if (value > 255 && !IN_BYTES) {
+       SvGROW(TARG, UNISKIP(value)+1);
+       tmps = (char*)uvchr_to_utf8((U8*)SvPVX(TARG), value);
        SvCUR_set(TARG, tmps - SvPVX(TARG));
        *tmps = '\0';
        (void)SvPOK_only(TARG);
        SvCUR_set(TARG, tmps - SvPVX(TARG));
        *tmps = '\0';
        (void)SvPOK_only(TARG);
@@ -2299,15 +3109,31 @@ PP(pp_chr)
 
 PP(pp_crypt)
 {
 
 PP(pp_crypt)
 {
-    djSP; dTARGET; dPOPTOPssrl;
+    dSP; dTARGET; dPOPTOPssrl;
     STRLEN n_a;
 #ifdef HAS_CRYPT
     STRLEN n_a;
 #ifdef HAS_CRYPT
-    char *tmps = SvPV(left, n_a);
+    STRLEN len;
+    char *tmps = SvPV(left, len);
+    char *t    = 0;
+    if (DO_UTF8(left)) {
+         /* If Unicode take the crypt() of the low 8 bits
+         * of the characters of the string. */
+        char *s    = tmps;
+        char *send = tmps + len;
+        STRLEN i   = 0;
+        Newz(688, t, len, char);
+        while (s < send) {
+             t[i++] = utf8_to_uvchr((U8*)s, 0) & 0xFF;
+             s += UTF8SKIP(s);
+        }
+        tmps = t;
+    }
 #ifdef FCRYPT
     sv_setpv(TARG, fcrypt(tmps, SvPV(right, n_a)));
 #else
     sv_setpv(TARG, PerlProc_crypt(tmps, SvPV(right, n_a)));
 #endif
 #ifdef FCRYPT
     sv_setpv(TARG, fcrypt(tmps, SvPV(right, n_a)));
 #else
     sv_setpv(TARG, PerlProc_crypt(tmps, SvPV(right, n_a)));
 #endif
+    Safefree(t);
 #else
     DIE(aTHX_
       "The crypt() function is unimplemented due to excessive paranoia.");
 #else
     DIE(aTHX_
       "The crypt() function is unimplemented due to excessive paranoia.");
@@ -2318,37 +3144,32 @@ PP(pp_crypt)
 
 PP(pp_ucfirst)
 {
 
 PP(pp_ucfirst)
 {
-    djSP;
+    dSP;
     SV *sv = TOPs;
     register U8 *s;
     STRLEN slen;
 
     SV *sv = TOPs;
     register U8 *s;
     STRLEN slen;
 
-    if (DO_UTF8(sv) && (s = (U8*)SvPV(sv, slen)) && slen && (*s & 0xc0) == 0xc0) {
+    if (DO_UTF8(sv)) {
+       U8 tmpbuf[UTF8_MAXLEN*2+1];
        STRLEN ulen;
        STRLEN ulen;
-       U8 tmpbuf[UTF8_MAXLEN];
-       U8 *tend;
-       UV uv = utf8_to_uv(s, slen, &ulen, 0);
+       STRLEN tculen;
 
 
-       if (PL_op->op_private & OPpLOCALE) {
-           TAINT;
-           SvTAINTED_on(sv);
-           uv = toTITLE_LC_uni(uv);
-       }
-       else
-           uv = toTITLE_utf8(s);
-       
-       tend = uv_to_utf8(tmpbuf, uv);
+       s = (U8*)SvPV(sv, slen);
+       utf8_to_uvchr(s, &ulen);
 
 
-       if (!SvPADTMP(sv) || tend - tmpbuf != ulen || SvREADONLY(sv)) {
+       toTITLE_utf8(s, tmpbuf, &tculen);
+       utf8_to_uvchr(tmpbuf, 0);
+
+       if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            dTARGET;
-           sv_setpvn(TARG, (char*)tmpbuf, tend - tmpbuf);
+           sv_setpvn(TARG, (char*)tmpbuf, tculen);
            sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
            SvUTF8_on(TARG);
            SETs(TARG);
        }
        else {
            s = (U8*)SvPV_force(sv, slen);
            sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
            SvUTF8_on(TARG);
            SETs(TARG);
        }
        else {
            s = (U8*)SvPV_force(sv, slen);
-           Copy(tmpbuf, s, ulen, U8);
+           Copy(tmpbuf, s, tculen, U8);
        }
     }
     else {
        }
     }
     else {
@@ -2361,7 +3182,7 @@ PP(pp_ucfirst)
        }
        s = (U8*)SvPV_force(sv, slen);
        if (*s) {
        }
        s = (U8*)SvPV_force(sv, slen);
        if (*s) {
-           if (PL_op->op_private & OPpLOCALE) {
+           if (IN_LOCALE_RUNTIME) {
                TAINT;
                SvTAINTED_on(sv);
                *s = toUPPER_LC(*s);
                TAINT;
                SvTAINTED_on(sv);
                *s = toUPPER_LC(*s);
@@ -2377,26 +3198,21 @@ PP(pp_ucfirst)
 
 PP(pp_lcfirst)
 {
 
 PP(pp_lcfirst)
 {
-    djSP;
+    dSP;
     SV *sv = TOPs;
     register U8 *s;
     STRLEN slen;
 
     SV *sv = TOPs;
     register U8 *s;
     STRLEN slen;
 
-    if (DO_UTF8(sv) && (s = (U8*)SvPV(sv, slen)) && slen && (*s & 0xc0) == 0xc0) {
+    if (DO_UTF8(sv) && (s = (U8*)SvPV(sv, slen)) && slen && UTF8_IS_START(*s)) {
        STRLEN ulen;
        STRLEN ulen;
-       U8 tmpbuf[UTF8_MAXLEN];
+       U8 tmpbuf[UTF8_MAXLEN*2+1];
        U8 *tend;
        U8 *tend;
-       UV uv = utf8_to_uv(s, slen, &ulen, 0);
+       UV uv;
 
 
-       if (PL_op->op_private & OPpLOCALE) {
-           TAINT;
-           SvTAINTED_on(sv);
-           uv = toLOWER_LC_uni(uv);
-       }
-       else
-           uv = toLOWER_utf8(s);
+       toLOWER_utf8(s, tmpbuf, &ulen);
+       uv = utf8_to_uvchr(tmpbuf, 0);
        
        
-       tend = uv_to_utf8(tmpbuf, uv);
+       tend = uvchr_to_utf8(tmpbuf, uv);
 
        if (!SvPADTMP(sv) || tend - tmpbuf != ulen || SvREADONLY(sv)) {
            dTARGET;
 
        if (!SvPADTMP(sv) || tend - tmpbuf != ulen || SvREADONLY(sv)) {
            dTARGET;
@@ -2420,7 +3236,7 @@ PP(pp_lcfirst)
        }
        s = (U8*)SvPV_force(sv, slen);
        if (*s) {
        }
        s = (U8*)SvPV_force(sv, slen);
        if (*s) {
-           if (PL_op->op_private & OPpLOCALE) {
+           if (IN_LOCALE_RUNTIME) {
                TAINT;
                SvTAINTED_on(sv);
                *s = toLOWER_LC(*s);
                TAINT;
                SvTAINTED_on(sv);
                *s = toLOWER_LC(*s);
@@ -2436,7 +3252,7 @@ PP(pp_lcfirst)
 
 PP(pp_uc)
 {
 
 PP(pp_uc)
 {
-    djSP;
+    dSP;
     SV *sv = TOPs;
     register U8 *s;
     STRLEN len;
     SV *sv = TOPs;
     register U8 *s;
     STRLEN len;
@@ -2446,6 +3262,7 @@ PP(pp_uc)
        STRLEN ulen;
        register U8 *d;
        U8 *send;
        STRLEN ulen;
        register U8 *d;
        U8 *send;
+       U8 tmpbuf[UTF8_MAXLEN*2+1];
 
        s = (U8*)SvPV(sv,len);
        if (!len) {
 
        s = (U8*)SvPV(sv,len);
        if (!len) {
@@ -2459,19 +3276,11 @@ PP(pp_uc)
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
-           if (PL_op->op_private & OPpLOCALE) {
-               TAINT;
-               SvTAINTED_on(TARG);
-               while (s < send) {
-                   d = uv_to_utf8(d, toUPPER_LC_uni( utf8_to_uv(s, len, &ulen, 0)));
-                   s += ulen;
-               }
-           }
-           else {
-               while (s < send) {
-                   d = uv_to_utf8(d, toUPPER_utf8( s ));
-                   s += UTF8SKIP(s);
-               }
+           while (s < send) {
+               toUPPER_utf8(s, tmpbuf, &ulen);
+               Copy(tmpbuf, d, ulen, U8);
+               d += ulen;
+               s += UTF8SKIP(s);
            }
            *d = '\0';
            SvUTF8_on(TARG);
            }
            *d = '\0';
            SvUTF8_on(TARG);
@@ -2491,7 +3300,7 @@ PP(pp_uc)
        if (len) {
            register U8 *send = s + len;
 
        if (len) {
            register U8 *send = s + len;
 
-           if (PL_op->op_private & OPpLOCALE) {
+           if (IN_LOCALE_RUNTIME) {
                TAINT;
                SvTAINTED_on(sv);
                for (; s < send; s++)
                TAINT;
                SvTAINTED_on(sv);
                for (; s < send; s++)
@@ -2510,7 +3319,7 @@ PP(pp_uc)
 
 PP(pp_lc)
 {
 
 PP(pp_lc)
 {
-    djSP;
+    dSP;
     SV *sv = TOPs;
     register U8 *s;
     STRLEN len;
     SV *sv = TOPs;
     register U8 *s;
     STRLEN len;
@@ -2520,6 +3329,7 @@ PP(pp_lc)
        STRLEN ulen;
        register U8 *d;
        U8 *send;
        STRLEN ulen;
        register U8 *d;
        U8 *send;
+       U8 tmpbuf[UTF8_MAXLEN*2+1];
 
        s = (U8*)SvPV(sv,len);
        if (!len) {
 
        s = (U8*)SvPV(sv,len);
        if (!len) {
@@ -2533,19 +3343,11 @@ PP(pp_lc)
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
-           if (PL_op->op_private & OPpLOCALE) {
-               TAINT;
-               SvTAINTED_on(TARG);
-               while (s < send) {
-                   d = uv_to_utf8(d, toLOWER_LC_uni( utf8_to_uv(s, len, &ulen, 0)));
-                   s += ulen;
-               }
-           }
-           else {
-               while (s < send) {
-                   d = uv_to_utf8(d, toLOWER_utf8(s));
-                   s += UTF8SKIP(s);
-               }
+           while (s < send) {
+               toLOWER_utf8(s, tmpbuf, &ulen);
+               Copy(tmpbuf, d, ulen, U8);
+               d += ulen;
+               s += UTF8SKIP(s);
            }
            *d = '\0';
            SvUTF8_on(TARG);
            }
            *d = '\0';
            SvUTF8_on(TARG);
@@ -2566,7 +3368,7 @@ PP(pp_lc)
        if (len) {
            register U8 *send = s + len;
 
        if (len) {
            register U8 *send = s + len;
 
-           if (PL_op->op_private & OPpLOCALE) {
+           if (IN_LOCALE_RUNTIME) {
                TAINT;
                SvTAINTED_on(sv);
                for (; s < send; s++)
                TAINT;
                SvTAINTED_on(sv);
                for (; s < send; s++)
@@ -2585,7 +3387,7 @@ PP(pp_lc)
 
 PP(pp_quotemeta)
 {
 
 PP(pp_quotemeta)
 {
-    djSP; dTARGET;
+    dSP; dTARGET;
     SV *sv = TOPs;
     STRLEN len;
     register char *s = SvPV(sv,len);
     SV *sv = TOPs;
     STRLEN len;
     register char *s = SvPV(sv,len);
@@ -2598,7 +3400,7 @@ PP(pp_quotemeta)
        d = SvPVX(TARG);
        if (DO_UTF8(sv)) {
            while (len) {
        d = SvPVX(TARG);
        if (DO_UTF8(sv)) {
            while (len) {
-               if (*s & 0x80) {
+               if (UTF8_IS_CONTINUED(*s)) {
                    STRLEN ulen = UTF8SKIP(s);
                    if (ulen > len)
                        ulen = len;
                    STRLEN ulen = UTF8SKIP(s);
                    if (ulen > len)
                        ulen = len;
@@ -2638,10 +3440,10 @@ PP(pp_quotemeta)
 
 PP(pp_aslice)
 {
 
 PP(pp_aslice)
 {
-    djSP; dMARK; dORIGMARK;
+    dSP; dMARK; dORIGMARK;
     register SV** svp;
     register AV* av = (AV*)POPs;
     register SV** svp;
     register AV* av = (AV*)POPs;
-    register I32 lval = PL_op->op_flags & OPf_MOD;
+    register I32 lval = (PL_op->op_flags & OPf_MOD || LVRET);
     I32 arybase = PL_curcop->cop_arybase;
     I32 elem;
 
     I32 arybase = PL_curcop->cop_arybase;
     I32 elem;
 
@@ -2683,7 +3485,7 @@ PP(pp_aslice)
 
 PP(pp_each)
 {
 
 PP(pp_each)
 {
-    djSP;
+    dSP;
     HV *hash = (HV*)POPs;
     HE *entry;
     I32 gimme = GIMME_V;
     HV *hash = (HV*)POPs;
     HE *entry;
     I32 gimme = GIMME_V;
@@ -2725,7 +3527,7 @@ PP(pp_keys)
 
 PP(pp_delete)
 {
 
 PP(pp_delete)
 {
-    djSP;
+    dSP;
     I32 gimme = GIMME_V;
     I32 discard = (gimme == G_VOID) ? G_DISCARD : 0;
     SV *sv;
     I32 gimme = GIMME_V;
     I32 discard = (gimme == G_VOID) ? G_DISCARD : 0;
     SV *sv;
@@ -2789,7 +3591,7 @@ PP(pp_delete)
 
 PP(pp_exists)
 {
 
 PP(pp_exists)
 {
-    djSP;
+    dSP;
     SV *tmpsv;
     HV *hv;
 
     SV *tmpsv;
     HV *hv;
 
@@ -2826,9 +3628,9 @@ PP(pp_exists)
 
 PP(pp_hslice)
 {
 
 PP(pp_hslice)
 {
-    djSP; dMARK; dORIGMARK;
+    dSP; dMARK; dORIGMARK;
     register HV *hv = (HV*)POPs;
     register HV *hv = (HV*)POPs;
-    register I32 lval = PL_op->op_flags & OPf_MOD;
+    register I32 lval = (PL_op->op_flags & OPf_MOD || LVRET);
     I32 realhv = (SvTYPE(hv) == SVt_PVHV);
 
     if (!realhv && PL_op->op_private & OPpLVAL_INTRO)
     I32 realhv = (SvTYPE(hv) == SVt_PVHV);
 
     if (!realhv && PL_op->op_private & OPpLVAL_INTRO)
@@ -2838,6 +3640,9 @@ PP(pp_hslice)
        while (++MARK <= SP) {
            SV *keysv = *MARK;
            SV **svp;
        while (++MARK <= SP) {
            SV *keysv = *MARK;
            SV **svp;
+           I32 preeminent = SvRMAGICAL(hv) ? 1 :
+                               realhv ? hv_exists_ent(hv, keysv, 0)
+                                      : avhv_exists_ent((AV*)hv, keysv, 0);
            if (realhv) {
                HE *he = hv_fetch_ent(hv, keysv, lval, 0);
                svp = he ? &HeVAL(he) : 0;
            if (realhv) {
                HE *he = hv_fetch_ent(hv, keysv, lval, 0);
                svp = he ? &HeVAL(he) : 0;
@@ -2850,8 +3655,15 @@ PP(pp_hslice)
                    STRLEN n_a;
                    DIE(aTHX_ PL_no_helem, SvPV(keysv, n_a));
                }
                    STRLEN n_a;
                    DIE(aTHX_ PL_no_helem, SvPV(keysv, n_a));
                }
-               if (PL_op->op_private & OPpLVAL_INTRO)
-                   save_helem(hv, keysv, svp);
+               if (PL_op->op_private & OPpLVAL_INTRO) {
+                   if (preeminent)
+                       save_helem(hv, keysv, svp);
+                   else {
+                       STRLEN keylen;
+                       char *key = SvPV(keysv, keylen);
+                       SAVEDELETE(hv, savepvn(key,keylen), keylen);
+                   }
+                }
            }
            *MARK = svp ? *svp : &PL_sv_undef;
        }
            }
            *MARK = svp ? *svp : &PL_sv_undef;
        }
@@ -2868,7 +3680,7 @@ PP(pp_hslice)
 
 PP(pp_list)
 {
 
 PP(pp_list)
 {
-    djSP; dMARK;
+    dSP; dMARK;
     if (GIMME != G_ARRAY) {
        if (++MARK <= SP)
            *MARK = *SP;                /* unwanted list, return last item */
     if (GIMME != G_ARRAY) {
        if (++MARK <= SP)
            *MARK = *SP;                /* unwanted list, return last item */
@@ -2881,7 +3693,7 @@ PP(pp_list)
 
 PP(pp_lslice)
 {
 
 PP(pp_lslice)
 {
-    djSP;
+    dSP;
     SV **lastrelem = PL_stack_sp;
     SV **lastlelem = PL_stack_base + POPMARK;
     SV **firstlelem = PL_stack_base + POPMARK + 1;
     SV **lastrelem = PL_stack_sp;
     SV **lastlelem = PL_stack_base + POPMARK;
     SV **firstlelem = PL_stack_base + POPMARK + 1;
@@ -2936,7 +3748,7 @@ PP(pp_lslice)
 
 PP(pp_anonlist)
 {
 
 PP(pp_anonlist)
 {
-    djSP; dMARK; dORIGMARK;
+    dSP; dMARK; dORIGMARK;
     I32 items = SP - MARK;
     SV *av = sv_2mortal((SV*)av_make(items, MARK+1));
     SP = ORIGMARK;             /* av_make() might realloc stack_sp */
     I32 items = SP - MARK;
     SV *av = sv_2mortal((SV*)av_make(items, MARK+1));
     SP = ORIGMARK;             /* av_make() might realloc stack_sp */
@@ -2946,7 +3758,7 @@ PP(pp_anonlist)
 
 PP(pp_anonhash)
 {
 
 PP(pp_anonhash)
 {
-    djSP; dMARK; dORIGMARK;
+    dSP; dMARK; dORIGMARK;
     HV* hv = (HV*)sv_2mortal((SV*)newHV());
 
     while (MARK < SP) {
     HV* hv = (HV*)sv_2mortal((SV*)newHV());
 
     while (MARK < SP) {
@@ -2965,7 +3777,7 @@ PP(pp_anonhash)
 
 PP(pp_splice)
 {
 
 PP(pp_splice)
 {
-    djSP; dMARK; dORIGMARK;
+    dSP; dMARK; dORIGMARK;
     register AV *ary = (AV*)*++MARK;
     register SV **src;
     register SV **dst;
     register AV *ary = (AV*)*++MARK;
     register SV **src;
     register SV **dst;
@@ -2978,7 +3790,7 @@ PP(pp_splice)
     SV **tmparyval = 0;
     MAGIC *mg;
 
     SV **tmparyval = 0;
     MAGIC *mg;
 
-    if ((mg = SvTIED_mg((SV*)ary, 'P'))) {
+    if ((mg = SvTIED_mg((SV*)ary, PERL_MAGIC_tied))) {
        *MARK-- = SvTIED_obj((SV*)ary, mg);
        PUSHMARK(MARK);
        PUTBACK;
        *MARK-- = SvTIED_obj((SV*)ary, mg);
        PUSHMARK(MARK);
        PUTBACK;
@@ -3167,12 +3979,12 @@ PP(pp_splice)
 
 PP(pp_push)
 {
 
 PP(pp_push)
 {
-    djSP; dMARK; dORIGMARK; dTARGET;
+    dSP; dMARK; dORIGMARK; dTARGET;
     register AV *ary = (AV*)*++MARK;
     register SV *sv = &PL_sv_undef;
     MAGIC *mg;
 
     register AV *ary = (AV*)*++MARK;
     register SV *sv = &PL_sv_undef;
     MAGIC *mg;
 
-    if ((mg = SvTIED_mg((SV*)ary, 'P'))) {
+    if ((mg = SvTIED_mg((SV*)ary, PERL_MAGIC_tied))) {
        *MARK-- = SvTIED_obj((SV*)ary, mg);
        PUSHMARK(MARK);
        PUTBACK;
        *MARK-- = SvTIED_obj((SV*)ary, mg);
        PUSHMARK(MARK);
        PUTBACK;
@@ -3197,7 +4009,7 @@ PP(pp_push)
 
 PP(pp_pop)
 {
 
 PP(pp_pop)
 {
-    djSP;
+    dSP;
     AV *av = (AV*)POPs;
     SV *sv = av_pop(av);
     if (AvREAL(av))
     AV *av = (AV*)POPs;
     SV *sv = av_pop(av);
     if (AvREAL(av))
@@ -3208,7 +4020,7 @@ PP(pp_pop)
 
 PP(pp_shift)
 {
 
 PP(pp_shift)
 {
-    djSP;
+    dSP;
     AV *av = (AV*)POPs;
     SV *sv = av_shift(av);
     EXTEND(SP, 1);
     AV *av = (AV*)POPs;
     SV *sv = av_shift(av);
     EXTEND(SP, 1);
@@ -3222,13 +4034,13 @@ PP(pp_shift)
 
 PP(pp_unshift)
 {
 
 PP(pp_unshift)
 {
-    djSP; dMARK; dORIGMARK; dTARGET;
+    dSP; dMARK; dORIGMARK; dTARGET;
     register AV *ary = (AV*)*++MARK;
     register SV *sv;
     register I32 i = 0;
     MAGIC *mg;
 
     register AV *ary = (AV*)*++MARK;
     register SV *sv;
     register I32 i = 0;
     MAGIC *mg;
 
-    if ((mg = SvTIED_mg((SV*)ary, 'P'))) {
+    if ((mg = SvTIED_mg((SV*)ary, PERL_MAGIC_tied))) {
        *MARK-- = SvTIED_obj((SV*)ary, mg);
        PUSHMARK(MARK);
        PUTBACK;
        *MARK-- = SvTIED_obj((SV*)ary, mg);
        PUSHMARK(MARK);
        PUTBACK;
@@ -3252,7 +4064,7 @@ PP(pp_unshift)
 
 PP(pp_reverse)
 {
 
 PP(pp_reverse)
 {
-    djSP; dMARK;
+    dSP; dMARK;
     register SV *tmp;
     SV **oldsp = SP;
 
     register SV *tmp;
     SV **oldsp = SP;
 
@@ -3284,20 +4096,17 @@ PP(pp_reverse)
                U8* s = (U8*)SvPVX(TARG);
                U8* send = (U8*)(s + len);
                while (s < send) {
                U8* s = (U8*)SvPVX(TARG);
                U8* send = (U8*)(s + len);
                while (s < send) {
-                   if (*s < 0x80) {
+                   if (UTF8_IS_INVARIANT(*s)) {
                        s++;
                        continue;
                    }
                    else {
                        s++;
                        continue;
                    }
                    else {
+                       if (!utf8_to_uvchr(s, 0))
+                           break;
                        up = (char*)s;
                        s += UTF8SKIP(s);
                        down = (char*)(s - 1);
                        up = (char*)s;
                        s += UTF8SKIP(s);
                        down = (char*)(s - 1);
-                       if (s > send || !((*down & 0xc0) == 0x80)) {
-                           if (ckWARN_d(WARN_UTF8))
-                               Perl_warner(aTHX_ WARN_UTF8,
-                                           "Malformed UTF-8 character");
-                           break;
-                       }
+                       /* reverse this character */
                        while (down > up) {
                            tmp = *up;
                            *up++ = *down;
                        while (down > up) {
                            tmp = *up;
                            *up++ = *down;
@@ -3321,1743 +4130,23 @@ PP(pp_reverse)
     RETURN;
 }
 
     RETURN;
 }
 
-STATIC SV *
-S_mul128(pTHX_ SV *sv, U8 m)
-{
-  STRLEN          len;
-  char           *s = SvPV(sv, len);
-  char           *t;
-  U32             i = 0;
-
-  if (!strnEQ(s, "0000", 4)) {  /* need to grow sv */
-    SV             *tmpNew = newSVpvn("0000000000", 10);
-
-    sv_catsv(tmpNew, sv);
-    SvREFCNT_dec(sv);          /* free old sv */
-    sv = tmpNew;
-    s = SvPV(sv, len);
-  }
-  t = s + len - 1;
-  while (!*t)                   /* trailing '\0'? */
-    t--;
-  while (t > s) {
-    i = ((*t - '0') << 7) + m;
-    *(t--) = '0' + (i % 10);
-    m = i / 10;
-  }
-  return (sv);
-}
-
-/* Explosives and implosives. */
-
-#if 'I' == 73 && 'J' == 74
-/* On an ASCII/ISO kind of system */
-#define ISUUCHAR(ch)    ((ch) >= ' ' && (ch) < 'a')
-#else
-/*
-  Some other sort of character set - use memchr() so we don't match
-  the null byte.
- */
-#define ISUUCHAR(ch)    (memchr(PL_uuemap, (ch), sizeof(PL_uuemap)-1) || (ch) == ' ')
-#endif
-
-PP(pp_unpack)
-{
-    djSP;
-    dPOPPOPssrl;
-    I32 start_sp_offset = SP - PL_stack_base;
-    I32 gimme = GIMME_V;
-    SV *sv;
-    STRLEN llen;
-    STRLEN rlen;
-    register char *pat = SvPV(left, llen);
-    register char *s = SvPV(right, rlen);
-    char *strend = s + rlen;
-    char *strbeg = s;
-    register char *patend = pat + llen;
-    I32 datumtype;
-    register I32 len;
-    register I32 bits;
-    register char *str;
-
-    /* These must not be in registers: */
-    short ashort;
-    int aint;
-    long along;
-#ifdef HAS_QUAD
-    Quad_t aquad;
-#endif
-    U16 aushort;
-    unsigned int auint;
-    U32 aulong;
-#ifdef HAS_QUAD
-    Uquad_t auquad;
-#endif
-    char *aptr;
-    float afloat;
-    double adouble;
-    I32 checksum = 0;
-    register U32 culong;
-    NV cdouble;
-    int commas = 0;
-    int star;
-#ifdef PERL_NATINT_PACK
-    int natint;                /* native integer */
-    int unatint;       /* unsigned native integer */
-#endif
-
-    if (gimme != G_ARRAY) {            /* arrange to do first one only */
-       /*SUPPRESS 530*/
-       for (patend = pat; !isALPHA(*patend) || *patend == 'x'; patend++) ;
-       if (strchr("aAZbBhHP", *patend) || *pat == '%') {
-           patend++;
-           while (isDIGIT(*patend) || *patend == '*')
-               patend++;
-       }
-       else
-           patend++;
-    }
-    while (pat < patend) {
-      reparse:
-       datumtype = *pat++ & 0xFF;
-#ifdef PERL_NATINT_PACK
-       natint = 0;
-#endif
-       if (isSPACE(datumtype))
-           continue;
-       if (datumtype == '#') {
-           while (pat < patend && *pat != '\n')
-               pat++;
-           continue;
-       }
-       if (*pat == '!') {
-           char *natstr = "sSiIlL";
-
-           if (strchr(natstr, datumtype)) {
-#ifdef PERL_NATINT_PACK
-               natint = 1;
-#endif
-               pat++;
-           }
-           else
-               DIE(aTHX_ "'!' allowed only after types %s", natstr);
-       }
-       star = 0;
-       if (pat >= patend)
-           len = 1;
-       else if (*pat == '*') {
-           len = strend - strbeg;      /* long enough */
-           pat++;
-           star = 1;
-       }
-       else if (isDIGIT(*pat)) {
-           len = *pat++ - '0';
-           while (isDIGIT(*pat)) {
-               len = (len * 10) + (*pat++ - '0');
-               if (len < 0)
-                   DIE(aTHX_ "Repeat count in unpack overflows");
-           }
-       }
-       else
-           len = (datumtype != '@');
-      redo_switch:
-       switch(datumtype) {
-       default:
-           DIE(aTHX_ "Invalid type in unpack: '%c'", (int)datumtype);
-       case ',': /* grandfather in commas but with a warning */
-           if (commas++ == 0 && ckWARN(WARN_UNPACK))
-               Perl_warner(aTHX_ WARN_UNPACK,
-                           "Invalid type in unpack: '%c'", (int)datumtype);
-           break;
-       case '%':
-           if (len == 1 && pat[-1] != '1')
-               len = 16;
-           checksum = len;
-           culong = 0;
-           cdouble = 0;
-           if (pat < patend)
-               goto reparse;
-           break;
-       case '@':
-           if (len > strend - strbeg)
-               DIE(aTHX_ "@ outside of string");
-           s = strbeg + len;
-           break;
-       case 'X':
-           if (len > s - strbeg)
-               DIE(aTHX_ "X outside of string");
-           s -= len;
-           break;
-       case 'x':
-           if (len > strend - s)
-               DIE(aTHX_ "x outside of string");
-           s += len;
-           break;
-       case '/':
-           if (start_sp_offset >= SP - PL_stack_base)
-               DIE(aTHX_ "/ must follow a numeric type");
-           datumtype = *pat++;
-           if (*pat == '*')
-               pat++;          /* ignore '*' for compatibility with pack */
-           if (isDIGIT(*pat))
-               DIE(aTHX_ "/ cannot take a count" );
-           len = POPi;
-           star = 0;
-           goto redo_switch;
-       case 'A':
-       case 'Z':
-       case 'a':
-           if (len > strend - s)
-               len = strend - s;
-           if (checksum)
-               goto uchar_checksum;
-           sv = NEWSV(35, len);
-           sv_setpvn(sv, s, len);
-           s += len;
-           if (datumtype == 'A' || datumtype == 'Z') {
-               aptr = s;       /* borrow register */
-               if (datumtype == 'Z') { /* 'Z' strips stuff after first null */
-                   s = SvPVX(sv);
-                   while (*s)
-                       s++;
-               }
-               else {          /* 'A' strips both nulls and spaces */
-                   s = SvPVX(sv) + len - 1;
-                   while (s >= SvPVX(sv) && (!*s || isSPACE(*s)))
-                       s--;
-                   *++s = '\0';
-               }
-               SvCUR_set(sv, s - SvPVX(sv));
-               s = aptr;       /* unborrow register */
-           }
-           XPUSHs(sv_2mortal(sv));
-           break;
-       case 'B':
-       case 'b':
-           if (star || len > (strend - s) * 8)
-               len = (strend - s) * 8;
-           if (checksum) {
-               if (!PL_bitcount) {
-                   Newz(601, PL_bitcount, 256, char);
-                   for (bits = 1; bits < 256; bits++) {
-                       if (bits & 1)   PL_bitcount[bits]++;
-                       if (bits & 2)   PL_bitcount[bits]++;
-                       if (bits & 4)   PL_bitcount[bits]++;
-                       if (bits & 8)   PL_bitcount[bits]++;
-                       if (bits & 16)  PL_bitcount[bits]++;
-                       if (bits & 32)  PL_bitcount[bits]++;
-                       if (bits & 64)  PL_bitcount[bits]++;
-                       if (bits & 128) PL_bitcount[bits]++;
-                   }
-               }
-               while (len >= 8) {
-                   culong += PL_bitcount[*(unsigned char*)s++];
-                   len -= 8;
-               }
-               if (len) {
-                   bits = *s;
-                   if (datumtype == 'b') {
-                       while (len-- > 0) {
-                           if (bits & 1) culong++;
-                           bits >>= 1;
-                       }
-                   }
-                   else {
-                       while (len-- > 0) {
-                           if (bits & 128) culong++;
-                           bits <<= 1;
-                       }
-                   }
-               }
-               break;
-           }
-           sv = NEWSV(35, len + 1);
-           SvCUR_set(sv, len);
-           SvPOK_on(sv);
-           str = SvPVX(sv);
-           if (datumtype == 'b') {
-               aint = len;
-               for (len = 0; len < aint; len++) {
-                   if (len & 7)                /*SUPPRESS 595*/
-                       bits >>= 1;
-                   else
-                       bits = *s++;
-                   *str++ = '0' + (bits & 1);
-               }
-           }
-           else {
-               aint = len;
-               for (len = 0; len < aint; len++) {
-                   if (len & 7)
-                       bits <<= 1;
-                   else
-                       bits = *s++;
-                   *str++ = '0' + ((bits & 128) != 0);
-               }
-           }
-           *str = '\0';
-           XPUSHs(sv_2mortal(sv));
-           break;
-       case 'H':
-       case 'h':
-           if (star || len > (strend - s) * 2)
-               len = (strend - s) * 2;
-           sv = NEWSV(35, len + 1);
-           SvCUR_set(sv, len);
-           SvPOK_on(sv);
-           str = SvPVX(sv);
-           if (datumtype == 'h') {
-               aint = len;
-               for (len = 0; len < aint; len++) {
-                   if (len & 1)
-                       bits >>= 4;
-                   else
-                       bits = *s++;
-                   *str++ = PL_hexdigit[bits & 15];
-               }
-           }
-           else {
-               aint = len;
-               for (len = 0; len < aint; len++) {
-                   if (len & 1)
-                       bits <<= 4;
-                   else
-                       bits = *s++;
-                   *str++ = PL_hexdigit[(bits >> 4) & 15];
-               }
-           }
-           *str = '\0';
-           XPUSHs(sv_2mortal(sv));
-           break;
-       case 'c':
-           if (len > strend - s)
-               len = strend - s;
-           if (checksum) {
-               while (len-- > 0) {
-                   aint = *s++;
-                   if (aint >= 128)    /* fake up signed chars */
-                       aint -= 256;
-                   culong += aint;
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-               while (len-- > 0) {
-                   aint = *s++;
-                   if (aint >= 128)    /* fake up signed chars */
-                       aint -= 256;
-                   sv = NEWSV(36, 0);
-                   sv_setiv(sv, (IV)aint);
-                   PUSHs(sv_2mortal(sv));
-               }
-           }
-           break;
-       case 'C':
-           if (len > strend - s)
-               len = strend - s;
-           if (checksum) {
-             uchar_checksum:
-               while (len-- > 0) {
-                   auint = *s++ & 255;
-                   culong += auint;
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-               while (len-- > 0) {
-                   auint = *s++ & 255;
-                   sv = NEWSV(37, 0);
-                   sv_setiv(sv, (IV)auint);
-                   PUSHs(sv_2mortal(sv));
-               }
-           }
-           break;
-       case 'U':
-           if (len > strend - s)
-               len = strend - s;
-           if (checksum) {
-               while (len-- > 0 && s < strend) {
-                   STRLEN alen;
-                   auint = utf8_to_uv((U8*)s, strend - s, &alen, 0);
-                   along = alen;
-                   s += along;
-                   if (checksum > 32)
-                       cdouble += (NV)auint;
-                   else
-                       culong += auint;
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-               while (len-- > 0 && s < strend) {
-                   STRLEN alen;
-                   auint = utf8_to_uv((U8*)s, strend - s, &alen, 0);
-                   along = alen;
-                   s += along;
-                   sv = NEWSV(37, 0);
-                   sv_setuv(sv, (UV)auint);
-                   PUSHs(sv_2mortal(sv));
-               }
-           }
-           break;
-       case 's':
-#if SHORTSIZE == SIZE16
-           along = (strend - s) / SIZE16;
-#else
-           along = (strend - s) / (natint ? sizeof(short) : SIZE16);
-#endif
-           if (len > along)
-               len = along;
-           if (checksum) {
-#if SHORTSIZE != SIZE16
-               if (natint) {
-                   short ashort;
-                   while (len-- > 0) {
-                       COPYNN(s, &ashort, sizeof(short));
-                       s += sizeof(short);
-                       culong += ashort;
-
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-                       COPY16(s, &ashort);
-#if SHORTSIZE > SIZE16
-                       if (ashort > 32767)
-                         ashort -= 65536;
-#endif
-                       s += SIZE16;
-                       culong += ashort;
-                   }
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-#if SHORTSIZE != SIZE16
-               if (natint) {
-                   short ashort;
-                   while (len-- > 0) {
-                       COPYNN(s, &ashort, sizeof(short));
-                       s += sizeof(short);
-                       sv = NEWSV(38, 0);
-                       sv_setiv(sv, (IV)ashort);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-                       COPY16(s, &ashort);
-#if SHORTSIZE > SIZE16
-                       if (ashort > 32767)
-                         ashort -= 65536;
-#endif
-                       s += SIZE16;
-                       sv = NEWSV(38, 0);
-                       sv_setiv(sv, (IV)ashort);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-           }
-           break;
-       case 'v':
-       case 'n':
-       case 'S':
-#if SHORTSIZE == SIZE16
-           along = (strend - s) / SIZE16;
-#else
-           unatint = natint && datumtype == 'S';
-           along = (strend - s) / (unatint ? sizeof(unsigned short) : SIZE16);
-#endif
-           if (len > along)
-               len = along;
-           if (checksum) {
-#if SHORTSIZE != SIZE16
-               if (unatint) {
-                   unsigned short aushort;
-                   while (len-- > 0) {
-                       COPYNN(s, &aushort, sizeof(unsigned short));
-                       s += sizeof(unsigned short);
-                       culong += aushort;
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-                       COPY16(s, &aushort);
-                       s += SIZE16;
-#ifdef HAS_NTOHS
-                       if (datumtype == 'n')
-                           aushort = PerlSock_ntohs(aushort);
-#endif
-#ifdef HAS_VTOHS
-                       if (datumtype == 'v')
-                           aushort = vtohs(aushort);
-#endif
-                       culong += aushort;
-                   }
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-#if SHORTSIZE != SIZE16
-               if (unatint) {
-                   unsigned short aushort;
-                   while (len-- > 0) {
-                       COPYNN(s, &aushort, sizeof(unsigned short));
-                       s += sizeof(unsigned short);
-                       sv = NEWSV(39, 0);
-                       sv_setiv(sv, (UV)aushort);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-                       COPY16(s, &aushort);
-                       s += SIZE16;
-                       sv = NEWSV(39, 0);
-#ifdef HAS_NTOHS
-                       if (datumtype == 'n')
-                           aushort = PerlSock_ntohs(aushort);
-#endif
-#ifdef HAS_VTOHS
-                       if (datumtype == 'v')
-                           aushort = vtohs(aushort);
-#endif
-                       sv_setiv(sv, (UV)aushort);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-           }
-           break;
-       case 'i':
-           along = (strend - s) / sizeof(int);
-           if (len > along)
-               len = along;
-           if (checksum) {
-               while (len-- > 0) {
-                   Copy(s, &aint, 1, int);
-                   s += sizeof(int);
-                   if (checksum > 32)
-                       cdouble += (NV)aint;
-                   else
-                       culong += aint;
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-               while (len-- > 0) {
-                   Copy(s, &aint, 1, int);
-                   s += sizeof(int);
-                   sv = NEWSV(40, 0);
-#ifdef __osf__
-                    /* Without the dummy below unpack("i", pack("i",-1))
-                     * return 0xFFffFFff instead of -1 for Digital Unix V4.0
-                     * cc with optimization turned on.
-                    *
-                    * The bug was detected in
-                    * DEC C V5.8-009 on Digital UNIX V4.0 (Rev. 1091) (V4.0E)
-                    * with optimization (-O4) turned on.
-                    * DEC C V5.2-040 on Digital UNIX V4.0 (Rev. 564) (V4.0B)
-                    * does not have this problem even with -O4.
-                    *
-                    * This bug was reported as DECC_BUGS 1431
-                    * and tracked internally as GEM_BUGS 7775.
-                    *
-                    * The bug is fixed in
-                    * Tru64 UNIX V5.0:      Compaq C V6.1-006 or later
-                    * UNIX V4.0F support:   DEC C V5.9-006 or later
-                    * UNIX V4.0E support:   DEC C V5.8-011 or later
-                    * and also in DTK.
-                    *
-                    * See also few lines later for the same bug.
-                    */
-                    (aint) ?
-                       sv_setiv(sv, (IV)aint) :
-#endif
-                   sv_setiv(sv, (IV)aint);
-                   PUSHs(sv_2mortal(sv));
-               }
-           }
-           break;
-       case 'I':
-           along = (strend - s) / sizeof(unsigned int);
-           if (len > along)
-               len = along;
-           if (checksum) {
-               while (len-- > 0) {
-                   Copy(s, &auint, 1, unsigned int);
-                   s += sizeof(unsigned int);
-                   if (checksum > 32)
-                       cdouble += (NV)auint;
-                   else
-                       culong += auint;
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-               while (len-- > 0) {
-                   Copy(s, &auint, 1, unsigned int);
-                   s += sizeof(unsigned int);
-                   sv = NEWSV(41, 0);
-#ifdef __osf__
-                    /* Without the dummy below unpack("I", pack("I",0xFFFFFFFF))
-                     * returns 1.84467440737096e+19 instead of 0xFFFFFFFF.
-                    * See details few lines earlier. */
-                    (auint) ?
-                       sv_setuv(sv, (UV)auint) :
-#endif
-                   sv_setuv(sv, (UV)auint);
-                   PUSHs(sv_2mortal(sv));
-               }
-           }
-           break;
-       case 'l':
-#if LONGSIZE == SIZE32
-           along = (strend - s) / SIZE32;
-#else
-           along = (strend - s) / (natint ? sizeof(long) : SIZE32);
-#endif
-           if (len > along)
-               len = along;
-           if (checksum) {
-#if LONGSIZE != SIZE32
-               if (natint) {
-                   while (len-- > 0) {
-                       COPYNN(s, &along, sizeof(long));
-                       s += sizeof(long);
-                       if (checksum > 32)
-                           cdouble += (NV)along;
-                       else
-                           culong += along;
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-#if LONGSIZE > SIZE32 && INTSIZE == SIZE32
-                       I32 along;
-#endif
-                       COPY32(s, &along);
-#if LONGSIZE > SIZE32
-                       if (along > 2147483647)
-                         along -= 4294967296;
-#endif
-                       s += SIZE32;
-                       if (checksum > 32)
-                           cdouble += (NV)along;
-                       else
-                           culong += along;
-                   }
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-#if LONGSIZE != SIZE32
-               if (natint) {
-                   while (len-- > 0) {
-                       COPYNN(s, &along, sizeof(long));
-                       s += sizeof(long);
-                       sv = NEWSV(42, 0);
-                       sv_setiv(sv, (IV)along);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-#if LONGSIZE > SIZE32 && INTSIZE == SIZE32
-                       I32 along;
-#endif
-                       COPY32(s, &along);
-#if LONGSIZE > SIZE32
-                       if (along > 2147483647)
-                         along -= 4294967296;
-#endif
-                       s += SIZE32;
-                       sv = NEWSV(42, 0);
-                       sv_setiv(sv, (IV)along);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-           }
-           break;
-       case 'V':
-       case 'N':
-       case 'L':
-#if LONGSIZE == SIZE32
-           along = (strend - s) / SIZE32;
-#else
-           unatint = natint && datumtype == 'L';
-           along = (strend - s) / (unatint ? sizeof(unsigned long) : SIZE32);
-#endif
-           if (len > along)
-               len = along;
-           if (checksum) {
-#if LONGSIZE != SIZE32
-               if (unatint) {
-                   unsigned long aulong;
-                   while (len-- > 0) {
-                       COPYNN(s, &aulong, sizeof(unsigned long));
-                       s += sizeof(unsigned long);
-                       if (checksum > 32)
-                           cdouble += (NV)aulong;
-                       else
-                           culong += aulong;
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-                       COPY32(s, &aulong);
-                       s += SIZE32;
-#ifdef HAS_NTOHL
-                       if (datumtype == 'N')
-                           aulong = PerlSock_ntohl(aulong);
-#endif
-#ifdef HAS_VTOHL
-                       if (datumtype == 'V')
-                           aulong = vtohl(aulong);
-#endif
-                       if (checksum > 32)
-                           cdouble += (NV)aulong;
-                       else
-                           culong += aulong;
-                   }
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-#if LONGSIZE != SIZE32
-               if (unatint) {
-                   unsigned long aulong;
-                   while (len-- > 0) {
-                       COPYNN(s, &aulong, sizeof(unsigned long));
-                       s += sizeof(unsigned long);
-                       sv = NEWSV(43, 0);
-                       sv_setuv(sv, (UV)aulong);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-               else
-#endif
-                {
-                   while (len-- > 0) {
-                       COPY32(s, &aulong);
-                       s += SIZE32;
-#ifdef HAS_NTOHL
-                       if (datumtype == 'N')
-                           aulong = PerlSock_ntohl(aulong);
-#endif
-#ifdef HAS_VTOHL
-                       if (datumtype == 'V')
-                           aulong = vtohl(aulong);
-#endif
-                       sv = NEWSV(43, 0);
-                       sv_setuv(sv, (UV)aulong);
-                       PUSHs(sv_2mortal(sv));
-                   }
-               }
-           }
-           break;
-       case 'p':
-           along = (strend - s) / sizeof(char*);
-           if (len > along)
-               len = along;
-           EXTEND(SP, len);
-           EXTEND_MORTAL(len);
-           while (len-- > 0) {
-               if (sizeof(char*) > strend - s)
-                   break;
-               else {
-                   Copy(s, &aptr, 1, char*);
-                   s += sizeof(char*);
-               }
-               sv = NEWSV(44, 0);
-               if (aptr)
-                   sv_setpv(sv, aptr);
-               PUSHs(sv_2mortal(sv));
-           }
-           break;
-       case 'w':
-           EXTEND(SP, len);
-           EXTEND_MORTAL(len);
-           {
-               UV auv = 0;
-               U32 bytes = 0;
-               
-               while ((len > 0) && (s < strend)) {
-                   auv = (auv << 7) | (*s & 0x7f);
-                   if (!(*s++ & 0x80)) {
-                       bytes = 0;
-                       sv = NEWSV(40, 0);
-                       sv_setuv(sv, auv);
-                       PUSHs(sv_2mortal(sv));
-                       len--;
-                       auv = 0;
-                   }
-                   else if (++bytes >= sizeof(UV)) {   /* promote to string */
-                       char *t;
-                       STRLEN n_a;
-
-                       sv = Perl_newSVpvf(aTHX_ "%.*"UVf, (int)TYPE_DIGITS(UV), auv);
-                       while (s < strend) {
-                           sv = mul128(sv, *s & 0x7f);
-                           if (!(*s++ & 0x80)) {
-                               bytes = 0;
-                               break;
-                           }
-                       }
-                       t = SvPV(sv, n_a);
-                       while (*t == '0')
-                           t++;
-                       sv_chop(sv, t);
-                       PUSHs(sv_2mortal(sv));
-                       len--;
-                       auv = 0;
-                   }
-               }
-               if ((s >= strend) && bytes)
-                   DIE(aTHX_ "Unterminated compressed integer");
-           }
-           break;
-       case 'P':
-           EXTEND(SP, 1);
-           if (sizeof(char*) > strend - s)
-               break;
-           else {
-               Copy(s, &aptr, 1, char*);
-               s += sizeof(char*);
-           }
-           sv = NEWSV(44, 0);
-           if (aptr)
-               sv_setpvn(sv, aptr, len);
-           PUSHs(sv_2mortal(sv));
-           break;
-#ifdef HAS_QUAD
-       case 'q':
-           along = (strend - s) / sizeof(Quad_t);
-           if (len > along)
-               len = along;
-           EXTEND(SP, len);
-           EXTEND_MORTAL(len);
-           while (len-- > 0) {
-               if (s + sizeof(Quad_t) > strend)
-                   aquad = 0;
-               else {
-                   Copy(s, &aquad, 1, Quad_t);
-                   s += sizeof(Quad_t);
-               }
-               sv = NEWSV(42, 0);
-               if (aquad >= IV_MIN && aquad <= IV_MAX)
-                   sv_setiv(sv, (IV)aquad);
-               else
-                   sv_setnv(sv, (NV)aquad);
-               PUSHs(sv_2mortal(sv));
-           }
-           break;
-       case 'Q':
-           along = (strend - s) / sizeof(Quad_t);
-           if (len > along)
-               len = along;
-           EXTEND(SP, len);
-           EXTEND_MORTAL(len);
-           while (len-- > 0) {
-               if (s + sizeof(Uquad_t) > strend)
-                   auquad = 0;
-               else {
-                   Copy(s, &auquad, 1, Uquad_t);
-                   s += sizeof(Uquad_t);
-               }
-               sv = NEWSV(43, 0);
-               if (auquad <= UV_MAX)
-                   sv_setuv(sv, (UV)auquad);
-               else
-                   sv_setnv(sv, (NV)auquad);
-               PUSHs(sv_2mortal(sv));
-           }
-           break;
-#endif
-       /* float and double added gnb@melba.bby.oz.au 22/11/89 */
-       case 'f':
-       case 'F':
-           along = (strend - s) / sizeof(float);
-           if (len > along)
-               len = along;
-           if (checksum) {
-               while (len-- > 0) {
-                   Copy(s, &afloat, 1, float);
-                   s += sizeof(float);
-                   cdouble += afloat;
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-               while (len-- > 0) {
-                   Copy(s, &afloat, 1, float);
-                   s += sizeof(float);
-                   sv = NEWSV(47, 0);
-                   sv_setnv(sv, (NV)afloat);
-                   PUSHs(sv_2mortal(sv));
-               }
-           }
-           break;
-       case 'd':
-       case 'D':
-           along = (strend - s) / sizeof(double);
-           if (len > along)
-               len = along;
-           if (checksum) {
-               while (len-- > 0) {
-                   Copy(s, &adouble, 1, double);
-                   s += sizeof(double);
-                   cdouble += adouble;
-               }
-           }
-           else {
-               EXTEND(SP, len);
-               EXTEND_MORTAL(len);
-               while (len-- > 0) {
-                   Copy(s, &adouble, 1, double);
-                   s += sizeof(double);
-                   sv = NEWSV(48, 0);
-                   sv_setnv(sv, (NV)adouble);
-                   PUSHs(sv_2mortal(sv));
-               }
-           }
-           break;
-       case 'u':
-           /* MKS:
-            * Initialise the decode mapping.  By using a table driven
-             * algorithm, the code will be character-set independent
-             * (and just as fast as doing character arithmetic)
-             */
-            if (PL_uudmap['M'] == 0) {
-                int i;
-
-                for (i = 0; i < sizeof(PL_uuemap); i += 1)
-                    PL_uudmap[(U8)PL_uuemap[i]] = i;
-                /*
-                 * Because ' ' and '`' map to the same value,
-                 * we need to decode them both the same.
-                 */
-                PL_uudmap[' '] = 0;
-            }
-
-           along = (strend - s) * 3 / 4;
-           sv = NEWSV(42, along);
-           if (along)
-               SvPOK_on(sv);
-           while (s < strend && *s > ' ' && ISUUCHAR(*s)) {
-               I32 a, b, c, d;
-               char hunk[4];
-
-               hunk[3] = '\0';
-               len = PL_uudmap[*(U8*)s++] & 077;
-               while (len > 0) {
-                   if (s < strend && ISUUCHAR(*s))
-                       a = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       a = 0;
-                   if (s < strend && ISUUCHAR(*s))
-                       b = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       b = 0;
-                   if (s < strend && ISUUCHAR(*s))
-                       c = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       c = 0;
-                   if (s < strend && ISUUCHAR(*s))
-                       d = PL_uudmap[*(U8*)s++] & 077;
-                   else
-                       d = 0;
-                   hunk[0] = (a << 2) | (b >> 4);
-                   hunk[1] = (b << 4) | (c >> 2);
-                   hunk[2] = (c << 6) | d;
-                   sv_catpvn(sv, hunk, (len > 3) ? 3 : len);
-                   len -= 3;
-               }
-               if (*s == '\n')
-                   s++;
-               else if (s[1] == '\n')          /* possible checksum byte */
-                   s += 2;
-           }
-           XPUSHs(sv_2mortal(sv));
-           break;
-       }
-       if (checksum) {
-           sv = NEWSV(42, 0);
-           if (strchr("fFdD", datumtype) ||
-             (checksum > 32 && strchr("iIlLNU", datumtype)) ) {
-               NV trouble;
-
-               adouble = 1.0;
-               while (checksum >= 16) {
-                   checksum -= 16;
-                   adouble *= 65536.0;
-               }
-               while (checksum >= 4) {
-                   checksum -= 4;
-                   adouble *= 16.0;
-               }
-               while (checksum--)
-                   adouble *= 2.0;
-               along = (1 << checksum) - 1;
-               while (cdouble < 0.0)
-                   cdouble += adouble;
-               cdouble = Perl_modf(cdouble / adouble, &trouble) * adouble;
-               sv_setnv(sv, cdouble);
-           }
-           else {
-               if (checksum < 32) {
-                   aulong = (1 << checksum) - 1;
-                   culong &= aulong;
-               }
-               sv_setuv(sv, (UV)culong);
-           }
-           XPUSHs(sv_2mortal(sv));
-           checksum = 0;
-       }
-    }
-    if (SP - PL_stack_base == start_sp_offset && gimme == G_SCALAR)
-       PUSHs(&PL_sv_undef);
-    RETURN;
-}
-
-STATIC void
-S_doencodes(pTHX_ register SV *sv, register char *s, register I32 len)
-{
-    char hunk[5];
-
-    *hunk = PL_uuemap[len];
-    sv_catpvn(sv, hunk, 1);
-    hunk[4] = '\0';
-    while (len > 2) {
-       hunk[0] = PL_uuemap[(077 & (*s >> 2))];
-       hunk[1] = PL_uuemap[(077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017)))];
-       hunk[2] = PL_uuemap[(077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03)))];
-       hunk[3] = PL_uuemap[(077 & (s[2] & 077))];
-       sv_catpvn(sv, hunk, 4);
-       s += 3;
-       len -= 3;
-    }
-    if (len > 0) {
-       char r = (len > 1 ? s[1] : '\0');
-       hunk[0] = PL_uuemap[(077 & (*s >> 2))];
-       hunk[1] = PL_uuemap[(077 & (((*s << 4) & 060) | ((r >> 4) & 017)))];
-       hunk[2] = PL_uuemap[(077 & ((r << 2) & 074))];
-       hunk[3] = PL_uuemap[0];
-       sv_catpvn(sv, hunk, 4);
-    }
-    sv_catpvn(sv, "\n", 1);
-}
-
-STATIC SV *
-S_is_an_int(pTHX_ char *s, STRLEN l)
-{
-  STRLEN        n_a;
-  SV             *result = newSVpvn(s, l);
-  char           *result_c = SvPV(result, n_a);        /* convenience */
-  char           *out = result_c;
-  bool            skip = 1;
-  bool            ignore = 0;
-
-  while (*s) {
-    switch (*s) {
-    case ' ':
-      break;
-    case '+':
-      if (!skip) {
-       SvREFCNT_dec(result);
-       return (NULL);
-      }
-      break;
-    case '0':
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-    case '5':
-    case '6':
-    case '7':
-    case '8':
-    case '9':
-      skip = 0;
-      if (!ignore) {
-       *(out++) = *s;
-      }
-      break;
-    case '.':
-      ignore = 1;
-      break;
-    default:
-      SvREFCNT_dec(result);
-      return (NULL);
-    }
-    s++;
-  }
-  *(out++) = '\0';
-  SvCUR_set(result, out - result_c);
-  return (result);
-}
-
-/* pnum must be '\0' terminated */
-STATIC int
-S_div128(pTHX_ SV *pnum, bool *done)
-{
-  STRLEN          len;
-  char           *s = SvPV(pnum, len);
-  int             m = 0;
-  int             r = 0;
-  char           *t = s;
-
-  *done = 1;
-  while (*t) {
-    int             i;
-
-    i = m * 10 + (*t - '0');
-    m = i & 0x7F;
-    r = (i >> 7);              /* r < 10 */
-    if (r) {
-      *done = 0;
-    }
-    *(t++) = '0' + r;
-  }
-  *(t++) = '\0';
-  SvCUR_set(pnum, (STRLEN) (t - s));
-  return (m);
-}
-
-
-PP(pp_pack)
-{
-    djSP; dMARK; dORIGMARK; dTARGET;
-    register SV *cat = TARG;
-    register I32 items;
-    STRLEN fromlen;
-    register char *pat = SvPVx(*++MARK, fromlen);
-    char *patcopy;
-    register char *patend = pat + fromlen;
-    register I32 len;
-    I32 datumtype;
-    SV *fromstr;
-    /*SUPPRESS 442*/
-    static char null10[] = {0,0,0,0,0,0,0,0,0,0};
-    static char *space10 = "          ";
-
-    /* These must not be in registers: */
-    char achar;
-    I16 ashort;
-    int aint;
-    unsigned int auint;
-    I32 along;
-    U32 aulong;
-#ifdef HAS_QUAD
-    Quad_t aquad;
-    Uquad_t auquad;
-#endif
-    char *aptr;
-    float afloat;
-    double adouble;
-    int commas = 0;
-#ifdef PERL_NATINT_PACK
-    int natint;                /* native integer */
-#endif
-
-    items = SP - MARK;
-    MARK++;
-    sv_setpvn(cat, "", 0);
-    patcopy = pat;
-    while (pat < patend) {
-       SV *lengthcode = Nullsv;
-#define NEXTFROM ( lengthcode ? lengthcode : items-- > 0 ? *MARK++ : &PL_sv_no)
-       datumtype = *pat++ & 0xFF;
-#ifdef PERL_NATINT_PACK
-       natint = 0;
-#endif
-       if (isSPACE(datumtype)) {
-           patcopy++;
-           continue;
-        }
-       if (datumtype == 'U' && pat == patcopy+1)
-           SvUTF8_on(cat);
-       if (datumtype == '#') {
-           while (pat < patend && *pat != '\n')
-               pat++;
-           continue;
-       }
-        if (*pat == '!') {
-           char *natstr = "sSiIlL";
-
-           if (strchr(natstr, datumtype)) {
-#ifdef PERL_NATINT_PACK
-               natint = 1;
-#endif
-               pat++;
-           }
-           else
-               DIE(aTHX_ "'!' allowed only after types %s", natstr);
-       }
-       if (*pat == '*') {
-           len = strchr("@Xxu", datumtype) ? 0 : items;
-           pat++;
-       }
-       else if (isDIGIT(*pat)) {
-           len = *pat++ - '0';
-           while (isDIGIT(*pat)) {
-               len = (len * 10) + (*pat++ - '0');
-               if (len < 0)
-                   DIE(aTHX_ "Repeat count in pack overflows");
-           }
-       }
-       else
-           len = 1;
-       if (*pat == '/') {
-           ++pat;
-           if ((*pat != 'a' && *pat != 'A' && *pat != 'Z') || pat[1] != '*')
-               DIE(aTHX_ "/ must be followed by a*, A* or Z*");
-           lengthcode = sv_2mortal(newSViv(sv_len(items > 0
-                                                  ? *MARK : &PL_sv_no)
-                                            + (*pat == 'Z' ? 1 : 0)));
-       }
-       switch(datumtype) {
-       default:
-           DIE(aTHX_ "Invalid type in pack: '%c'", (int)datumtype);
-       case ',': /* grandfather in commas but with a warning */
-           if (commas++ == 0 && ckWARN(WARN_PACK))
-               Perl_warner(aTHX_ WARN_PACK,
-                           "Invalid type in pack: '%c'", (int)datumtype);
-           break;
-       case '%':
-           DIE(aTHX_ "%% may only be used in unpack");
-       case '@':
-           len -= SvCUR(cat);
-           if (len > 0)
-               goto grow;
-           len = -len;
-           if (len > 0)
-               goto shrink;
-           break;
-       case 'X':
-         shrink:
-           if (SvCUR(cat) < len)
-               DIE(aTHX_ "X outside of string");
-           SvCUR(cat) -= len;
-           *SvEND(cat) = '\0';
-           break;
-       case 'x':
-         grow:
-           while (len >= 10) {
-               sv_catpvn(cat, null10, 10);
-               len -= 10;
-           }
-           sv_catpvn(cat, null10, len);
-           break;
-       case 'A':
-       case 'Z':
-       case 'a':
-           fromstr = NEXTFROM;
-           aptr = SvPV(fromstr, fromlen);
-           if (pat[-1] == '*') {
-               len = fromlen;
-               if (datumtype == 'Z')
-                   ++len;
-           }
-           if (fromlen >= len) {
-               sv_catpvn(cat, aptr, len);
-               if (datumtype == 'Z')
-                   *(SvEND(cat)-1) = '\0';
-           }
-           else {
-               sv_catpvn(cat, aptr, fromlen);
-               len -= fromlen;
-               if (datumtype == 'A') {
-                   while (len >= 10) {
-                       sv_catpvn(cat, space10, 10);
-                       len -= 10;
-                   }
-                   sv_catpvn(cat, space10, len);
-               }
-               else {
-                   while (len >= 10) {
-                       sv_catpvn(cat, null10, 10);
-                       len -= 10;
-                   }
-                   sv_catpvn(cat, null10, len);
-               }
-           }
-           break;
-       case 'B':
-       case 'b':
-           {
-               register char *str;
-               I32 saveitems;
-
-               fromstr = NEXTFROM;
-               saveitems = items;
-               str = SvPV(fromstr, fromlen);
-               if (pat[-1] == '*')
-                   len = fromlen;
-               aint = SvCUR(cat);
-               SvCUR(cat) += (len+7)/8;
-               SvGROW(cat, SvCUR(cat) + 1);
-               aptr = SvPVX(cat) + aint;
-               if (len > fromlen)
-                   len = fromlen;
-               aint = len;
-               items = 0;
-               if (datumtype == 'B') {
-                   for (len = 0; len++ < aint;) {
-                       items |= *str++ & 1;
-                       if (len & 7)
-                           items <<= 1;
-                       else {
-                           *aptr++ = items & 0xff;
-                           items = 0;
-                       }
-                   }
-               }
-               else {
-                   for (len = 0; len++ < aint;) {
-                       if (*str++ & 1)
-                           items |= 128;
-                       if (len & 7)
-                           items >>= 1;
-                       else {
-                           *aptr++ = items & 0xff;
-                           items = 0;
-                       }
-                   }
-               }
-               if (aint & 7) {
-                   if (datumtype == 'B')
-                       items <<= 7 - (aint & 7);
-                   else
-                       items >>= 7 - (aint & 7);
-                   *aptr++ = items & 0xff;
-               }
-               str = SvPVX(cat) + SvCUR(cat);
-               while (aptr <= str)
-                   *aptr++ = '\0';
-
-               items = saveitems;
-           }
-           break;
-       case 'H':
-       case 'h':
-           {
-               register char *str;
-               I32 saveitems;
-
-               fromstr = NEXTFROM;
-               saveitems = items;
-               str = SvPV(fromstr, fromlen);
-               if (pat[-1] == '*')
-                   len = fromlen;
-               aint = SvCUR(cat);
-               SvCUR(cat) += (len+1)/2;
-               SvGROW(cat, SvCUR(cat) + 1);
-               aptr = SvPVX(cat) + aint;
-               if (len > fromlen)
-                   len = fromlen;
-               aint = len;
-               items = 0;
-               if (datumtype == 'H') {
-                   for (len = 0; len++ < aint;) {
-                       if (isALPHA(*str))
-                           items |= ((*str++ & 15) + 9) & 15;
-                       else
-                           items |= *str++ & 15;
-                       if (len & 1)
-                           items <<= 4;
-                       else {
-                           *aptr++ = items & 0xff;
-                           items = 0;
-                       }
-                   }
-               }
-               else {
-                   for (len = 0; len++ < aint;) {
-                       if (isALPHA(*str))
-                           items |= (((*str++ & 15) + 9) & 15) << 4;
-                       else
-                           items |= (*str++ & 15) << 4;
-                       if (len & 1)
-                           items >>= 4;
-                       else {
-                           *aptr++ = items & 0xff;
-                           items = 0;
-                       }
-                   }
-               }
-               if (aint & 1)
-                   *aptr++ = items & 0xff;
-               str = SvPVX(cat) + SvCUR(cat);
-               while (aptr <= str)
-                   *aptr++ = '\0';
-
-               items = saveitems;
-           }
-           break;
-       case 'C':
-       case 'c':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               aint = SvIV(fromstr);
-               achar = aint;
-               sv_catpvn(cat, &achar, sizeof(char));
-           }
-           break;
-       case 'U':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               auint = SvUV(fromstr);
-               SvGROW(cat, SvCUR(cat) + UTF8_MAXLEN);
-               SvCUR_set(cat, (char*)uv_to_utf8((U8*)SvEND(cat),auint)
-                              - SvPVX(cat));
-           }
-           *SvEND(cat) = '\0';
-           break;
-       /* Float and double added by gnb@melba.bby.oz.au  22/11/89 */
-       case 'f':
-       case 'F':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               afloat = (float)SvNV(fromstr);
-               sv_catpvn(cat, (char *)&afloat, sizeof (float));
-           }
-           break;
-       case 'd':
-       case 'D':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               adouble = (double)SvNV(fromstr);
-               sv_catpvn(cat, (char *)&adouble, sizeof (double));
-           }
-           break;
-       case 'n':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               ashort = (I16)SvIV(fromstr);
-#ifdef HAS_HTONS
-               ashort = PerlSock_htons(ashort);
-#endif
-               CAT16(cat, &ashort);
-           }
-           break;
-       case 'v':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               ashort = (I16)SvIV(fromstr);
-#ifdef HAS_HTOVS
-               ashort = htovs(ashort);
-#endif
-               CAT16(cat, &ashort);
-           }
-           break;
-       case 'S':
-#if SHORTSIZE != SIZE16
-           if (natint) {
-               unsigned short aushort;
-
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   aushort = SvUV(fromstr);
-                   sv_catpvn(cat, (char *)&aushort, sizeof(unsigned short));
-               }
-           }
-           else
-#endif
-            {
-               U16 aushort;
-
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   aushort = (U16)SvUV(fromstr);
-                   CAT16(cat, &aushort);
-               }
-
-           }
-           break;
-       case 's':
-#if SHORTSIZE != SIZE16
-           if (natint) {
-               short ashort;
-
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   ashort = SvIV(fromstr);
-                   sv_catpvn(cat, (char *)&ashort, sizeof(short));
-               }
-           }
-           else
-#endif
-            {
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   ashort = (I16)SvIV(fromstr);
-                   CAT16(cat, &ashort);
-               }
-           }
-           break;
-       case 'I':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               auint = SvUV(fromstr);
-               sv_catpvn(cat, (char*)&auint, sizeof(unsigned int));
-           }
-           break;
-       case 'w':
-            while (len-- > 0) {
-               fromstr = NEXTFROM;
-               adouble = Perl_floor(SvNV(fromstr));
-
-               if (adouble < 0)
-                   DIE(aTHX_ "Cannot compress negative numbers");
-
-               if (
-#if UVSIZE > 4 && UVSIZE >= NVSIZE
-                   adouble <= 0xffffffff
-#else
-#   ifdef CXUX_BROKEN_CONSTANT_CONVERT
-                   adouble <= UV_MAX_cxux
-#   else
-                   adouble <= UV_MAX
-#   endif
-#endif
-                   )
-               {
-                   char   buf[1 + sizeof(UV)];
-                   char  *in = buf + sizeof(buf);
-                   UV     auv = U_V(adouble);
-
-                   do {
-                       *--in = (auv & 0x7f) | 0x80;
-                       auv >>= 7;
-                   } while (auv);
-                   buf[sizeof(buf) - 1] &= 0x7f; /* clear continue bit */
-                   sv_catpvn(cat, in, (buf + sizeof(buf)) - in);
-               }
-               else if (SvPOKp(fromstr)) {  /* decimal string arithmetics */
-                   char           *from, *result, *in;
-                   SV             *norm;
-                   STRLEN          len;
-                   bool            done;
-
-                   /* Copy string and check for compliance */
-                   from = SvPV(fromstr, len);
-                   if ((norm = is_an_int(from, len)) == NULL)
-                       DIE(aTHX_ "can compress only unsigned integer");
-
-                   New('w', result, len, char);
-                   in = result + len;
-                   done = FALSE;
-                   while (!done)
-                       *--in = div128(norm, &done) | 0x80;
-                   result[len - 1] &= 0x7F; /* clear continue bit */
-                   sv_catpvn(cat, in, (result + len) - in);
-                   Safefree(result);
-                   SvREFCNT_dec(norm); /* free norm */
-                }
-               else if (SvNOKp(fromstr)) {
-                   char   buf[sizeof(double) * 2];     /* 8/7 <= 2 */
-                   char  *in = buf + sizeof(buf);
-
-                   do {
-                       double next = floor(adouble / 128);
-                       *--in = (unsigned char)(adouble - (next * 128)) | 0x80;
-                       if (in <= buf)  /* this cannot happen ;-) */
-                           DIE(aTHX_ "Cannot compress integer");
-                       in--;
-                       adouble = next;
-                   } while (adouble > 0);
-                   buf[sizeof(buf) - 1] &= 0x7f; /* clear continue bit */
-                   sv_catpvn(cat, in, (buf + sizeof(buf)) - in);
-               }
-               else
-                   DIE(aTHX_ "Cannot compress non integer");
-           }
-            break;
-       case 'i':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               aint = SvIV(fromstr);
-               sv_catpvn(cat, (char*)&aint, sizeof(int));
-           }
-           break;
-       case 'N':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               aulong = SvUV(fromstr);
-#ifdef HAS_HTONL
-               aulong = PerlSock_htonl(aulong);
-#endif
-               CAT32(cat, &aulong);
-           }
-           break;
-       case 'V':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               aulong = SvUV(fromstr);
-#ifdef HAS_HTOVL
-               aulong = htovl(aulong);
-#endif
-               CAT32(cat, &aulong);
-           }
-           break;
-       case 'L':
-#if LONGSIZE != SIZE32
-           if (natint) {
-               unsigned long aulong;
-
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   aulong = SvUV(fromstr);
-                   sv_catpvn(cat, (char *)&aulong, sizeof(unsigned long));
-               }
-           }
-           else
-#endif
-            {
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   aulong = SvUV(fromstr);
-                   CAT32(cat, &aulong);
-               }
-           }
-           break;
-       case 'l':
-#if LONGSIZE != SIZE32
-           if (natint) {
-               long along;
-
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   along = SvIV(fromstr);
-                   sv_catpvn(cat, (char *)&along, sizeof(long));
-               }
-           }
-           else
-#endif
-            {
-               while (len-- > 0) {
-                   fromstr = NEXTFROM;
-                   along = SvIV(fromstr);
-                   CAT32(cat, &along);
-               }
-           }
-           break;
-#ifdef HAS_QUAD
-       case 'Q':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               auquad = (Uquad_t)SvUV(fromstr);
-               sv_catpvn(cat, (char*)&auquad, sizeof(Uquad_t));
-           }
-           break;
-       case 'q':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               aquad = (Quad_t)SvIV(fromstr);
-               sv_catpvn(cat, (char*)&aquad, sizeof(Quad_t));
-           }
-           break;
-#endif
-       case 'P':
-           len = 1;            /* assume SV is correct length */
-           /* FALL THROUGH */
-       case 'p':
-           while (len-- > 0) {
-               fromstr = NEXTFROM;
-               if (fromstr == &PL_sv_undef)
-                   aptr = NULL;
-               else {
-                   STRLEN n_a;
-                   /* XXX better yet, could spirit away the string to
-                    * a safe spot and hang on to it until the result
-                    * of pack() (and all copies of the result) are
-                    * gone.
-                    */
-                   if (ckWARN(WARN_PACK) && (SvTEMP(fromstr)
-                                               || (SvPADTMP(fromstr)
-                                                   && !SvREADONLY(fromstr))))
-                   {
-                       Perl_warner(aTHX_ WARN_PACK,
-                               "Attempt to pack pointer to temporary value");
-                   }
-                   if (SvPOK(fromstr) || SvNIOK(fromstr))
-                       aptr = SvPV(fromstr,n_a);
-                   else
-                       aptr = SvPV_force(fromstr,n_a);
-               }
-               sv_catpvn(cat, (char*)&aptr, sizeof(char*));
-           }
-           break;
-       case 'u':
-           fromstr = NEXTFROM;
-           aptr = SvPV(fromstr, fromlen);
-           SvGROW(cat, fromlen * 4 / 3);
-           if (len <= 1)
-               len = 45;
-           else
-               len = len / 3 * 3;
-           while (fromlen > 0) {
-               I32 todo;
-
-               if (fromlen > len)
-                   todo = len;
-               else
-                   todo = fromlen;
-               doencodes(cat, aptr, todo);
-               fromlen -= todo;
-               aptr += todo;
-           }
-           break;
-       }
-    }
-    SvSETMAGIC(cat);
-    SP = ORIGMARK;
-    PUSHs(cat);
-    RETURN;
-}
-#undef NEXTFROM
-
-
 PP(pp_split)
 {
 PP(pp_split)
 {
-    djSP; dTARG;
+    dSP; dTARG;
     AV *ary;
     register IV limit = POPi;                  /* note, negative is forever */
     SV *sv = POPs;
     AV *ary;
     register IV limit = POPi;                  /* note, negative is forever */
     SV *sv = POPs;
-    bool doutf8 = DO_UTF8(sv);
     STRLEN len;
     register char *s = SvPV(sv, len);
     STRLEN len;
     register char *s = SvPV(sv, len);
+    bool do_utf8 = DO_UTF8(sv);
     char *strend = s + len;
     register PMOP *pm;
     register REGEXP *rx;
     register SV *dstr;
     register char *m;
     I32 iters = 0;
     char *strend = s + len;
     register PMOP *pm;
     register REGEXP *rx;
     register SV *dstr;
     register char *m;
     I32 iters = 0;
-    I32 maxiters = (strend - s) + 10;
+    STRLEN slen = do_utf8 ? utf8_length((U8*)s, (U8*)strend) : (strend - s);
+    I32 maxiters = slen + 10;
     I32 i;
     char *orig;
     I32 origlimit = limit;
     I32 i;
     char *orig;
     I32 origlimit = limit;
@@ -5075,25 +4164,27 @@ PP(pp_split)
     pm = (PMOP*)POPs;
 #endif
     if (!pm || !s)
     pm = (PMOP*)POPs;
 #endif
     if (!pm || !s)
-       DIE(aTHX_ "panic: do_split");
-    rx = pm->op_pmregexp;
+       DIE(aTHX_ "panic: pp_split");
+    rx = PM_GETRE(pm);
 
     TAINT_IF((pm->op_pmflags & PMf_LOCALE) &&
             (pm->op_pmflags & (PMf_WHITE | PMf_SKIPWHITE)));
 
 
     TAINT_IF((pm->op_pmflags & PMf_LOCALE) &&
             (pm->op_pmflags & (PMf_WHITE | PMf_SKIPWHITE)));
 
+    PL_reg_match_utf8 = do_utf8;
+
     if (pm->op_pmreplroot) {
 #ifdef USE_ITHREADS
     if (pm->op_pmreplroot) {
 #ifdef USE_ITHREADS
-       ary = GvAVn((GV*)PL_curpad[(PADOFFSET)pm->op_pmreplroot]);
+       ary = GvAVn((GV*)PL_curpad[INT2PTR(PADOFFSET, pm->op_pmreplroot)]);
 #else
        ary = GvAVn((GV*)pm->op_pmreplroot);
 #endif
     }
     else if (gimme != G_ARRAY)
 #else
        ary = GvAVn((GV*)pm->op_pmreplroot);
 #endif
     }
     else if (gimme != G_ARRAY)
-#ifdef USE_THREADS
+#ifdef USE_5005THREADS
        ary = (AV*)PL_curpad[0];
 #else
        ary = GvAVn(PL_defgv);
        ary = (AV*)PL_curpad[0];
 #else
        ary = GvAVn(PL_defgv);
-#endif /* USE_THREADS */
+#endif /* USE_5005THREADS */
     else
        ary = Nullav;
     if (ary && (gimme != G_ARRAY || (pm->op_pmflags & PMf_ONCE))) {
     else
        ary = Nullav;
     if (ary && (gimme != G_ARRAY || (pm->op_pmflags & PMf_ONCE))) {
@@ -5102,7 +4193,7 @@ PP(pp_split)
        av_extend(ary,0);
        av_clear(ary);
        SPAGAIN;
        av_extend(ary,0);
        av_clear(ary);
        SPAGAIN;
-       if ((mg = SvTIED_mg((SV*)ary, 'P'))) {
+       if ((mg = SvTIED_mg((SV*)ary, PERL_MAGIC_tied))) {
            PUSHMARK(SP);
            XPUSHs(SvTIED_obj((SV*)ary, mg));
        }
            PUSHMARK(SP);
            XPUSHs(SvTIED_obj((SV*)ary, mg));
        }
@@ -5151,7 +4242,7 @@ PP(pp_split)
            sv_setpvn(dstr, s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
            sv_setpvn(dstr, s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
-           if (doutf8)
+           if (do_utf8)
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
 
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
 
@@ -5173,20 +4264,21 @@ PP(pp_split)
            sv_setpvn(dstr, s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
            sv_setpvn(dstr, s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
-           if (doutf8)
+           if (do_utf8)
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
            s = m;
        }
     }
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
            s = m;
        }
     }
-    else if ((rx->reganch & RE_USE_INTUIT) && !rx->nparens
+    else if (do_utf8 == ((rx->reganch & ROPT_UTF8) != 0) &&
+            (rx->reganch & RE_USE_INTUIT) && !rx->nparens
             && (rx->reganch & ROPT_CHECK_ALL)
             && !(rx->reganch & ROPT_ANCH)) {
        int tail = (rx->reganch & RE_INTUIT_TAIL);
        SV *csv = CALLREG_INTUIT_STRING(aTHX_ rx);
 
        len = rx->minlen;
             && (rx->reganch & ROPT_CHECK_ALL)
             && !(rx->reganch & ROPT_ANCH)) {
        int tail = (rx->reganch & RE_INTUIT_TAIL);
        SV *csv = CALLREG_INTUIT_STRING(aTHX_ rx);
 
        len = rx->minlen;
-       if (len == 1 && !tail) {
+       if (len == 1 && !(rx->reganch & ROPT_UTF8) && !tail) {
            STRLEN n_a;
            char c = *SvPV(csv, n_a);
            while (--limit) {
            STRLEN n_a;
            char c = *SvPV(csv, n_a);
            while (--limit) {
@@ -5198,12 +4290,15 @@ PP(pp_split)
                sv_setpvn(dstr, s, m-s);
                if (make_mortal)
                    sv_2mortal(dstr);
                sv_setpvn(dstr, s, m-s);
                if (make_mortal)
                    sv_2mortal(dstr);
-               if (doutf8)
+               if (do_utf8)
                    (void)SvUTF8_on(dstr);
                XPUSHs(dstr);
                /* The rx->minlen is in characters but we want to step
                 * s ahead by bytes. */
                    (void)SvUTF8_on(dstr);
                XPUSHs(dstr);
                /* The rx->minlen is in characters but we want to step
                 * s ahead by bytes. */
-               s = m + (doutf8 ? SvCUR(csv) : len);
+               if (do_utf8)
+                   s = (char*)utf8_hop((U8*)m, len);
+               else
+                   s = m + len; /* Fake \n at the end */
            }
        }
        else {
            }
        }
        else {
@@ -5217,17 +4312,20 @@ PP(pp_split)
                sv_setpvn(dstr, s, m-s);
                if (make_mortal)
                    sv_2mortal(dstr);
                sv_setpvn(dstr, s, m-s);
                if (make_mortal)
                    sv_2mortal(dstr);
-               if (doutf8)
+               if (do_utf8)
                    (void)SvUTF8_on(dstr);
                XPUSHs(dstr);
                /* The rx->minlen is in characters but we want to step
                 * s ahead by bytes. */
                    (void)SvUTF8_on(dstr);
                XPUSHs(dstr);
                /* The rx->minlen is in characters but we want to step
                 * s ahead by bytes. */
-               s = m + (doutf8 ? SvCUR(csv) : len); /* Fake \n at the end */
+               if (do_utf8)
+                   s = (char*)utf8_hop((U8*)m, len);
+               else
+                   s = m + len; /* Fake \n at the end */
            }
        }
     }
     else {
            }
        }
     }
     else {
-       maxiters += (strend - s) * rx->nparens;
+       maxiters += slen * rx->nparens;
        while (s < strend && --limit
 /*            && (!rx->check_substr
                   || ((s = CALLREG_INTUIT_START(aTHX_ rx, sv, s, strend,
        while (s < strend && --limit
 /*            && (!rx->check_substr
                   || ((s = CALLREG_INTUIT_START(aTHX_ rx, sv, s, strend,
@@ -5248,22 +4346,26 @@ PP(pp_split)
            sv_setpvn(dstr, s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
            sv_setpvn(dstr, s, m-s);
            if (make_mortal)
                sv_2mortal(dstr);
-           if (doutf8)
+           if (do_utf8)
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
            if (rx->nparens) {
                for (i = 1; i <= rx->nparens; i++) {
                    s = rx->startp[i] + orig;
                    m = rx->endp[i] + orig;
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
            if (rx->nparens) {
                for (i = 1; i <= rx->nparens; i++) {
                    s = rx->startp[i] + orig;
                    m = rx->endp[i] + orig;
-                   if (m && s) {
+
+                   /* japhy (07/27/01) -- the (m && s) test doesn't catch
+                      parens that didn't match -- they should be set to
+                      undef, not the empty string */
+                   if (m >= orig && s >= orig) {
                        dstr = NEWSV(33, m-s);
                        sv_setpvn(dstr, s, m-s);
                    }
                    else
                        dstr = NEWSV(33, m-s);
                        sv_setpvn(dstr, s, m-s);
                    }
                    else
-                       dstr = NEWSV(33, 0);
+                       dstr = &PL_sv_undef;  /* undef, not "" */
                    if (make_mortal)
                        sv_2mortal(dstr);
                    if (make_mortal)
                        sv_2mortal(dstr);
-                   if (doutf8)
+                   if (do_utf8)
                        (void)SvUTF8_on(dstr);
                    XPUSHs(dstr);
                }
                        (void)SvUTF8_on(dstr);
                    XPUSHs(dstr);
                }
@@ -5284,7 +4386,7 @@ PP(pp_split)
        sv_setpvn(dstr, s, l);
        if (make_mortal)
            sv_2mortal(dstr);
        sv_setpvn(dstr, s, l);
        if (make_mortal)
            sv_2mortal(dstr);
-       if (doutf8)
+       if (do_utf8)
            (void)SvUTF8_on(dstr);
        XPUSHs(dstr);
        iters++;
            (void)SvUTF8_on(dstr);
        XPUSHs(dstr);
        iters++;
@@ -5338,12 +4440,11 @@ PP(pp_split)
     RETPUSHUNDEF;
 }
 
     RETPUSHUNDEF;
 }
 
-#ifdef USE_THREADS
+#ifdef USE_5005THREADS
 void
 Perl_unlock_condpair(pTHX_ void *svv)
 {
 void
 Perl_unlock_condpair(pTHX_ void *svv)
 {
-    dTHR;
-    MAGIC *mg = mg_find((SV*)svv, 'm');
+    MAGIC *mg = mg_find((SV*)svv, PERL_MAGIC_mutex);
 
     if (!mg)
        Perl_croak(aTHX_ "panic: unlock_condpair unlocking non-mutex");
 
     if (!mg)
        Perl_croak(aTHX_ "panic: unlock_condpair unlocking non-mutex");
@@ -5353,19 +4454,24 @@ Perl_unlock_condpair(pTHX_ void *svv)
     MgOWNER(mg) = 0;
     COND_SIGNAL(MgOWNERCONDP(mg));
     DEBUG_S(PerlIO_printf(Perl_debug_log, "0x%"UVxf": unlock 0x%"UVxf"\n",
     MgOWNER(mg) = 0;
     COND_SIGNAL(MgOWNERCONDP(mg));
     DEBUG_S(PerlIO_printf(Perl_debug_log, "0x%"UVxf": unlock 0x%"UVxf"\n",
-                         PTR2UV(thr), PTR2UV(svv));)
+                         PTR2UV(thr), PTR2UV(svv)));
     MUTEX_UNLOCK(MgMUTEXP(mg));
 }
     MUTEX_UNLOCK(MgMUTEXP(mg));
 }
-#endif /* USE_THREADS */
+#endif /* USE_5005THREADS */
 
 PP(pp_lock)
 {
 
 PP(pp_lock)
 {
-    djSP;
+    dSP;
     dTOPss;
     SV *retsv = sv;
     dTOPss;
     SV *retsv = sv;
-#ifdef USE_THREADS
+#ifdef USE_5005THREADS
     sv_lock(sv);
     sv_lock(sv);
-#endif /* USE_THREADS */
+#endif /* USE_5005THREADS */
+#ifdef USE_ITHREADS
+    shared_sv *ssv = Perl_sharedsv_find(aTHX_ sv);
+    if(ssv)
+        Perl_sharedsv_lock(aTHX_ ssv);
+#endif /* USE_ITHREADS */
     if (SvTYPE(retsv) == SVt_PVAV || SvTYPE(retsv) == SVt_PVHV
        || SvTYPE(retsv) == SVt_PVCV) {
        retsv = refto(retsv);
     if (SvTYPE(retsv) == SVt_PVAV || SvTYPE(retsv) == SVt_PVHV
        || SvTYPE(retsv) == SVt_PVCV) {
        retsv = refto(retsv);
@@ -5376,8 +4482,8 @@ PP(pp_lock)
 
 PP(pp_threadsv)
 {
 
 PP(pp_threadsv)
 {
-#ifdef USE_THREADS
-    djSP;
+#ifdef USE_5005THREADS
+    dSP;
     EXTEND(SP, 1);
     if (PL_op->op_private & OPpLVAL_INTRO)
        PUSHs(*save_threadsv(PL_op->op_targ));
     EXTEND(SP, 1);
     if (PL_op->op_private & OPpLVAL_INTRO)
        PUSHs(*save_threadsv(PL_op->op_targ));
@@ -5386,5 +4492,5 @@ PP(pp_threadsv)
     RETURN;
 #else
     DIE(aTHX_ "tried to access per-thread data in non-threaded perl");
     RETURN;
 #else
     DIE(aTHX_ "tried to access per-thread data in non-threaded perl");
-#endif /* USE_THREADS */
+#endif /* USE_5005THREADS */
 }
 }