This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
clean up perlocal.pod output on VMS
[perl5.git] / pp.c
diff --git a/pp.c b/pp.c
index 863478d..f7f0c41 100644 (file)
--- a/pp.c
+++ b/pp.c
  * floating-point type to use for NV that has adequate bits to fully
  * hold an IV/UV.  (In other words, sizeof(long) == sizeof(double).)
  *
- * It just so happens that "int" is the right size everywhere, at
- * least today.
+ * It just so happens that "int" is the right size almost everywhere.
  */
 typedef int IBW;
 typedef unsigned UBW;
 
+/*
+ * Mask used after bitwise operations.
+ *
+ * There is at least one realm (Cray word machines) that doesn't
+ * have an integral type (except char) small enough to be represented
+ * in a double without loss; that is, it has no 32-bit type.
+ */
+#if BYTEORDER > 0xFFFF && defined(_CRAY) && !defined(_CRAYMPP)
+#  define BW_BITS  32
+#  define BW_MASK  ((1 << BW_BITS) - 1)
+#  define BW_SIGN  (1 << (BW_BITS - 1))
+#  define BWi(i)  (((i) & BW_SIGN) ? ((i) | ~BW_MASK) : ((i) & BW_MASK))
+#  define BWu(u)  ((u) & BW_MASK)
+#else
+#  define BWi(i)  (i)
+#  define BWu(u)  (u)
+#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.)
+ */
+#define SIZE16 2
+#define SIZE32 4
+
+#if BYTEORDER > 0xFFFF && defined(_CRAY) && !defined(_CRAYMPP)
+#  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 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 CAT16(sv,p)  sv_catpvn(sv, (char*)(p), SIZE16)
+#  define CAT32(sv,p)  sv_catpvn(sv, (char*)(p), SIZE32)
+#endif
+
 static void doencodes _((SV* sv, char* s, I32 len));
 static SV* refto _((SV* sv));
 static U32 seed _((void));
@@ -40,9 +97,8 @@ static bool srand_called = FALSE;
 PP(pp_stub)
 {
     dSP;
-    if (GIMME != G_ARRAY) {
+    if (GIMME_V == G_SCALAR)
        XPUSHs(&sv_undef);
-    }
     RETURN;
 }
 
@@ -81,26 +137,27 @@ PP(pp_padav)
 PP(pp_padhv)
 {
     dSP; dTARGET;
+    I32 gimme;
+
     XPUSHs(TARG);
     if (op->op_private & OPpLVAL_INTRO)
        SAVECLEARSV(curpad[op->op_targ]);
     if (op->op_flags & OPf_REF)
        RETURN;
-    if (GIMME == G_ARRAY) { /* array wanted */
+    gimme = GIMME_V;
+    if (gimme == G_ARRAY) {
        RETURNOP(do_kv(ARGS));
     }
-    else {
+    else if (gimme == G_SCALAR) {
        SV* sv = sv_newmortal();
-       if (HvFILL((HV*)TARG)) {
-           sprintf(buf, "%ld/%ld",
-                   (long)HvFILL((HV*)TARG), (long)HvMAX((HV*)TARG)+1);
-           sv_setpv(sv, buf);
-       }
+       if (HvFILL((HV*)TARG))
+           sv_setpvf(sv, "%ld/%ld",
+                     (long)HvFILL((HV*)TARG), (long)HvMAX((HV*)TARG) + 1);
        else
            sv_setiv(sv, 0);
        SETs(sv);
-       RETURN;
     }
+    RETURN;
 }
 
 PP(pp_padany)
@@ -198,7 +255,7 @@ PP(pp_rv2sv)
        if (op->op_private & OPpLVAL_INTRO)
            sv = save_scalar((GV*)TOPs);
        else if (op->op_private & OPpDEREF)
-           provide_ref(op, sv);
+           vivify_ref(sv, op->op_private & OPpDEREF);
     }
     SETs(sv);
     RETURN;
@@ -321,9 +378,9 @@ SV* sv;
 
     if (SvTYPE(sv) == SVt_PVLV && LvTYPE(sv) == 'y') {
        if (LvTARGLEN(sv))
-           vivify_itervar(sv);
-       if (LvTARG(sv))
-           sv = LvTARG(sv);
+           vivify_defelem(sv);
+       if (!(sv = LvTARG(sv)))
+           sv = &sv_undef;
     }
     else if (SvPADTMP(sv))
        sv = newSVsv(sv);
@@ -550,12 +607,14 @@ PP(pp_undef)
        hv_undef((HV*)sv);
        break;
     case SVt_PVCV:
-       if (!CvANON((CV*)sv) && cv_const_sv((CV*)sv))
+       if (cv_const_sv((CV*)sv))
            warn("Constant subroutine %s undefined",
-                GvENAME(CvGV((CV*)sv)));
+                CvANON((CV*)sv) ? "(anonymous)" : GvENAME(CvGV((CV*)sv)));
        /* FALL THROUGH */
     case SVt_PVFM:
