This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Plan 9: No Configure.
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index 51e10de..c9d1dc6 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -1,6 +1,6 @@
 /*    pp.c
  *
- *    Copyright (c) 1991-2001, Larry Wall
+ *    Copyright (c) 1991-2002, 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.
@@ -15,8 +15,9 @@
 #include "EXTERN.h"
 #define PERL_IN_PP_C
 #include "perl.h"
+#include "keywords.h"
 
-/* variations on pp_null */
+#include "reentr.h"
 
 /* XXX I can't imagine anyone who doesn't have this actually _needs_
    it, since pid_t is an integral type.
@@ -26,6 +27,8 @@
 extern Pid_t getpid (void);
 #endif
 
+/* variations on pp_null */
+
 PP(pp_stub)
 {
     dSP;
@@ -44,8 +47,9 @@ PP(pp_scalar)
 PP(pp_padav)
 {
     dSP; dTARGET;
+    I32 gimme;
     if (PL_op->op_private & OPpLVAL_INTRO)
-       SAVECLEARSV(PL_curpad[PL_op->op_targ]);
+       SAVECLEARSV(PAD_SVl(PL_op->op_targ));
     EXTEND(SP, 1);
     if (PL_op->op_flags & OPf_REF) {
        PUSHs(TARG);
@@ -56,12 +60,13 @@ PP(pp_padav)
        PUSHs(TARG);
        RETURN;
     }
-    if (GIMME == G_ARRAY) {
+    gimme = GIMME_V;
+    if (gimme == G_ARRAY) {
        I32 maxarg = AvFILL((AV*)TARG) + 1;
        EXTEND(SP, maxarg);
        if (SvMAGICAL(TARG)) {
            U32 i;
-           for (i=0; i < maxarg; i++) {
+           for (i=0; i < (U32)maxarg; i++) {
                SV **svp = av_fetch((AV*)TARG, i, FALSE);
                SP[i+1] = (svp) ? *svp : &PL_sv_undef;
            }
@@ -71,7 +76,7 @@ PP(pp_padav)
        }
        SP += maxarg;
     }
-    else {
+    else if (gimme == G_SCALAR) {
        SV* sv = sv_newmortal();
        I32 maxarg = AvFILL((AV*)TARG) + 1;
        sv_setiv(sv, maxarg);
@@ -87,7 +92,7 @@ PP(pp_padhv)
 
     XPUSHs(TARG);
     if (PL_op->op_private & OPpLVAL_INTRO)
-       SAVECLEARSV(PL_curpad[PL_op->op_targ]);
+       SAVECLEARSV(PAD_SVl(PL_op->op_targ));
     if (PL_op->op_flags & OPf_REF)
        RETURN;
     else if (LVRET) {
@@ -156,7 +161,7 @@ PP(pp_rv2gv)
                    GV *gv;
                    if (cUNOP->op_targ) {
                        STRLEN len;
-                       SV *namesv = PL_curpad[cUNOP->op_targ];
+                       SV *namesv = PAD_SV(cUNOP->op_targ);
                        name = SvPV(namesv, len);
                        gv = (GV*)NEWSV(0,0);
                        gv_init(gv, CopSTASH(PL_curcop), name, len, 0);
@@ -365,6 +370,8 @@ PP(pp_prototype)
                I32 oa;
                char str[ MAX_ARGS_OP * 2 + 2 ]; /* One ';', one '\0' */
 
+               if (code == -KEY_chop || code == -KEY_chomp)
+                   goto set;
                while (i < MAXO) {      /* The slow way. */
                    if (strEQ(s + 6, PL_op_name[i])
                        || strEQ(s + 6, PL_op_desc[i]))
@@ -415,7 +422,7 @@ PP(pp_prototype)
 PP(pp_anoncode)
 {
     dSP;
-    CV* cv = (CV*)PL_curpad[PL_op->op_targ];
+    CV* cv = (CV*)PAD_SV(PL_op->op_targ);
     if (CvCLONE(cv))
        cv = (CV*)sv_2mortal((SV*)cv_clone(cv));
     EXTEND(SP,1);
@@ -467,8 +474,8 @@ S_refto(pTHX_ SV *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);
@@ -516,7 +523,7 @@ PP(pp_bless)
            Perl_croak(aTHX_ "Attempt to bless into a reference");
        ptr = SvPV(ssv,len);
        if (ckWARN(WARN_MISC) && len == 0)
-           Perl_warner(aTHX_ WARN_MISC,
+           Perl_warner(aTHX_ packWARN(WARN_MISC),
                   "Explicit blessing to '' (assuming package main)");
        stash = gv_stashpvn(ptr, len, TRUE);
     }
@@ -550,8 +557,11 @@ PP(pp_gelem)
            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);
+       }
        else
        if (strEQ(elem, "FORMAT"))
            tmpRef = (SV*)GvFORM(gv);
@@ -756,8 +766,7 @@ PP(pp_undef)
     if (!sv)
        RETPUSHUNDEF;
 
-    if (SvTHINKFIRST(sv))
-       sv_force_normal(sv);
+    SV_CHECK_THINKFIRST_COW_DROP(sv);
 
     switch (SvTYPE(sv)) {
     case SVt_NULL:
@@ -770,7 +779,7 @@ PP(pp_undef)
        break;
     case SVt_PVCV:
        if (ckWARN(WARN_MISC) && cv_const_sv((CV*)sv))
-           Perl_warner(aTHX_ WARN_MISC, "Constant subroutine %s undefined",
+           Perl_warner(aTHX_ packWARN(WARN_MISC), "Constant subroutine %s undefined",
                 CvANON((CV*)sv) ? "(anonymous)" : GvENAME(CvGV((CV*)sv)));
        /* FALL THROUGH */
     case SVt_PVFM:
@@ -812,10 +821,10 @@ PP(pp_undef)
 PP(pp_predec)
 {
     dSP;
-    if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
+    if (SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
-    if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
-       SvIVX(TOPs) != IV_MIN)
+    if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
+        && SvIVX(TOPs) != IV_MIN)
     {
        --SvIVX(TOPs);
        SvFLAGS(TOPs) &= ~(SVp_NOK|SVp_POK);
@@ -829,11 +838,11 @@ PP(pp_predec)
 PP(pp_postinc)
 {
     dSP; dTARGET;
-    if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
+    if (SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
-    if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
-       SvIVX(TOPs) != IV_MAX)
+    if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
+        && SvIVX(TOPs) != IV_MAX)
     {
        ++SvIVX(TOPs);
        SvFLAGS(TOPs) &= ~(SVp_NOK|SVp_POK);
@@ -850,11 +859,11 @@ PP(pp_postinc)
 PP(pp_postdec)
 {
     dSP; dTARGET;
-    if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
+    if (SvTYPE(TOPs) > SVt_PVLV)
        DIE(aTHX_ PL_no_modify);
     sv_setsv(TARG, TOPs);
-    if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
-       SvIVX(TOPs) != IV_MIN)
+    if (!SvREADONLY(TOPs) && SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)
+        && SvIVX(TOPs) != IV_MIN)
     {
        --SvIVX(TOPs);
        SvFLAGS(TOPs) &= ~(SVp_NOK|SVp_POK);
@@ -871,10 +880,100 @@ PP(pp_postdec)
 PP(pp_pow)
 {
     dSP; dATARGET; tryAMAGICbin(pow,opASSIGN);
+#ifdef PERL_PRESERVE_IVUV
+    /* ** is implemented with pow. pow is floating point. Perl programmers
+       write 2 ** 31 and expect it to be 2147483648
+       pow never made any guarantee to deliver a result to 53 (or whatever)
+       bits of accuracy. Which is unfortunate, as perl programmers expect it
+       to, and on some platforms (eg Irix with long doubles) it doesn't in
+       a very visible case. (2 ** 31, which a regression test uses)
+       So we'll implement power-of-2 ** +ve integer with multiplies, to avoid
+       these problems.  */
     {
-      dPOPTOPnnrl;
-      SETn( Perl_pow( left, right) );
-      RETURN;
+        SvIV_please(TOPm1s);
+        if (SvIOK(TOPm1s)) {
+            bool baseuok = SvUOK(TOPm1s);
+            UV baseuv;
+
+            if (baseuok) {
+                baseuv = SvUVX(TOPm1s);
+            } else {
+                IV iv = SvIVX(TOPm1s);
+                if (iv >= 0) {
+                    baseuv = iv;
+                    baseuok = TRUE; /* effectively it's a UV now */
+                } else {
+                    baseuv = -iv; /* abs, baseuok == false records sign */
+                }
+            }
+            SvIV_please(TOPs);
+            if (SvIOK(TOPs)) {
+                UV power;
+
+                if (SvUOK(TOPs)) {
+                    power = SvUVX(TOPs);
+                } else {
+                    IV iv = SvIVX(TOPs);
+                    if (iv >= 0) {
+                        power = iv;
+                    } else {
+                        goto float_it; /* Can't do negative powers this way.  */
+                    }
+                }
+                /* now we have integer ** positive integer.
+                   foo & (foo - 1) is zero only for a power of 2.  */
+                if (!(baseuv & (baseuv - 1))) {
+                    /* We are raising power-of-2 to postive integer.
+                       The logic here will work for any base (even non-integer
+                       bases) but it can be less accurate than
+                       pow (base,power) or exp (power * log (base)) when the
+                       intermediate values start to spill out of the mantissa.
+                       With powers of 2 we know this can't happen.
+                       And powers of 2 are the favourite thing for perl
+                       programmers to notice ** not doing what they mean. */
+                    NV result = 1.0;
+                    NV base = baseuok ? baseuv : -(NV)baseuv;
+                    int n = 0;
+
+                    /* The logic is this.
+                       x ** n === x ** m1 * x ** m2 where n = m1 + m2
+                       so as 42 is 32 + 8 + 2
+                       x ** 42 can be written as
+                       x ** 32 * x ** 8 * x ** 2
+                       I can calculate x ** 2, x ** 4, x ** 8 etc trivially:
+                       x ** 2n is x ** n * x ** n
+                       So I loop round, squaring x each time
+                       (x, x ** 2, x ** 4, x ** 8) and multiply the result
+                       by the x-value whenever that bit is set in the power.
+                       To finish as soon as possible I zero bits in the power
+                       when I've done them, so that power becomes zero when
+                       I clear the last bit (no more to do), and the loop
+                       terminates.  */
+                    for (; power; base *= base, n++) {
+                        /* Do I look like I trust gcc with long longs here?
+                           Do I hell.  */
+                        UV bit = (UV)1 << (UV)n;
+                        if (power & bit) {
+                            result *= base;
+                            /* Only bother to clear the bit if it is set.  */
+                            power &= ~bit;
+                           /* Avoid squaring base again if we're done. */
+                           if (power == 0) break;
+                        }
+                    }
+                    SP--;
+                    SETn( result );
+                    RETURN;
+                }
+            }
+        }
+    }
+      float_it:
+#endif    
+    {
+        dPOPTOPnnrl;
+        SETn( Perl_pow( left, right) );
+        RETURN;
     }
 }
 
@@ -999,29 +1098,115 @@ PP(pp_multiply)
 PP(pp_divide)
 {
     dSP; dATARGET; tryAMAGICbin(div,opASSIGN);
-    {
-      dPOPPOPnnrl;
-      NV value;
-      if (right == 0.0)
-       DIE(aTHX_ "Illegal division by zero");
+    /* 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
-      /* 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
-      value = left / right;
+#  ifdef PERL_PRESERVE_IVUV
+#    ifndef NV_PRESERVES_UV
+#      define PERL_TRY_UV_DIVIDE
+#    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.  However,
+                   as left >= right to ensure integer result here, we know that
+                   we can skip the test on the right operand - right big
+                   enough not to be preserved can't get here unless left is
+                   also too big.  */
+
+                && (left > ((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( -(IV)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;
     }
 }
 
@@ -1031,64 +1216,93 @@ PP(pp_modulo)
     {
        UV left  = 0;
        UV right = 0;
-       bool left_neg;
-       bool right_neg;
-       bool use_double = 0;
+       bool left_neg = FALSE;
+       bool right_neg = FALSE;
+       bool use_double = FALSE;
+       bool dright_valid = FALSE;
        NV dright = 0.0;
        NV dleft  = 0.0;
 
-       if (SvIOK_notUV(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)) {
-           IV i = SvIVX(POPs);
-           right = (right_neg = (i < 0)) ? -i : i;
-       }
-       else {
+        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;
-           use_double = 1;
            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;
-           if (!use_double) {
-               use_double = 1;
-               dright = right;
-           }
            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 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");
 
@@ -1102,7 +1316,6 @@ PP(pp_modulo)
        else {
            UV ans;
 
-       do_uv:
            if (!right)
                DIE(aTHX_ "Illegal modulus zero");
 
@@ -1139,8 +1352,33 @@ PP(pp_repeat)
        MEXTEND(MARK, max);
        if (count > 1) {
            while (SP > MARK) {
-               if (*SP)
-                   SvTEMP_off((*SP));
+#if 0
+             /* This code was intended to fix 20010809.028:
+
+                $x = 'abcd';
+                for (($x =~ /./g) x 2) {
+                    print chop; # "abcdabcd" expected as output.
+                }
+
+              * but that change (#11635) broke this code:
+
+              $x = [("foo")x2]; # only one "foo" ended up in the anonlist.
+
+              * I can't think of a better fix that doesn't introduce
+              * an efficiency hit by copying the SVs. The stack isn't
+              * refcounted, and mortalisation obviously doesn't
+              * Do The Right Thing when the stack has more than
+              * one pointer to the same mortal value.
+              * .robin.
+              */
+               if (*SP) {
+                   *SP = sv_2mortal(newSVsv(*SP));
+                   SvREADONLY_on(*SP);
+               }
+#else
+               if (*SP)
+                  SvTEMP_off((*SP));
+#endif
                SP--;
            }
            MARK++;
@@ -1244,7 +1482,7 @@ PP(pp_subtract)
                    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.
+              else "IV" now, independent 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)
@@ -1379,11 +1617,6 @@ PP(pp_lt)
                    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;
            }
@@ -1400,17 +1633,22 @@ PP(pp_lt)
                }
                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
+#ifndef NV_PRESERVES_UV
+#ifdef PERL_PRESERVE_IVUV
+    else
+#endif
+        if (SvROK(TOPs) && SvROK(TOPm1s)) {
+            SP--;
+            SETs(boolSV(SvRV(TOPs) < SvRV(TOPp1s)));
+            RETURN;
+        }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn < value));
@@ -1457,11 +1695,6 @@ PP(pp_gt)
                    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;
            }
@@ -1478,17 +1711,22 @@ PP(pp_gt)
                }
                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
+#ifndef NV_PRESERVES_UV
+#ifdef PERL_PRESERVE_IVUV
+    else
+#endif
+        if (SvROK(TOPs) && SvROK(TOPm1s)) {
+        SP--;
+        SETs(boolSV(SvRV(TOPs) > SvRV(TOPp1s)));
+        RETURN;
+    }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn > value));
@@ -1535,11 +1773,6 @@ PP(pp_le)
                    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;
            }
@@ -1556,17 +1789,22 @@ PP(pp_le)
                }
                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
+#ifndef NV_PRESERVES_UV
+#ifdef PERL_PRESERVE_IVUV
+    else
+#endif
+        if (SvROK(TOPs) && SvROK(TOPm1s)) {
+        SP--;
+        SETs(boolSV(SvRV(TOPs) <= SvRV(TOPp1s)));
+        RETURN;
+    }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn <= value));
@@ -1613,11 +1851,6 @@ PP(pp_ge)
                    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;
            }
@@ -1634,17 +1867,22 @@ PP(pp_ge)
                }
                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
+#ifndef NV_PRESERVES_UV
+#ifdef PERL_PRESERVE_IVUV
+    else
+#endif
+        if (SvROK(TOPs) && SvROK(TOPm1s)) {
+        SP--;
+        SETs(boolSV(SvRV(TOPs) >= SvRV(TOPp1s)));
+        RETURN;
+    }
+#endif
     {
       dPOPnv;
       SETs(boolSV(TOPn >= value));
@@ -1657,7 +1895,8 @@ PP(pp_ne)
     dSP; tryAMAGICbinSET(ne,0);
 #ifndef NV_PRESERVES_UV
     if (SvROK(TOPs) && SvROK(TOPm1s)) {
-       SETs(boolSV(SvRV(TOPs) != SvRV(TOPm1s)));
+        SP--;
+       SETs(boolSV(SvRV(TOPs) != SvRV(TOPp1s)));
        RETURN;
     }
 #endif
@@ -1669,19 +1908,16 @@ PP(pp_ne)
            bool auvok = SvUOK(TOPm1s);
            bool buvok = SvUOK(TOPs);
        
-           if (!auvok && !buvok) { /* ## IV <=> IV ## */
-               IV aiv = SvIVX(TOPm1s);
-               IV biv = SvIVX(TOPs);
+           if (auvok == buvok) { /* ## IV == IV or UV == UV ## */
+                /* Casting IV to UV before comparison isn't going to matter
+                   on 2s complement. On 1s complement or sign&magnitude
+                   (if we have any of them) it could make negative zero
+                   differ from normal zero. As I understand it. (Need to
+                   check - is negative zero implementation defined behaviour
+                   anyway?). NWC  */
+               UV buv = SvUVX(POPs);
+               UV auv = SvUVX(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;
            }
@@ -1710,11 +1946,6 @@ PP(pp_ne)
                    }
                    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;
            }
@@ -1733,7 +1964,9 @@ PP(pp_ncmp)
     dSP; dTARGET; tryAMAGICbin(ncmp,0);
 #ifndef NV_PRESERVES_UV
     if (SvROK(TOPs) && SvROK(TOPm1s)) {
-       SETi(PTR2UV(SvRV(TOPs)) - PTR2UV(SvRV(TOPm1s)));
+        UV right = PTR2UV(SvRV(POPs));
+        UV left = PTR2UV(SvRV(TOPs));
+       SETi((left > right) - (left < right));
        RETURN;
     }
 #endif
@@ -1776,10 +2009,7 @@ PP(pp_ncmp)
                    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) {
+                   if (leftuv > (UV)rightiv) {
                        value = 1;
                    } else if (leftuv < (UV)rightiv) {
                        value = -1;
@@ -1797,12 +2027,9 @@ PP(pp_ncmp)
                    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) {
+                   if ((UV)leftiv > rightuv) {
                        value = 1;
-                   } else if (leftiv < (UV)rightuv) {
+                   } else if ((UV)leftiv < rightuv) {
                        value = -1;
                    } else {
                        value = 0;
@@ -2042,15 +2269,22 @@ PP(pp_negate)
                sv_setsv(TARG, sv);
                *SvPV_force(TARG, len) = *s == '-' ? '+' : '-';
            }
-           else if (DO_UTF8(sv) && UTF8_IS_START(*s) && isIDFIRST_utf8((U8*)s)) {
-               sv_setpvn(TARG, "-", 1);
-               sv_catsv(TARG, sv);
+           else if (DO_UTF8(sv)) {
+               SvIV_please(sv);
+               if (SvIOK(sv))
+                   goto oops_its_an_int;
+               if (SvNOK(sv))
+                   sv_setnv(TARG, -SvNV(sv));
+               else {
+                   sv_setpvn(TARG, "-", 1);
+                   sv_catsv(TARG, sv);
+               }
            }
            else {
-             SvIV_please(sv);
-             if (SvIOK(sv))
-               goto oops_its_an_int;
-             sv_setnv(TARG, -SvNV(sv));
+               SvIV_please(sv);
+               if (SvIOK(sv))
+                 goto oops_its_an_int;
+               sv_setnv(TARG, -SvNV(sv));
            }
            SETTARG;
        }
@@ -2117,7 +2351,7 @@ PP(pp_complement)
              while (tmps < send) {
                  UV c = utf8n_to_uvchr(tmps, send-tmps, &l, UTF8_ALLOW_ANYUV);
                  tmps += UTF8SKIP(tmps);
-                 result = uvchr_to_utf8(result, ~c);
+                 result = uvchr_to_utf8_flags(result, ~c, UNICODE_ALLOW_ANY);
              }
              *result = '\0';
              result -= targlen;
@@ -2451,7 +2685,7 @@ S_seed(pTHX)
     u = (U32)SEED_C1 * when[0] + (U32)SEED_C2 * when[1];
 #else
 #  ifdef HAS_GETTIMEOFDAY
-    gettimeofday(&when,(struct timezone *) 0);
+    PerlProc_gettimeofday(&when,NULL);
     u = (U32)SEED_C1 * when.tv_sec + (U32)SEED_C2 * when.tv_usec;
 #  else
     (void)time(&when);
@@ -2486,7 +2720,7 @@ PP(pp_log)
       value = POPn;
       if (value <= 0.0) {
        SET_NUMERIC_STANDARD();
-       DIE(aTHX_ "Can't take log of %g", value);
+       DIE(aTHX_ "Can't take log of %"NVgf, value);
       }
       value = Perl_log(value);
       XPUSHn(value);
@@ -2502,7 +2736,7 @@ PP(pp_sqrt)
       value = POPn;
       if (value < 0.0) {
        SET_NUMERIC_STANDARD();
-       DIE(aTHX_ "Can't take sqrt of %g", value);
+       DIE(aTHX_ "Can't take sqrt of %"NVgf, value);
       }
       value = Perl_sqrt(value);
       XPUSHn(value);
@@ -2510,6 +2744,28 @@ PP(pp_sqrt)
     }
 }
 
+/*
+ * There are strange code-generation bugs caused on sparc64 by gcc-2.95.2.
+ * These need to be revisited when a newer toolchain becomes available.
+ */
+#if defined(__sparc64__) && defined(__GNUC__)
+#   if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+#       undef  SPARC64_MODF_WORKAROUND
+#       define SPARC64_MODF_WORKAROUND 1
+#   endif
+#endif
+
+#if defined(SPARC64_MODF_WORKAROUND)
+static NV
+sparc64_workaround_modf(NV theVal, NV *theIntRes)
+{
+    NV res, ret;
+    ret = Perl_modf(theVal, &res);
+    *theIntRes = res;
+    return ret;
+}
+#endif
+
 PP(pp_int)
 {
     dSP; dTARGET; tryAMAGICun(int);
@@ -2533,21 +2789,25 @@ PP(pp_int)
              if (value < (NV)UV_MAX + 0.5) {
                  SETu(U_V(value));
              } else {
-#if defined(HAS_MODFL) || defined(LONG_DOUBLE_EQUALS_DOUBLE)
-#   ifdef HAS_MODFL_POW32_BUG
+#if defined(SPARC64_MODF_WORKAROUND)
+               (void)sparc64_workaround_modf(value, &value);
+#else
+#   if defined(HAS_MODFL) || defined(LONG_DOUBLE_EQUALS_DOUBLE)
+#       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
+#       else
                  (void)Perl_modf(value, &value);
-#   endif
-#else
+#       endif
+#   else
                  double tmp = (double)value;
                  (void)Perl_modf(tmp, &tmp);
                  value = (NV)tmp;
+#   endif
 #endif
                  SETn(value);
              }
@@ -2615,40 +2875,74 @@ PP(pp_abs)
     RETURN;
 }
 
+
 PP(pp_hex)
 {
     dSP; dTARGET;
     char *tmps;
-    STRLEN argtype;
+    I32 flags = PERL_SCAN_ALLOW_UNDERSCORES;
     STRLEN len;
+    NV result_nv;
+    UV result_uv;
+    SV* sv = POPs;
 
-    tmps = (SvPVx(POPs, len));
-    argtype = 1;               /* allow underscores */
-    XPUSHn(scan_hex(tmps, len, &argtype));
+    tmps = (SvPVx(sv, len));
+    if (DO_UTF8(sv)) {
+        /* If Unicode, try to downgrade
+         * If not possible, croak. */
+         SV* tsv = sv_2mortal(newSVsv(sv));
+       
+        SvUTF8_on(tsv);
+        sv_utf8_downgrade(tsv, FALSE);
+        tmps = SvPVX(tsv);
+    }
+    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)
 {
     dSP; dTARGET;
-    NV value;
-    STRLEN argtype;
     char *tmps;
+    I32 flags = PERL_SCAN_ALLOW_UNDERSCORES;
     STRLEN len;
+    NV result_nv;
+    UV result_uv;
+    SV* sv = POPs;
 
-    tmps = (SvPVx(POPs, len));
+    tmps = (SvPVx(sv, len));
+    if (DO_UTF8(sv)) {
+        /* If Unicode, try to downgrade
+         * If not possible, croak. */
+         SV* tsv = sv_2mortal(newSVsv(sv));
+       
+        SvUTF8_on(tsv);
+        sv_utf8_downgrade(tsv, FALSE);
+        tmps = SvPVX(tsv);
+    }
     while (*tmps && len && isSPACE(*tmps))
-       tmps++, len--;
+        tmps++, len--;
     if (*tmps == '0')
-       tmps++, len--;
-    argtype = 1;               /* allow underscores */
+        tmps++, len--;
     if (*tmps == 'x')
-       value = scan_hex(++tmps, --len, &argtype);
+        result_uv = grok_hex (tmps, &len, &flags, &result_nv);
     else if (*tmps == 'b')
-       value = scan_bin(++tmps, --len, &argtype);
+        result_uv = grok_bin (tmps, &len, &flags, &result_nv);
     else
-       value = scan_oct(tmps, len, &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;
 }
 
@@ -2755,7 +3049,7 @@ PP(pp_substr)
        if (lvalue || repl)
            Perl_croak(aTHX_ "substr outside of string");
        if (ckWARN(WARN_SUBSTR))
-           Perl_warner(aTHX_ WARN_SUBSTR, "substr outside of string");
+           Perl_warner(aTHX_ packWARN(WARN_SUBSTR), "substr outside of string");
        RETPUSHUNDEF;
     }
     else {
@@ -2791,7 +3085,7 @@ PP(pp_substr)
                    STRLEN n_a;
                    SvPV_force(sv,n_a);
                    if (ckWARN(WARN_SUBSTR))
-                       Perl_warner(aTHX_ WARN_SUBSTR,
+                       Perl_warner(aTHX_ packWARN(WARN_SUBSTR),
                                "Attempt to use reference as lvalue in substr");
                }
                if (SvOK(sv))           /* is it defined ? */
@@ -2872,7 +3166,7 @@ PP(pp_index)
        sv_pos_u2b(big, &offset, 0);
     if (offset < 0)
        offset = 0;
-    else if (offset > biglen)
+    else if (offset > (I32)biglen)
        offset = biglen;
     if (!(tmps2 = fbm_instr((unsigned char*)tmps + offset,
       (unsigned char*)tmps + biglen, little, 0)))
@@ -2913,7 +3207,7 @@ PP(pp_rindex)
     }
     if (offset < 0)
        offset = 0;
-    else if (offset > blen)
+    else if (offset > (I32)blen)
        offset = blen;
     if (!(tmps2 = rninstr(tmps,  tmps  + offset,
                          tmps2, tmps2 + llen)))
@@ -2931,6 +3225,8 @@ PP(pp_sprintf)
     dSP; dMARK; dORIGMARK; dTARGET;
     do_sprintf(TARG, SP-MARK, MARK+1);
     TAINT_IF(SvTAINTED(TARG));
+    if (DO_UTF8(*(MARK+1)))
+       SvUTF8_on(TARG);
     SP = ORIGMARK;
     PUSHTARG;
     RETURN;
@@ -2942,8 +3238,18 @@ PP(pp_ord)
     SV *argsv = POPs;
     STRLEN len;
     U8 *s = (U8*)SvPVx(argsv, len);
+    SV *tmpsv;
+
+    if (PL_encoding && SvPOK(argsv) && !DO_UTF8(argsv)) {
+        tmpsv = sv_2mortal(newSVsv(argsv));
+        s = (U8*)sv_recode_to_utf8(tmpsv, PL_encoding);
+        argsv = tmpsv;
+    }
+
+    XPUSHu(DO_UTF8(argsv) ?
+          utf8n_to_uvchr(s, UTF8_MAXLEN, 0, UTF8_ALLOW_ANYUV) :
+          (*s & 0xff));
 
-    XPUSHu(DO_UTF8(argsv) ? utf8_to_uvchr(s, 0) : (*s & 0xff));
     RETURN;
 }
 
@@ -2956,8 +3262,8 @@ PP(pp_chr)
     (void)SvUPGRADE(TARG,SVt_PV);
 
     if (value > 255 && !IN_BYTES) {
-       SvGROW(TARG, UNISKIP(value)+1);
-       tmps = (char*)uvchr_to_utf8((U8*)SvPVX(TARG), value);
+       SvGROW(TARG, (STRLEN)UNISKIP(value)+1);
+       tmps = (char*)uvchr_to_utf8_flags((U8*)SvPVX(TARG), value, 0);
        SvCUR_set(TARG, tmps - SvPVX(TARG));
        *tmps = '\0';
        (void)SvPOK_only(TARG);
@@ -2969,30 +3275,56 @@ PP(pp_chr)
     SvGROW(TARG,2);
     SvCUR_set(TARG, 1);
     tmps = SvPVX(TARG);
-    *tmps++ = value;
+    *tmps++ = (char)value;
     *tmps = '\0';
     (void)SvPOK_only(TARG);
+    if (PL_encoding && !IN_BYTES) {
+        sv_recode_to_utf8(TARG, PL_encoding);
+       tmps = SvPVX(TARG);
+       if (SvCUR(TARG) == 0 || !is_utf8_string((U8*)tmps, SvCUR(TARG)) ||
+           memEQ(tmps, "\xef\xbf\xbd\0", 4)) {
+           SvGROW(TARG,3);
+           SvCUR_set(TARG, 2);
+           *tmps++ = (U8)UTF8_EIGHT_BIT_HI(value);
+           *tmps++ = (U8)UTF8_EIGHT_BIT_LO(value);
+           *tmps = '\0';
+           SvUTF8_on(TARG);
+       }
+    }
     XPUSHs(TARG);
     RETURN;
 }
 
 PP(pp_crypt)
 {
-    dSP; dTARGET; dPOPTOPssrl;
-    STRLEN n_a;
+    dSP; dTARGET;
 #ifdef HAS_CRYPT
-    char *tmps = SvPV(left, n_a);
-#ifdef FCRYPT
+    dPOPTOPssrl;
+    STRLEN n_a;
+    STRLEN len;
+    char *tmps = SvPV(left, len);
+
+    if (DO_UTF8(left)) {
+         /* If Unicode, try to downgrade.
+         * If not possible, croak.
+         * Yes, we made this up.  */
+         SV* tsv = sv_2mortal(newSVsv(left));
+
+        SvUTF8_on(tsv);
+        sv_utf8_downgrade(tsv, FALSE);
+        tmps = SvPVX(tsv);
+    }
+#   ifdef FCRYPT
     sv_setpv(TARG, fcrypt(tmps, SvPV(right, n_a)));
-#else
+#   else
     sv_setpv(TARG, PerlProc_crypt(tmps, SvPV(right, n_a)));
-#endif
+#   endif
+    SETs(TARG);
+    RETURN;
 #else
     DIE(aTHX_
       "The crypt() function is unimplemented due to excessive paranoia.");
 #endif
-    SETs(TARG);
-    RETURN;
 }
 
 PP(pp_ucfirst)
@@ -3002,43 +3334,47 @@ PP(pp_ucfirst)
     register U8 *s;
     STRLEN slen;
 
-    if (DO_UTF8(sv) && (s = (U8*)SvPV(sv, slen)) && slen && UTF8_IS_START(*s)) {
+    SvGETMAGIC(sv);
+    if (DO_UTF8(sv) &&
+       (s = (U8*)SvPV_nomg(sv, slen)) && slen &&
+       UTF8_IS_START(*s)) {
+       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
        STRLEN ulen;
-       U8 tmpbuf[UTF8_MAXLEN+1];
-       U8 *tend;
-       UV uv;
+       STRLEN tculen;
 
-       if (IN_LOCALE_RUNTIME) {
-           TAINT;
-           SvTAINTED_on(sv);
-           uv = toTITLE_LC_uvchr(utf8n_to_uvchr(s, slen, &ulen, 0));
-       }
-       else
-           uv = toTITLE_utf8(s);
-       
-       tend = uvchr_to_utf8(tmpbuf, uv);
+       utf8_to_uvchr(s, &ulen);
+       toTITLE_utf8(s, tmpbuf, &tculen);
+       utf8_to_uvchr(tmpbuf, 0);
 
-       if (!SvPADTMP(sv) || tend - tmpbuf != ulen || SvREADONLY(sv)) {
+       if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
-           sv_setpvn(TARG, (char*)tmpbuf, tend - tmpbuf);
-           sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
+           /* slen is the byte length of the whole SV.
+            * ulen is the byte length of the original Unicode character
+            * stored as UTF-8 at s.
+            * tculen is the byte length of the freshly titlecased
+            * Unicode character stored as UTF-8 at tmpbuf.
+            * We first set the result to be the titlecased character,
+            * and then append the rest of the SV data. */
+           sv_setpvn(TARG, (char*)tmpbuf, tculen);
+           if (slen > ulen)
+               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);
+           s = (U8*)SvPV_force_nomg(sv, slen);
+           Copy(tmpbuf, s, tculen, U8);
        }
     }
     else {
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
-       s = (U8*)SvPV_force(sv, slen);
+       s = (U8*)SvPV_force_nomg(sv, slen);
        if (*s) {
            if (IN_LOCALE_RUNTIME) {
                TAINT;
@@ -3049,8 +3385,7 @@ PP(pp_ucfirst)
                *s = toUPPER(*s);
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3061,31 +3396,29 @@ PP(pp_lcfirst)
     register U8 *s;
     STRLEN slen;
 
-    if (DO_UTF8(sv) && (s = (U8*)SvPV(sv, slen)) && slen && UTF8_IS_START(*s)) {
+    SvGETMAGIC(sv);
+    if (DO_UTF8(sv) &&
+       (s = (U8*)SvPV_nomg(sv, slen)) && slen &&
+       UTF8_IS_START(*s)) {
        STRLEN ulen;
-       U8 tmpbuf[UTF8_MAXLEN+1];
+       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
        U8 *tend;
        UV uv;
 
-       if (IN_LOCALE_RUNTIME) {
-           TAINT;
-           SvTAINTED_on(sv);
-           uv = toLOWER_LC_uvchr(utf8n_to_uvchr(s, slen, &ulen, 0));
-       }
-       else
-           uv = toLOWER_utf8(s);
-       
+       toLOWER_utf8(s, tmpbuf, &ulen);
+       uv = utf8_to_uvchr(tmpbuf, 0);
        tend = uvchr_to_utf8(tmpbuf, uv);
 
-       if (!SvPADTMP(sv) || tend - tmpbuf != ulen || SvREADONLY(sv)) {
+       if (!SvPADTMP(sv) || (STRLEN)(tend - tmpbuf) != ulen || SvREADONLY(sv)) {
            dTARGET;
            sv_setpvn(TARG, (char*)tmpbuf, tend - tmpbuf);
-           sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
+           if (slen > ulen)
+               sv_catpvn(TARG, (char*)(s + ulen), slen - ulen);
            SvUTF8_on(TARG);
            SETs(TARG);
        }
        else {
-           s = (U8*)SvPV_force(sv, slen);
+           s = (U8*)SvPV_force_nomg(sv, slen);
            Copy(tmpbuf, s, ulen, U8);
        }
     }
@@ -3093,11 +3426,11 @@ PP(pp_lcfirst)
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
-       s = (U8*)SvPV_force(sv, slen);
+       s = (U8*)SvPV_force_nomg(sv, slen);
        if (*s) {
            if (IN_LOCALE_RUNTIME) {
                TAINT;
@@ -3108,8 +3441,7 @@ PP(pp_lcfirst)
                *s = toLOWER(*s);
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3120,37 +3452,33 @@ PP(pp_uc)
     register U8 *s;
     STRLEN len;
 
+    SvGETMAGIC(sv);
     if (DO_UTF8(sv)) {
        dTARGET;
        STRLEN ulen;
        register U8 *d;
        U8 *send;
+       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
 
-       s = (U8*)SvPV(sv,len);
+       s = (U8*)SvPV_nomg(sv,len);
        if (!len) {
            SvUTF8_off(TARG);                           /* decontaminate */
            sv_setpvn(TARG, "", 0);
            SETs(TARG);
        }
        else {
+           STRLEN nchar = utf8_length(s, s + len);
+
            (void)SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, (len * 2) + 1);
+           SvGROW(TARG, (nchar * UTF8_MAXLEN_UCLC) + 1);
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
-           if (IN_LOCALE_RUNTIME) {
-               TAINT;
-               SvTAINTED_on(TARG);
-               while (s < send) {
-                   d = uvchr_to_utf8(d, toUPPER_LC_uvchr( utf8n_to_uvchr(s, len, &ulen, 0)));
-                   s += ulen;
-               }
-           }
-           else {
-               while (s < send) {
-                   d = uvchr_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);
@@ -3162,11 +3490,11 @@ PP(pp_uc)
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
-       s = (U8*)SvPV_force(sv, len);
+       s = (U8*)SvPV_force_nomg(sv, len);
        if (len) {
            register U8 *send = s + len;
 
@@ -3182,8 +3510,7 @@ PP(pp_uc)
            }
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3194,37 +3521,50 @@ PP(pp_lc)
     register U8 *s;
     STRLEN len;
 
+    SvGETMAGIC(sv);
     if (DO_UTF8(sv)) {
        dTARGET;
        STRLEN ulen;
        register U8 *d;
        U8 *send;
+       U8 tmpbuf[UTF8_MAXLEN_UCLC+1];
 
-       s = (U8*)SvPV(sv,len);
+       s = (U8*)SvPV_nomg(sv,len);
        if (!len) {
            SvUTF8_off(TARG);                           /* decontaminate */
            sv_setpvn(TARG, "", 0);
            SETs(TARG);
        }
        else {
+           STRLEN nchar = utf8_length(s, s + len);
+
            (void)SvUPGRADE(TARG, SVt_PV);
-           SvGROW(TARG, (len * 2) + 1);
+           SvGROW(TARG, (nchar * UTF8_MAXLEN_UCLC) + 1);
            (void)SvPOK_only(TARG);
            d = (U8*)SvPVX(TARG);
            send = s + len;
-           if (IN_LOCALE_RUNTIME) {
-               TAINT;
-               SvTAINTED_on(TARG);
-               while (s < send) {
-                   d = uvchr_to_utf8(d, toLOWER_LC_uvchr( utf8n_to_uvchr(s, len, &ulen, 0)));
-                   s += ulen;
-               }
-           }
-           else {
-               while (s < send) {
-                   d = uvchr_to_utf8(d, toLOWER_utf8(s));
-                   s += UTF8SKIP(s);
+           while (s < send) {
+               UV uv = toLOWER_utf8(s, tmpbuf, &ulen);
+#define GREEK_CAPITAL_LETTER_SIGMA 0x03A3 /* Unicode */
+               if (uv == GREEK_CAPITAL_LETTER_SIGMA) {
+                    /*
+                     * Now if the sigma is NOT followed by
+                     * /$ignorable_sequence$cased_letter/;
+                     * and it IS preceded by
+                     * /$cased_letter$ignorable_sequence/;
+                     * where $ignorable_sequence is
+                     * [\x{2010}\x{AD}\p{Mn}]*
+                     * and $cased_letter is
+                     * [\p{Ll}\p{Lo}\p{Lt}]
+                     * then it should be mapped to 0x03C2,
+                     * (GREEK SMALL LETTER FINAL SIGMA),
+                     * instead of staying 0x03A3.
+                     * See lib/unicore/SpecCase.txt.
+                     */
                }
+               Copy(tmpbuf, d, ulen, U8);
+               d += ulen;
+               s += UTF8SKIP(s);
            }
            *d = '\0';
            SvUTF8_on(TARG);
@@ -3236,12 +3576,12 @@ PP(pp_lc)
        if (!SvPADTMP(sv) || SvREADONLY(sv)) {
            dTARGET;
            SvUTF8_off(TARG);                           /* decontaminate */
-           sv_setsv(TARG, sv);
+           sv_setsv_nomg(TARG, sv);
            sv = TARG;
            SETs(sv);
        }
 
-       s = (U8*)SvPV_force(sv, len);
+       s = (U8*)SvPV_force_nomg(sv, len);
        if (len) {
            register U8 *send = s + len;
 
@@ -3257,8 +3597,7 @@ PP(pp_lc)
            }
        }
     }
-    if (SvSMAGICAL(sv))
-       mg_set(sv);
+    SvSETMAGIC(sv);
     RETURN;
 }
 
@@ -3366,22 +3705,21 @@ PP(pp_each)
     HV *hash = (HV*)POPs;
     HE *entry;
     I32 gimme = GIMME_V;
-    I32 realhv = (SvTYPE(hash) == SVt_PVHV);
 
     PUTBACK;
     /* might clobber stack_sp */
-    entry = realhv ? hv_iternext(hash) : avhv_iternext((AV*)hash);
+    entry = hv_iternext(hash);
     SPAGAIN;
 
     EXTEND(SP, 2);
     if (entry) {
-       PUSHs(hv_iterkeysv(entry));     /* won't clobber stack_sp */
+        SV* sv = hv_iterkeysv(entry);
+       PUSHs(sv);      /* won't clobber stack_sp */
        if (gimme == G_ARRAY) {
            SV *val;
            PUTBACK;
            /* might clobber stack_sp */
-           val = realhv ?
-                 hv_iterval(hash, entry) : avhv_iterval((AV*)hash, entry);
+           val = hv_iterval(hash, entry);
            SPAGAIN;
            PUSHs(val);
        }
@@ -3421,19 +3759,13 @@ PP(pp_delete)
                *MARK = sv ? sv : &PL_sv_undef;
            }
        }
-       else if (hvtype == SVt_PVAV) {
-           if (PL_op->op_flags & OPf_SPECIAL) {        /* array element */
-               while (++MARK <= SP) {
-                   sv = av_delete((AV*)hv, SvIV(*MARK), discard);
-                   *MARK = sv ? sv : &PL_sv_undef;
-               }
-           }
-           else {                                      /* pseudo-hash element */
-               while (++MARK <= SP) {
-                   sv = avhv_delete_ent((AV*)hv, *MARK, discard, 0);
-                   *MARK = sv ? sv : &PL_sv_undef;
-               }
-           }
+       else if (hvtype == SVt_PVAV) {                  /* array element */
+            if (PL_op->op_flags & OPf_SPECIAL) {
+                while (++MARK <= SP) {
+                    sv = av_delete((AV*)hv, SvIV(*MARK), discard);
+                    *MARK = sv ? sv : &PL_sv_undef;
+                }
+            }
        }
        else
            DIE(aTHX_ "Not a HASH reference");
@@ -3454,7 +3786,7 @@ PP(pp_delete)
            if (PL_op->op_flags & OPf_SPECIAL)
                sv = av_delete((AV*)hv, SvIV(keysv), discard);
            else
-               sv = avhv_delete_ent((AV*)hv, keysv, discard, 0);
+               DIE(aTHX_ "panic: avhv_delete no longer supported");
        }
        else
            DIE(aTHX_ "Not a HASH reference");
@@ -3494,8 +3826,6 @@ PP(pp_exists)
            if (av_exists((AV*)hv, SvIV(tmpsv)))
                RETPUSHYES;
        }
-       else if (avhv_exists_ent((AV*)hv, tmpsv, 0))    /* pseudo-hash element */
-           RETPUSHYES;
     }
     else {
        DIE(aTHX_ "Not a HASH reference");
@@ -3508,42 +3838,53 @@ PP(pp_hslice)
     dSP; dMARK; dORIGMARK;
     register HV *hv = (HV*)POPs;
     register I32 lval = (PL_op->op_flags & OPf_MOD || LVRET);
-    I32 realhv = (SvTYPE(hv) == SVt_PVHV);
-
-    if (!realhv && PL_op->op_private & OPpLVAL_INTRO)
-       DIE(aTHX_ "Can't localize pseudo-hash element");
-
-    if (realhv || SvTYPE(hv) == SVt_PVAV) {
-       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;
-           }
-           else {
-               svp = avhv_fetch_ent((AV*)hv, keysv, lval, 0);
-           }
-           if (lval) {
-               if (!svp || *svp == &PL_sv_undef) {
-                   STRLEN n_a;
-                   DIE(aTHX_ PL_no_helem, SvPV(keysv, n_a));
-               }
-               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);
-                   }
+    bool localizing = PL_op->op_private & OPpLVAL_INTRO ? TRUE : FALSE;
+    bool other_magic = FALSE;
+
+    if (localizing) {
+        MAGIC *mg;
+        HV *stash;
+
+        other_magic = mg_find((SV*)hv, PERL_MAGIC_env) ||
+            ((mg = mg_find((SV*)hv, PERL_MAGIC_tied))
+             /* Try to preserve the existenceness of a tied hash
+              * element by using EXISTS and DELETE if possible.
+              * Fallback to FETCH and STORE otherwise */
+             && (stash = SvSTASH(SvRV(SvTIED_obj((SV*)hv, mg))))
+             && gv_fetchmethod_autoload(stash, "EXISTS", TRUE)
+             && gv_fetchmethod_autoload(stash, "DELETE", TRUE));
+    }
+
+    while (++MARK <= SP) {
+        SV *keysv = *MARK;
+        SV **svp;
+        HE *he;
+        bool preeminent = FALSE;
+
+        if (localizing) {
+            preeminent = SvRMAGICAL(hv) && !other_magic ? 1 :
+                hv_exists_ent(hv, keysv, 0);
+        }
+
+        he = hv_fetch_ent(hv, keysv, lval, 0);
+        svp = he ? &HeVAL(he) : 0;
+
+        if (lval) {
+            if (!svp || *svp == &PL_sv_undef) {
+                STRLEN n_a;
+                DIE(aTHX_ PL_no_helem, SvPV(keysv, n_a));
+            }
+            if (localizing) {
+                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;
     }
     if (GIMME != G_ARRAY) {
        MARK = ORIGMARK;
@@ -3644,7 +3985,7 @@ PP(pp_anonhash)
        if (MARK < SP)
            sv_setsv(val, *++MARK);
        else if (ckWARN(WARN_MISC))
-           Perl_warner(aTHX_ WARN_MISC, "Odd number of elements in hash assignment");
+           Perl_warner(aTHX_ packWARN(WARN_MISC), "Odd number of elements in anonymous hash");
        (void)hv_store_ent(hv,key,val,0);
     }
     SP = ORIGMARK;
@@ -3703,8 +4044,11 @@ PP(pp_splice)
        offset = 0;
        length = AvMAX(ary) + 1;
     }
-    if (offset > AvFILLp(ary) + 1)
+    if (offset > AvFILLp(ary) + 1) {
+       if (ckWARN(WARN_MISC))
+           Perl_warner(aTHX_ packWARN(WARN_MISC), "splice() offset past end of array" );
        offset = AvFILLp(ary) + 1;
+    }
     after = AvFILLp(ary) + 1 - (offset + length);
     if (after < 0) {                           /* not that much array */
        length += after;                        /* offset+length now in array */
@@ -3987,7 +4331,7 @@ PP(pp_reverse)
                        while (down > up) {
                            tmp = *up;
                            *up++ = *down;
-                           *down-- = tmp;
+                           *down-- = (char)tmp;
                        }
                    }
                }
@@ -3997,7 +4341,7 @@ PP(pp_reverse)
            while (down > up) {
                tmp = *up;
                *up++ = *down;
-               *down-- = tmp;
+               *down-- = (char)tmp;
            }
            (void)SvPOK_only_UTF8(TARG);
        }
@@ -4047,19 +4391,17 @@ PP(pp_split)
     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
-       ary = GvAVn((GV*)PL_curpad[(PADOFFSET)pm->op_pmreplroot]);
+       ary = GvAVn((GV*)PAD_SVl(INT2PTR(PADOFFSET, pm->op_pmreplroot)));
 #else
        ary = GvAVn((GV*)pm->op_pmreplroot);
 #endif
     }
     else if (gimme != G_ARRAY)
-#ifdef USE_THREADS
-       ary = (AV*)PL_curpad[0];
-#else
        ary = GvAVn(PL_defgv);
-#endif /* USE_THREADS */
     else
        ary = Nullav;
     if (ary && (gimme != G_ARRAY || (pm->op_pmflags & PMf_ONCE))) {
@@ -4225,15 +4567,19 @@ PP(pp_split)
                (void)SvUTF8_on(dstr);
            XPUSHs(dstr);
            if (rx->nparens) {
-               for (i = 1; i <= rx->nparens; i++) {
+               for (i = 1; i <= (I32)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, 0);
+                       dstr = &PL_sv_undef;  /* undef, not "" */
                    if (make_mortal)
                        sv_2mortal(dstr);
                    if (do_utf8)
@@ -4263,8 +4609,12 @@ PP(pp_split)
        iters++;
     }
     else if (!origlimit) {
-       while (iters > 0 && (!TOPs || !SvANY(TOPs) || SvCUR(TOPs) == 0))
-           iters--, SP--;
+       while (iters > 0 && (!TOPs || !SvANY(TOPs) || SvCUR(TOPs) == 0)) {
+           if (TOPs && !make_mortal)
+               sv_2mortal(TOPs);
+           iters--;
+           SP--;
+       }
     }
 
     if (realarray) {
@@ -4311,33 +4661,12 @@ PP(pp_split)
     RETPUSHUNDEF;
 }
 
-#ifdef USE_THREADS
-void
-Perl_unlock_condpair(pTHX_ void *svv)
-{
-    MAGIC *mg = mg_find((SV*)svv, PERL_MAGIC_mutex);
-
-    if (!mg)
-       Perl_croak(aTHX_ "panic: unlock_condpair unlocking non-mutex");
-    MUTEX_LOCK(MgMUTEXP(mg));
-    if (MgOWNER(mg) != thr)
-       Perl_croak(aTHX_ "panic: unlock_condpair unlocking mutex that we don't own");
-    MgOWNER(mg) = 0;
-    COND_SIGNAL(MgOWNERCONDP(mg));
-    DEBUG_S(PerlIO_printf(Perl_debug_log, "0x%"UVxf": unlock 0x%"UVxf"\n",
-                         PTR2UV(thr), PTR2UV(svv)));
-    MUTEX_UNLOCK(MgMUTEXP(mg));
-}
-#endif /* USE_THREADS */
-
 PP(pp_lock)
 {
     dSP;
     dTOPss;
     SV *retsv = sv;
-#ifdef USE_THREADS
-    sv_lock(sv);
-#endif /* USE_THREADS */
+    SvLOCK(sv);
     if (SvTYPE(retsv) == SVt_PVAV || SvTYPE(retsv) == SVt_PVHV
        || SvTYPE(retsv) == SVt_PVCV) {
        retsv = refto(retsv);
@@ -4348,15 +4677,5 @@ PP(pp_lock)
 
 PP(pp_threadsv)
 {
-#ifdef USE_THREADS
-    dSP;
-    EXTEND(SP, 1);
-    if (PL_op->op_private & OPpLVAL_INTRO)
-       PUSHs(*save_threadsv(PL_op->op_targ));
-    else
-       PUSHs(THREADSV(PL_op->op_targ));
-    RETURN;
-#else
     DIE(aTHX_ "tried to access per-thread data in non-threaded perl");
-#endif /* USE_THREADS */
 }