-       cv_undef((CV*)sv);
+       { GV* gv = (GV*)SvREFCNT_inc(CvGV((CV*)sv));
+         cv_undef((CV*)sv);
+         CvGV((CV*)sv) = gv; }   /* let user-undef'd sub keep its identity */
        break;
     case SVt_PVGV:
        if (SvFAKE(sv))
@@ -578,7 +637,7 @@ PP(pp_undef)
 PP(pp_predec)
 {
     dSP;
-    if (SvREADONLY(TOPs))
+    if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        croak(no_modify);
     if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
        SvIVX(TOPs) != IV_MIN)
@@ -595,7 +654,7 @@ PP(pp_predec)
 PP(pp_postinc)
 {
     dSP; dTARGET;
-    if (SvREADONLY(TOPs))
+    if (SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        croak(no_modify);
     sv_setsv(TARG, TOPs);
     if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
@@ -616,7 +675,7 @@ PP(pp_postinc)
 PP(pp_postdec)
 {
     dSP; dTARGET;
-    if(SvREADONLY(TOPs))
+    if(SvREADONLY(TOPs) || SvTYPE(TOPs) > SVt_PVLV)
        croak(no_modify);
     sv_setsv(TARG, TOPs);
     if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs) &&
@@ -686,26 +745,45 @@ PP(pp_modulo)
 {
     dSP; dATARGET; tryAMAGICbin(mod,opASSIGN);
     {
-      register UV right;
+      UV left;
+      UV right;
+      bool left_neg;
+      bool right_neg;
+      UV ans;
 
-      right = POPu;
-      if (!right)
-       DIE("Illegal modulus zero");
+      if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)) {
+       IV i = SvIVX(POPs);
+       right = (right_neg = (i < 0)) ? -i : i;
+      }
+      else {
+       double n = POPn;
+       right = U_V((right_neg = (n < 0)) ? -n : n);
+      }
 
       if (SvIOK(TOPs) && !SvNOK(TOPs) && !SvPOK(TOPs)) {
-       register IV left = SvIVX(TOPs);
-       if (left < 0)
-         SETu( (right - ((UV)(-left) - 1) % right) - 1 );
-       else
-         SETi( left % right );
+       IV i = SvIVX(POPs);
+       left = (left_neg = (i < 0)) ? -i : i;
       }
       else {
-       register double left = TOPn;
-       if (left < 0.0)
-         SETu( (right - (U_V(-left) - 1) % right) - 1 );
+       double n = POPn;
+       left = U_V((left_neg = (n < 0)) ? -n : n);
+      }
+
+      if (!right)
+       DIE("Illegal modulus zero");
+
+      ans = left % right;
+      if ((left_neg != right_neg) && ans)
+       ans = right - ans;
+      if (right_neg) {
+       if (ans <= -(UV)IV_MAX)
+         sv_setiv(TARG, (IV) -ans);
        else
-         SETu( U_V(left) % right );
+         sv_setnv(TARG, -(double)ans);
       }
+      else
+       sv_setuv(TARG, ans);
+      PUSHTARG;
       RETURN;
     }
 }
@@ -783,11 +861,13 @@ PP(pp_left_shift)
       IBW shift = POPi;
       if (op->op_private & HINT_INTEGER) {
        IBW i = TOPi;
-       SETi( i << shift );
+       i = BWi(i) << shift;
+       SETi(BWi(i));
       }
       else {
        UBW u = TOPu;
-       SETu( u << shift );
+       u <<= shift;
+       SETu(BWu(u));
       }
       RETURN;
     }
@@ -800,11 +880,13 @@ PP(pp_right_shift)
       IBW shift = POPi;
       if (op->op_private & HINT_INTEGER) {
        IBW i = TOPi;
-       SETi( i >> shift );
+       i = BWi(i) >> shift;
+       SETi(BWi(i));
       }
       else {
        UBW u = TOPu;
-       SETu( u >> shift );
+       u >>= shift;
+       SETu(BWu(u));
       }
       RETURN;
     }
@@ -815,7 +897,7 @@ PP(pp_lt)
     dSP; tryAMAGICbinSET(lt,0); 
     {
       dPOPnv;
-      SETs((TOPn < value) ? &sv_yes : &sv_no);
+      SETs(boolSV(TOPn < value));
       RETURN;
     }
 }
@@ -825,7 +907,7 @@ PP(pp_gt)
     dSP; tryAMAGICbinSET(gt,0); 
     {
       dPOPnv;
-      SETs((TOPn > value) ? &sv_yes : &sv_no);
+      SETs(boolSV(TOPn > value));
       RETURN;
     }
 }
@@ -835,7 +917,7 @@ PP(pp_le)
     dSP; tryAMAGICbinSET(le,0); 
     {
       dPOPnv;
-      SETs((TOPn <= value) ? &sv_yes : &sv_no);
+      SETs(boolSV(TOPn <= value));
       RETURN;
     }
 }
@@ -845,7 +927,7 @@ PP(pp_ge)
     dSP; tryAMAGICbinSET(ge,0); 
     {
       dPOPnv;
-      SETs((TOPn >= value) ? &sv_yes : &sv_no);
+      SETs(boolSV(TOPn >= value));
       RETURN;
     }
 }
@@ -855,7 +937,7 @@ PP(pp_ne)
     dSP; tryAMAGICbinSET(ne,0); 
     {
       dPOPnv;
-      SETs((TOPn != value) ? &sv_yes : &sv_no);
+      SETs(boolSV(TOPn != value));
       RETURN;
     }
 }
@@ -890,7 +972,7 @@ PP(pp_slt)
       int cmp = ((op->op_private & OPpLOCALE)
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
-      SETs( cmp < 0 ? &sv_yes : &sv_no );
+      SETs(boolSV(cmp < 0));
       RETURN;
     }
 }
@@ -903,7 +985,7 @@ PP(pp_sgt)
       int cmp = ((op->op_private & OPpLOCALE)
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
-      SETs( cmp > 0 ? &sv_yes : &sv_no );
+      SETs(boolSV(cmp > 0));
       RETURN;
     }
 }
@@ -916,7 +998,7 @@ PP(pp_sle)
       int cmp = ((op->op_private & OPpLOCALE)
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
-      SETs( cmp <= 0 ? &sv_yes : &sv_no );
+      SETs(boolSV(cmp <= 0));
       RETURN;
     }
 }
@@ -929,7 +1011,7 @@ PP(pp_sge)
       int cmp = ((op->op_private & OPpLOCALE)
                 ? sv_cmp_locale(left, right)
                 : sv_cmp(left, right));
-      SETs( cmp >= 0 ? &sv_yes : &sv_no );
+      SETs(boolSV(cmp >= 0));
       RETURN;
     }
 }
@@ -939,7 +1021,7 @@ PP(pp_seq)
     dSP; tryAMAGICbinSET(seq,0); 
     {
       dPOPTOPssrl;
-      SETs( sv_eq(left, right) ? &sv_yes : &sv_no );
+      SETs(boolSV(sv_eq(left, right)));
       RETURN;
     }
 }
@@ -949,7 +1031,7 @@ PP(pp_sne)
     dSP; tryAMAGICbinSET(sne,0); 
     {
       dPOPTOPssrl;
-      SETs( !sv_eq(left, right) ? &sv_yes : &sv_no );
+      SETs(boolSV(!sv_eq(left, right)));
       RETURN;
     }
 }
@@ -975,11 +1057,11 @@ PP(pp_bit_and)
       if (SvNIOKp(left) || SvNIOKp(right)) {
        if (op->op_private & HINT_INTEGER) {
          IBW value = SvIV(left) & SvIV(right); 
-         SETi( value );
+         SETi(BWi(value));
        }
        else {
          UBW value = SvUV(left) & SvUV(right); 
-         SETu( value );
+         SETu(BWu(value));
        }
       }
       else {
@@ -998,11 +1080,11 @@ PP(pp_bit_xor)
       if (SvNIOKp(left) || SvNIOKp(right)) {
        if (op->op_private & HINT_INTEGER) {
          IBW value = (USE_LEFT(left) ? SvIV(left) : 0) ^ SvIV(right); 
-         SETi( value );
+         SETi(BWi(value));
        }
        else {
          UBW value = (USE_LEFT(left) ? SvUV(left) : 0) ^ SvUV(right); 
-         SETu( value );
+         SETu(BWu(value));
        }
       }
       else {
@@ -1021,11 +1103,11 @@ PP(pp_bit_or)
       if (SvNIOKp(left) || SvNIOKp(right)) {
        if (op->op_private & HINT_INTEGER) {
          IBW value = (USE_LEFT(left) ? SvIV(left) : 0) | SvIV(right); 
-         SETi( value );
+         SETi(BWi(value));
        }
        else {
          UBW value = (USE_LEFT(left) ? SvUV(left) : 0) | SvUV(right); 
-         SETu( value );
+         SETu(BWu(value));
        }
       }
       else {
@@ -1073,7 +1155,7 @@ PP(pp_not)
 #ifdef OVERLOAD
     dSP; tryAMAGICunSET(not);
 #endif /* OVERLOAD */
-    *stack_sp = SvTRUE(*stack_sp) ? &sv_no : &sv_yes;
+    *stack_sp = boolSV(!SvTRUE(*stack_sp));
     return NORMAL;
 }
 
@@ -1085,11 +1167,11 @@ PP(pp_complement)
       if (SvNIOKp(sv)) {
        if (op->op_private & HINT_INTEGER) {
          IBW value = ~SvIV(sv);
-         SETi( value );
+         SETi(BWi(value));
        }
        else {
          UBW value = ~SvUV(sv);
-         SETu( value );
+         SETu(BWu(value));
        }
       }
       else {
@@ -1180,7 +1262,7 @@ PP(pp_i_lt)
     dSP; tryAMAGICbinSET(lt,0); 
     {
       dPOPTOPiirl;
-      SETs((left < right) ? &sv_yes : &sv_no);
+      SETs(boolSV(left < right));
       RETURN;
     }
 }
@@ -1190,7 +1272,7 @@ PP(pp_i_gt)
     dSP; tryAMAGICbinSET(gt,0); 
     {
       dPOPTOPiirl;
-      SETs((left > right) ? &sv_yes : &sv_no);
+      SETs(boolSV(left > right));
       RETURN;
     }
 }
@@ -1200,7 +1282,7 @@ PP(pp_i_le)
     dSP; tryAMAGICbinSET(le,0); 
     {
       dPOPTOPiirl;
-      SETs((left <= right) ? &sv_yes : &sv_no);
+      SETs(boolSV(left <= right));
       RETURN;
     }
 }
@@ -1210,7 +1292,7 @@ PP(pp_i_ge)
     dSP; tryAMAGICbinSET(ge,0); 
     {
       dPOPTOPiirl;
-      SETs((left >= right) ? &sv_yes : &sv_no);
+      SETs(boolSV(left >= right));
       RETURN;
     }
 }
@@ -1220,7 +1302,7 @@ PP(pp_i_eq)
     dSP; tryAMAGICbinSET(eq,0); 
     {
       dPOPTOPiirl;
-      SETs((left == right) ? &sv_yes : &sv_no);
+      SETs(boolSV(left == right));
       RETURN;
     }
 }
@@ -1230,7 +1312,7 @@ PP(pp_i_ne)
     dSP; tryAMAGICbinSET(ne,0); 
     {
       dPOPTOPiirl;
-      SETs((left != right) ? &sv_yes : &sv_no);
+      SETs(boolSV(left != right));
       RETURN;
     }
 }
@@ -1344,28 +1426,51 @@ PP(pp_srand)
 static U32
 seed()
 {
+    /*
+     * This is really just a quick hack which grabs various garbage
+     * values.  It really should be a real hash algorithm which
+     * spreads the effect of every input bit onto every output bit,
+     * if someone who knows about such tings would bother to write it.
+     * Might be a good idea to add that function to CORE as well.
+     * No numbers below come from careful analysis or anyting here,
+     * except they are primes and SEED_C1 > 1E6 to get a full-width
+     * value from (tv_sec * SEED_C1 + tv_usec).  The multipliers should
+     * probably be bigger too.
+     */
+#if RANDBITS > 16
+#  define SEED_C1      1000003
+#define   SEED_C4      73819
+#else
+#  define SEED_C1      25747
+#define   SEED_C4      20639
+#endif
+#define   SEED_C2      3
+#define   SEED_C3      269
+#define   SEED_C5      26107
+
     U32 u;
 #ifdef VMS
 #  include <starlet.h>
+    /* when[] = (low 32 bits, high 32 bits) of time since epoch
+     * in 100-ns units, typically incremented ever 10 ms.        */
     unsigned int when[2];
     _ckvmssts(sys$gettim(when));
-    u = when[0] ^ when[1];
+    u = (U32)SEED_C1 * when[0] + (U32)SEED_C2 * when[1];
 #else
 #  ifdef HAS_GETTIMEOFDAY
     struct timeval when;
     gettimeofday(&when,(struct timezone *) 0);
-    u = when.tv_sec ^ when.tv_usec;
+    u = (U32)SEED_C1 * when.tv_sec + (U32)SEED_C2 * when.tv_usec;
 #  else
     Time_t when;
     (void)time(&when);
-    u = when;
+    u = (U32)SEED_C1 * when;
 #  endif
 #endif
-#ifndef PLAN9          /* XXX Plan9 assembler chokes on this; fix needed  */
-    /* What is a good hashing algorithm here? */
-    u ^= (   (  269 * (U32)getpid())
-          ^ (26107 * (U32)&when)
-          ^ (73819 * (U32)stack_sp));
+    u += SEED_C3 * (U32)getpid();
+    u += SEED_C4 * (U32)(UV)stack_sp;
+#ifndef PLAN9           /* XXX Plan9 assembler chokes on this; fix needed  */
+    u += SEED_C5 * (U32)(UV)&when;
 #endif
     return u;
 }
@@ -1521,8 +1626,11 @@ PP(pp_substr)
     pos = POPi - arybase;
     sv = POPs;
     tmps = SvPV(sv, curlen);
-    if (pos < 0)
+    if (pos < 0) {
        pos += curlen + arybase;
+       if (pos < 0 && MAXARG < 3)
+           pos = 0;
+    }
     if (pos < 0 || pos > curlen) {
        if (dowarn || lvalue)
            warn("substr outside of string");
@@ -1977,22 +2085,23 @@ PP(pp_each)
     dSP; dTARGET;
     HV *hash = (HV*)POPs;
     HE *entry;
+    I32 gimme = GIMME_V;
     
     PUTBACK;
-    entry = hv_iternext(hash);                        /* might clobber stack_sp */
+    entry = hv_iternext(hash);         /* might clobber stack_sp */
     SPAGAIN;
 
     EXTEND(SP, 2);
     if (entry) {
-       PUSHs(hv_iterkeysv(entry));                   /* won't clobber stack_sp */
-       if (GIMME == G_ARRAY) {
+       PUSHs(hv_iterkeysv(entry));     /* won't clobber stack_sp */
+       if (gimme == G_ARRAY) {
            PUTBACK;
-           sv_setsv(TARG, hv_iterval(hash, entry));  /* might clobber stack_sp */
+           sv_setsv(TARG, hv_iterval(hash, entry));  /* might hit stack_sp */
            SPAGAIN;
            PUSHs(TARG);
        }
     }
-    else if (GIMME == G_SCALAR)
+    else if (gimme == G_SCALAR)
        RETPUSHUNDEF;
 
     RETURN;
@@ -2011,6 +2120,8 @@ PP(pp_keys)
 PP(pp_delete)
 {
     dSP;
+    I32 gimme = GIMME_V;
+    I32 discard = (gimme == G_VOID) ? G_DISCARD : 0;
     SV *sv;
     HV *hv;
 
@@ -2020,11 +2131,12 @@ PP(pp_delete)
        if (SvTYPE(hv) != SVt_PVHV)
            DIE("Not a HASH reference");
        while (++MARK <= SP) {
-           sv = hv_delete_ent(hv, *MARK,
-                       (op->op_private & OPpLEAVE_VOID ? G_DISCARD : 0), 0);
+           sv = hv_delete_ent(hv, *MARK, discard, 0);
            *MARK = sv ? sv : &sv_undef;
        }
-       if (GIMME != G_ARRAY) {
+       if (discard)
+           SP = ORIGMARK;
+       else if (gimme == G_SCALAR) {
            MARK = ORIGMARK;
            *++MARK = *SP;
            SP = MARK;
@@ -2035,11 +2147,11 @@ PP(pp_delete)
        hv = (HV*)POPs;
        if (SvTYPE(hv) != SVt_PVHV)
            DIE("Not a HASH reference");
-       sv = hv_delete_ent(hv, keysv,
-                       (op->op_private & OPpLEAVE_VOID ? G_DISCARD : 0), 0);
+       sv = hv_delete_ent(hv, keysv, discard, 0);
        if (!sv)
            sv = &sv_undef;
-       PUSHs(sv);
+       if (!discard)
+           PUSHs(sv);
     }
     RETURN;
 }
@@ -2468,7 +2580,7 @@ PP(pp_reverse)
        if (SP - MARK > 1)
            do_join(TARG, &sv_no, MARK, SP);
        else
-           sv_setsv(TARG, *SP);
+           sv_setsv(TARG, (SP > MARK) ? *SP : GvSV(defgv));
        up = SvPV_force(TARG, len);
        if (len > 1) {
            down = SvPVX(TARG) + len - 1;
@@ -2521,6 +2633,7 @@ PP(pp_unpack)
     dSP;
     dPOPPOPssrl;
     SV **oldsp = sp;
+    I32 gimme = GIMME_V;
     SV *sv;
     STRLEN llen;
     STRLEN rlen;
@@ -2554,7 +2667,7 @@ PP(pp_unpack)
     double cdouble;
     static char* bitcount = 0;
 
-    if (GIMME != G_ARRAY) {            /* arrange to do first one only */
+    if (gimme != G_ARRAY) {            /* arrange to do first one only */
        /*SUPPRESS 530*/
        for (patend = pat; !isALPHA(*patend) || *patend == 'x'; patend++) ;
        if (strchr("aAbBhHP", *patend) || *pat == '%') {
@@ -2567,7 +2680,9 @@ PP(pp_unpack)
     }
     while (pat < patend) {
       reparse:
-       datumtype = *pat++;
+       datumtype = *pat++ & 0xFF;
+       if (isSPACE(datumtype))
+           continue;
        if (pat >= patend)
            len = 1;
        else if (*pat == '*') {
@@ -2583,7 +2698,7 @@ PP(pp_unpack)
            len = (datumtype != '@');
        switch(datumtype) {
        default:
-           break;
+           croak("Invalid type in unpack: '%c'", (int)datumtype);
        case '%':
            if (len == 1 && pat[-1] != '1')
                len = 16;
@@ -2775,13 +2890,13 @@ PP(pp_unpack)
            }
            break;
        case 's':
-           along = (strend - s) / sizeof(I16);
+           along = (strend - s) / SIZE16;
            if (len > along)
                len = along;
            if (checksum) {
                while (len-- > 0) {
-                   Copy(s, &ashort, 1, I16);
-                   s += sizeof(I16);
+                   COPY16(s, &ashort);
+                   s += SIZE16;
                    culong += ashort;
                }
            }
@@ -2789,8 +2904,8 @@ PP(pp_unpack)
                EXTEND(SP, len);
                EXTEND_MORTAL(len);
                while (len-- > 0) {
-                   Copy(s, &ashort, 1, I16);
-                   s += sizeof(I16);
+                   COPY16(s, &ashort);
+                   s += SIZE16;
                    sv = NEWSV(38, 0);
                    sv_setiv(sv, (IV)ashort);
                    PUSHs(sv_2mortal(sv));
@@ -2800,13 +2915,13 @@ PP(pp_unpack)
        case 'v':
        case 'n':
        case 'S':
-           along = (strend - s) / sizeof(U16);
+           along = (strend - s) / SIZE16;
            if (len > along)
                len = along;
            if (checksum) {
                while (len-- > 0) {
-                   Copy(s, &aushort, 1, U16);
-                   s += sizeof(U16);
+                   COPY16(s, &aushort);
+                   s += SIZE16;
 #ifdef HAS_NTOHS
                    if (datumtype == 'n')
                        aushort = ntohs(aushort);
@@ -2822,8 +2937,8 @@ PP(pp_unpack)
                EXTEND(SP, len);
                EXTEND_MORTAL(len);
                while (len-- > 0) {
-                   Copy(s, &aushort, 1, U16);
-                   s += sizeof(U16);
+                   COPY16(s, &aushort);
+                   s += SIZE16;
                    sv = NEWSV(39, 0);
 #ifdef HAS_NTOHS
                    if (datumtype == 'n')
@@ -2891,13 +3006,13 @@ PP(pp_unpack)
            }
            break;
        case 'l':
-           along = (strend - s) / sizeof(I32);
+           along = (strend - s) / SIZE32;
            if (len > along)
                len = along;
            if (checksum) {
                while (len-- > 0) {
-                   Copy(s, &along, 1, I32);
-                   s += sizeof(I32);
+                   COPY32(s, &along);
+                   s += SIZE32;
                    if (checksum > 32)
                        cdouble += (double)along;
                    else
@@ -2908,8 +3023,8 @@ PP(pp_unpack)
                EXTEND(SP, len);
                EXTEND_MORTAL(len);
                while (len-- > 0) {
-                   Copy(s, &along, 1, I32);
-                   s += sizeof(I32);
+                   COPY32(s, &along);
+                   s += SIZE32;
                    sv = NEWSV(42, 0);
                    sv_setiv(sv, (IV)along);
                    PUSHs(sv_2mortal(sv));
@@ -2919,13 +3034,13 @@ PP(pp_unpack)
        case 'V':
        case 'N':
        case 'L':
-           along = (strend - s) / sizeof(U32);
+           along = (strend - s) / SIZE32;
            if (len > along)
                len = along;
            if (checksum) {
                while (len-- > 0) {
-                   Copy(s, &aulong, 1, U32);
-                   s += sizeof(U32);
+                   COPY32(s, &aulong);
+                   s += SIZE32;
 #ifdef HAS_NTOHL
                    if (datumtype == 'N')
                        aulong = ntohl(aulong);
@@ -2944,8 +3059,8 @@ PP(pp_unpack)
                EXTEND(SP, len);
                EXTEND_MORTAL(len);
                while (len-- > 0) {
-                   Copy(s, &aulong, 1, U32);
-                   s += sizeof(U32);
+                   COPY32(s, &aulong);
+                   s += SIZE32;
 #ifdef HAS_NTOHL
                    if (datumtype == 'N')
                        aulong = ntohl(aulong);
@@ -2997,12 +3112,9 @@ PP(pp_unpack)
                        auv = 0;
                    }
                    else if (++bytes >= sizeof(UV)) {   /* promote to string */
-                       char decn[sizeof(UV) * 3 + 1];
                        char *t;
 
-                       (void) sprintf(decn, "%0*ld",
-                                      (int)sizeof(decn) - 1, auv);
-                       sv = newSVpv(decn, 0);
+                       sv = newSVpvf("%.*Vu", (int)TYPE_DIGITS(UV), auv);
                        while (s < strend) {
                            sv = mul128(sv, *s & 0x7f);
                            if (!(*s++ & 0x80)) {
@@ -3048,7 +3160,10 @@ PP(pp_unpack)
                    s += sizeof(Quad_t);
                }
                sv = NEWSV(42, 0);
-               sv_setiv(sv, (IV)aquad);
+               if (aquad >= IV_MIN && aquad <= IV_MAX)
+                   sv_setiv(sv, (IV)aquad);
+               else
+                   sv_setnv(sv, (double)aquad);
                PUSHs(sv_2mortal(sv));
            }
            break;
@@ -3063,7 +3178,10 @@ PP(pp_unpack)
                    s += sizeof(unsigned Quad_t);
                }
                sv = NEWSV(43, 0);
-               sv_setuv(sv, (UV)auquad);
+               if (aquad <= UV_MAX)
+                   sv_setuv(sv, (UV)auquad);
+               else
+                   sv_setnv(sv, (double)auquad);
                PUSHs(sv_2mortal(sv));
            }
            break;
@@ -3184,16 +3302,16 @@ PP(pp_unpack)
            }
            else {
                if (checksum < 32) {
-                   along = (1 << checksum) - 1;
-                   culong &= (U32)along;
+                   aulong = (1 << checksum) - 1;
+                   culong &= aulong;
                }
-               sv_setnv(sv, (double)culong);
+               sv_setuv(sv, (UV)culong);
            }
            XPUSHs(sv_2mortal(sv));
            checksum = 0;
        }
     }
-    if (sp == oldsp && GIMME != G_ARRAY)
+    if (sp == oldsp && gimme == G_SCALAR)
        PUSHs(&sv_undef);
     RETURN;
 }
@@ -3339,7 +3457,9 @@ PP(pp_pack)
     sv_setpvn(cat, "", 0);
     while (pat < patend) {
 #define NEXTFROM (items-- > 0 ? *MARK++ : &sv_no)
-       datumtype = *pat++;
+       datumtype = *pat++ & 0xFF;
+       if (isSPACE(datumtype))
+           continue;
        if (*pat == '*') {
            len = strchr("@Xxu", datumtype) ? 0 : items;
            pat++;
@@ -3353,7 +3473,7 @@ PP(pp_pack)
            len = 1;
        switch(datumtype) {
        default:
-           break;
+           croak("Invalid type in pack: '%c'", (int)datumtype);
        case '%':
            DIE("%% may only be used in unpack");
        case '@':
@@ -3555,7 +3675,7 @@ PP(pp_pack)
 #ifdef HAS_HTONS
                ashort = htons(ashort);
 #endif
-               sv_catpvn(cat, (char*)&ashort, sizeof(I16));
+               CAT16(cat, &ashort);
            }
            break;
        case 'v':
@@ -3565,7 +3685,7 @@ PP(pp_pack)
 #ifdef HAS_HTOVS
                ashort = htovs(ashort);
 #endif
-               sv_catpvn(cat, (char*)&ashort, sizeof(I16));
+               CAT16(cat, &ashort);
            }
            break;
        case 'S':
@@ -3573,13 +3693,13 @@ PP(pp_pack)
            while (len-- > 0) {
                fromstr = NEXTFROM;
                ashort = (I16)SvIV(fromstr);
-               sv_catpvn(cat, (char*)&ashort, sizeof(I16));
+               CAT16(cat, &ashort);
            }
            break;
        case 'I':
            while (len-- > 0) {
                fromstr = NEXTFROM;
-               auint = U_I(SvNV(fromstr));
+               auint = SvUV(fromstr);
                sv_catpvn(cat, (char*)&auint, sizeof(unsigned int));
            }
            break;
@@ -3591,7 +3711,14 @@ PP(pp_pack)
                if (adouble < 0)
                    croak("Cannot compress negative numbers");
 
-               if (adouble <= UV_MAX) {
+               if (
+#ifdef BW_BITS
+                   adouble <= BW_MASK
+#else
+                   adouble <= UV_MAX
+#endif
+                   )
+               {
                    char   buf[1 + sizeof(UV)];
                    char  *in = buf + sizeof(buf);
                    UV     auv = U_V(adouble);;
@@ -3652,35 +3779,35 @@ PP(pp_pack)
        case 'N':
            while (len-- > 0) {
                fromstr = NEXTFROM;
-               aulong = U_L(SvNV(fromstr));
+               aulong = SvUV(fromstr);
 #ifdef HAS_HTONL
                aulong = htonl(aulong);
 #endif
-               sv_catpvn(cat, (char*)&aulong, sizeof(U32));
+               CAT32(cat, &aulong);
            }
            break;
        case 'V':
            while (len-- > 0) {
                fromstr = NEXTFROM;
-               aulong = U_L(SvNV(fromstr));
+               aulong = SvUV(fromstr);
 #ifdef HAS_HTOVL
                aulong = htovl(aulong);
 #endif
-               sv_catpvn(cat, (char*)&aulong, sizeof(U32));
+               CAT32(cat, &aulong);
            }
            break;
        case 'L':
            while (len-- > 0) {
                fromstr = NEXTFROM;
-               aulong = U_L(SvNV(fromstr));
-               sv_catpvn(cat, (char*)&aulong, sizeof(U32));
+               aulong = SvUV(fromstr);
+               CAT32(cat, &aulong);
            }
            break;
        case 'l':
            while (len-- > 0) {
                fromstr = NEXTFROM;
                along = SvIV(fromstr);
-               sv_catpvn(cat, (char*)&along, sizeof(I32));
+               CAT32(cat, &along);
            }
            break;
 #ifdef HAS_QUAD
@@ -3759,7 +3886,7 @@ PP(pp_split)
     I32 realarray = 0;
     I32 base;
     AV *oldstack = curstack;
-    I32 gimme = GIMME;
+    I32 gimme = GIMME_V;
     I32 oldsave = savestack_ix;
 
 #ifdef DEBUGGING