Re: New Configure functionality
[p5sagit/p5-mst-13.2.git] / pp_pack.c
index 6c9b92a..00a7da2 100644 (file)
--- a/pp_pack.c
+++ b/pp_pack.c
@@ -39,6 +39,8 @@
 #ifndef CHAR_BIT
 # define CHAR_BIT      8
 #endif
+/* Maximum number of bytes to which a byte can grow due to upgrade */
+#define UTF8_EXPAND    2
 
 /*
  * Offset for integer pack/unpack.
@@ -117,7 +119,7 @@ STMT_START {                                                \
 } STMT_END
 
 #define PUSH_VAR(utf8, aptr, var)      \
-       PUSH_BYTES(utf8, aptr, (char *) &(var), sizeof(var))
+       PUSH_BYTES(utf8, aptr, &(var), sizeof(var))
 
 /* Avoid stack overflow due to pathological templates. 100 should be plenty. */
 #define MAX_SUB_TEMPLATE_LEVEL 100
@@ -181,9 +183,9 @@ S_mul128(pTHX_ SV *sv, U8 m)
 #define TYPE_NO_MODIFIERS(t)   ((t) & 0xFF)
 
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
-#define SHRIEKING_ALLOWED_TYPES "sSiIlLxXnNvV"
+# define SHRIEKING_ALLOWED_TYPES "sSiIlLxXnNvV@."
 #else
-#define SHRIEKING_ALLOWED_TYPES "sSiIlLxX"
+# define SHRIEKING_ALLOWED_TYPES "sSiIlLxX"
 #endif
 
 #ifndef PERL_PACK_CAN_BYTEORDER
@@ -195,8 +197,8 @@ S_mul128(pTHX_ SV *sv, U8 m)
 
 # define DO_BO_UNPACK(var, type)
 # define DO_BO_PACK(var, type)
-# define DO_BO_UNPACK_PTR(var, type, pre_cast)
-# define DO_BO_PACK_PTR(var, type, pre_cast)
+# define DO_BO_UNPACK_PTR(var, type, pre_cast, post_cast)
+# define DO_BO_PACK_PTR(var, type, pre_cast, post_cast)
 # define DO_BO_UNPACK_N(var, type)
 # define DO_BO_PACK_N(var, type)
 # define DO_BO_UNPACK_P(var)
@@ -227,28 +229,28 @@ S_mul128(pTHX_ SV *sv, U8 m)
           }                                                                   \
         } STMT_END
 
-# define DO_BO_UNPACK_PTR(var, type, pre_cast)                                \
+# define DO_BO_UNPACK_PTR(var, type, pre_cast, post_cast)                     \
         STMT_START {                                                          \
           switch (TYPE_ENDIANNESS(datumtype)) {                               \
             case TYPE_IS_BIG_ENDIAN:                                          \
-              var = (void *) my_betoh ## type ((pre_cast) var);               \
+              var = (post_cast*) my_betoh ## type ((pre_cast) var);           \
               break;                                                          \
             case TYPE_IS_LITTLE_ENDIAN:                                       \
-              var = (void *) my_letoh ## type ((pre_cast) var);               \
+              var = (post_cast *) my_letoh ## type ((pre_cast) var);          \
               break;                                                          \
             default:                                                          \
               break;                                                          \
           }                                                                   \
         } STMT_END
 
-# define DO_BO_PACK_PTR(var, type, pre_cast)                                  \
+# define DO_BO_PACK_PTR(var, type, pre_cast, post_cast)                       \
         STMT_START {                                                          \
           switch (TYPE_ENDIANNESS(datumtype)) {                               \
             case TYPE_IS_BIG_ENDIAN:                                          \
-              var = (void *) my_htobe ## type ((pre_cast) var);               \
+              var = (post_cast *) my_htobe ## type ((pre_cast) var);          \
               break;                                                          \
             case TYPE_IS_LITTLE_ENDIAN:                                       \
-              var = (void *) my_htole ## type ((pre_cast) var);               \
+              var = (post_cast *) my_htole ## type ((pre_cast) var);          \
               break;                                                          \
             default:                                                          \
               break;                                                          \
@@ -272,11 +274,15 @@ S_mul128(pTHX_ SV *sv, U8 m)
          } STMT_END
 
 # if PTRSIZE == INTSIZE
-#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, i, int)
-#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, i, int)
+#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, i, int, void)
+#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, i, int, void)
+#  define DO_BO_UNPACK_PC(var) DO_BO_UNPACK_PTR(var, i, int, char)
+#  define DO_BO_PACK_PC(var)   DO_BO_PACK_PTR(var, i, int, char)
 # elif PTRSIZE == LONGSIZE
-#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, l, long)
-#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, l, long)
+#  define DO_BO_UNPACK_P(var)  DO_BO_UNPACK_PTR(var, l, long, void)
+#  define DO_BO_PACK_P(var)    DO_BO_PACK_PTR(var, l, long, void)
+#  define DO_BO_UNPACK_PC(var) DO_BO_UNPACK_PTR(var, l, long, char)
+#  define DO_BO_PACK_PC(var)   DO_BO_PACK_PTR(var, l, long, char)
 # else
 #  define DO_BO_UNPACK_P(var)  BO_CANT_DOIT(unpack, pointer)
 #  define DO_BO_PACK_P(var)    BO_CANT_DOIT(pack, pointer)
@@ -312,213 +318,253 @@ S_mul128(pTHX_ SV *sv, U8 m)
 #define PACK_SIZE_UNPREDICTABLE                0x40    /* Not a fixed size element */
 #define PACK_SIZE_MASK                 0x3F
 
-
-struct packsize_t {
-    const unsigned char *array;
-    int first;
-    int size;
-};
-
-#define PACK_SIZE_NORMAL 0
-#define PACK_SIZE_SHRIEKING 1
-
 /* These tables are regenerated by genpacksizetables.pl (and then hand pasted
    in).  You're unlikely ever to need to regenerate them.  */
+
+#if TYPE_IS_SHRIEKING != 0x100
+   ++++shriek offset should be 256
+#endif
+
+typedef U8 packprops_t;
 #if 'J'-'I' == 1
 /* ASCII */
-unsigned char size_normal[53] = {
-  /* C */ sizeof(unsigned char),
+const packprops_t packprops[512] = {
+    /* normal */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0,
+    /* C */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
-  /* D */ LONG_DOUBLESIZE,
+    /* D */ LONG_DOUBLESIZE,
 #else
-  0,
+    0,
 #endif
-  0,
-  /* F */ NVSIZE,
-  0, 0,
-  /* I */ sizeof(unsigned int),
-  /* J */ UVSIZE,
-  0,
-  /* L */ SIZE32,
-  0,
-  /* N */ SIZE32,
-  0, 0,
+    0,
+    /* F */ NVSIZE,
+    0, 0,
+    /* I */ sizeof(unsigned int),
+    /* J */ UVSIZE,
+    0,
+    /* L */ SIZE32,
+    0,
+    /* N */ SIZE32,
+    0, 0,
 #if defined(HAS_QUAD)
-  /* Q */ sizeof(Uquad_t),
+    /* Q */ sizeof(Uquad_t),
 #else
-  0,
+    0,
 #endif
-  0,
-  /* S */ SIZE16,
-  0,
-  /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
-  /* V */ SIZE32,
-  /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* c */ sizeof(char),
-  /* d */ sizeof(double),
-  0,
-  /* f */ sizeof(float),
-  0, 0,
-  /* i */ sizeof(int),
-  /* j */ IVSIZE,
-  0,
-  /* l */ SIZE32,
-  0,
-  /* n */ SIZE16,
-  0,
-  /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
+    0,
+    /* S */ SIZE16,
+    0,
+    /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
+    /* V */ SIZE32,
+    /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* c */ sizeof(char),
+    /* d */ sizeof(double),
+    0,
+    /* f */ sizeof(float),
+    0, 0,
+    /* i */ sizeof(int),
+    /* j */ IVSIZE,
+    0,
+    /* l */ SIZE32,
+    0,
+    /* n */ SIZE16,
+    0,
+    /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
 #if defined(HAS_QUAD)
-  /* q */ sizeof(Quad_t),
+    /* q */ sizeof(Quad_t),
 #else
-  0,
+    0,
 #endif
-  0,
-  /* s */ SIZE16,
-  0, 0,
-  /* v */ SIZE16,
-  /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
-};
-unsigned char size_shrieking[46] = {
-  /* I */ sizeof(unsigned int),
-  0, 0,
-  /* L */ sizeof(unsigned long),
-  0,
+    0,
+    /* s */ SIZE16,
+    0, 0,
+    /* v */ SIZE16,
+    /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    /* shrieking */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* I */ sizeof(unsigned int),
+    0, 0,
+    /* L */ sizeof(unsigned long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* N */ SIZE32,
+    /* N */ SIZE32,
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0,
-  /* S */ sizeof(unsigned short),
-  0, 0,
+    0, 0, 0, 0,
+    /* S */ sizeof(unsigned short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* V */ SIZE32,
+    /* V */ SIZE32,
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* i */ sizeof(int),
-  0, 0,
-  /* l */ sizeof(long),
-  0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0,
+    /* i */ sizeof(int),
+    0, 0,
+    /* l */ sizeof(long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* n */ SIZE16,
+    /* n */ SIZE16,
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0,
-  /* s */ sizeof(short),
-  0, 0,
+    0, 0, 0, 0,
+    /* s */ sizeof(short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* v */ SIZE16
+    /* v */ SIZE16,
 #else
-  0
+    0,
 #endif
-};
-struct packsize_t packsize[2] = {
-  {size_normal, 67, 53},
-  {size_shrieking, 73, 46}
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 #else
 /* EBCDIC (or bust) */
-unsigned char size_normal[100] = {
-  /* c */ sizeof(char),
-  /* d */ sizeof(double),
-  0,
-  /* f */ sizeof(float),
-  0, 0,
-  /* i */ sizeof(int),
-  0, 0, 0, 0, 0, 0, 0,
-  /* j */ IVSIZE,
-  0,
-  /* l */ SIZE32,
-  0,
-  /* n */ SIZE16,
-  0,
-  /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
+const packprops_t packprops[512] = {
+    /* normal */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0,
+    /* c */ sizeof(char),
+    /* d */ sizeof(double),
+    0,
+    /* f */ sizeof(float),
+    0, 0,
+    /* i */ sizeof(int),
+    0, 0, 0, 0, 0, 0, 0,
+    /* j */ IVSIZE,
+    0,
+    /* l */ SIZE32,
+    0,
+    /* n */ SIZE16,
+    0,
+    /* p */ sizeof(char *) | PACK_SIZE_CANNOT_CSUM,
 #if defined(HAS_QUAD)
-  /* q */ sizeof(Quad_t),
+    /* q */ sizeof(Quad_t),
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* s */ SIZE16,
-  0, 0,
-  /* v */ SIZE16,
-  /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0,
-  /* C */ sizeof(unsigned char),
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* s */ SIZE16,
+    0, 0,
+    /* v */ SIZE16,
+    /* w */ sizeof(char) | PACK_SIZE_UNPREDICTABLE | PACK_SIZE_CANNOT_CSUM,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* C */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
-  /* D */ LONG_DOUBLESIZE,
+    /* D */ LONG_DOUBLESIZE,
 #else
-  0,
+    0,
 #endif
-  0,
-  /* F */ NVSIZE,
-  0, 0,
-  /* I */ sizeof(unsigned int),
-  0, 0, 0, 0, 0, 0, 0,
-  /* J */ UVSIZE,
-  0,
-  /* L */ SIZE32,
-  0,
-  /* N */ SIZE32,
-  0, 0,
+    0,
+    /* F */ NVSIZE,
+    0, 0,
+    /* I */ sizeof(unsigned int),
+    0, 0, 0, 0, 0, 0, 0,
+    /* J */ UVSIZE,
+    0,
+    /* L */ SIZE32,
+    0,
+    /* N */ SIZE32,
+    0, 0,
 #if defined(HAS_QUAD)
-  /* Q */ sizeof(Uquad_t),
+    /* Q */ sizeof(Uquad_t),
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* S */ SIZE16,
-  0,
-  /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
-  /* V */ SIZE32,
-  /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
-};
-unsigned char size_shrieking[93] = {
-  /* i */ sizeof(int),
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* l */ sizeof(long),
-  0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* S */ SIZE16,
+    0,
+    /* U */ sizeof(char) | PACK_SIZE_UNPREDICTABLE,
+    /* V */ SIZE32,
+    /* W */ sizeof(unsigned char) | PACK_SIZE_UNPREDICTABLE,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* shrieking */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* i */ sizeof(int),
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* l */ sizeof(long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* n */ SIZE16,
+    /* n */ SIZE16,
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* s */ sizeof(short),
-  0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* s */ sizeof(short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* v */ SIZE16,
+    /* v */ SIZE16,
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* I */ sizeof(unsigned int),
-  0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* L */ sizeof(unsigned long),
-  0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0,
+    /* I */ sizeof(unsigned int),
+    0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* L */ sizeof(unsigned long),
+    0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* N */ SIZE32,
+    /* N */ SIZE32,
 #else
-  0,
+    0,
 #endif
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  /* S */ sizeof(unsigned short),
-  0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* S */ sizeof(unsigned short),
+    0, 0,
 #if defined(PERL_PACK_CAN_SHRIEKSIGN)
-  /* V */ SIZE32
+    /* V */ SIZE32,
 #else
-  0
+    0,
 #endif
-};
-struct packsize_t packsize[2] = {
-  {size_normal, 131, 100},
-  {size_shrieking, 137, 93}
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 #endif
 
@@ -543,7 +589,7 @@ uni_to_byte(pTHX_ char **s, const char *end, I32 datumtype)
        val &= 0xff;
     }
     *s += retlen;
-    return val;
+    return (U8)val;
 }
 
 #define SHIFT_BYTE(utf8, s, strend, datumtype) ((utf8) ? \
@@ -570,7 +616,7 @@ uni_to_bytes(pTHX_ char **s, char *end, char *buf, int buf_len, I32 datumtype)
            bad |= 2;
            val &= 0xff;
        }
-       *(U8 *)buf++ = val;
+       *(U8 *)buf++ = (U8)val;
     }
     /* We have enough characters for the buffer. Did we have problems ? */
     if (bad) {
@@ -585,10 +631,10 @@ uni_to_bytes(pTHX_ char **s, char *end, char *buf, int buf_len, I32 datumtype)
            if (from > end) from = end;
        }
        if ((bad & 2) && ckWARN(WARN_UNPACK))
-           Perl_warner(aTHX_ packWARN(datumtype & TYPE_IS_PACK ? 
+           Perl_warner(aTHX_ packWARN(datumtype & TYPE_IS_PACK ?
                                       WARN_PACK : WARN_UNPACK),
                        "Character(s) in '%c' format wrapped in %s",
-                       (int) TYPE_NO_MODIFIERS(datumtype), 
+                       (int) TYPE_NO_MODIFIERS(datumtype),
                        datumtype & TYPE_IS_PACK ? "pack" : "unpack");
     }
     *s = from;
@@ -636,19 +682,19 @@ bytes_to_uni(pTHX_ U8 *start, STRLEN len, char **dest) {
     *dest = d;
 }
 
-#define PUSH_BYTES(utf8, cur, buf, len)                        \
-STMT_START {                                           \
-    if (utf8) bytes_to_uni(aTHX_ buf, len, &(cur));    \
-    else {                                             \
-       Copy(buf, cur, len, char);                      \
-       (cur) += (len);                                 \
-    }                                                  \
+#define PUSH_BYTES(utf8, cur, buf, len)                                \
+STMT_START {                                                   \
+    if (utf8) bytes_to_uni(aTHX_ (U8 *) buf, len, &(cur));     \
+    else {                                                     \
+       Copy(buf, cur, len, char);                              \
+       (cur) += (len);                                         \
+    }                                                          \
 } STMT_END
 
 #define GROWING(utf8, cat, start, cur, in_len) \
 STMT_START {                                   \
     STRLEN glen = (in_len);                    \
-    if (utf8) glen *= 2;                       \
+    if (utf8) glen *= UTF8_EXPAND;             \
     if ((cur) + glen >= (start) + SvLEN(cat)) {        \
        (start) = sv_exp_grow(aTHX_ cat, glen); \
        (cur) = (start) + SvCUR(cat);           \
@@ -659,7 +705,7 @@ STMT_START {                                        \
 STMT_START {                                   \
     STRLEN glen = (in_len);                    \
     STRLEN gl = glen;                          \
-    if (utf8) gl *= 2;                         \
+    if (utf8) gl *= UTF8_EXPAND;               \
     if ((cur) + gl >= (start) + SvLEN(cat)) {  \
         *cur = '\0';                           \
         SvCUR(cat) = (cur) - (start);          \
@@ -699,12 +745,9 @@ S_measure_struct(pTHX_ tempsym_t* symptr)
     while (next_symbol(symptr)) {
        I32 len;
        int star, size;
-       int which = (symptr->code & TYPE_IS_SHRIEKING) ?
-           PACK_SIZE_SHRIEKING : PACK_SIZE_NORMAL;
-       int offset = TYPE_NO_MODIFIERS(symptr->code) - packsize[which].first;
 
         switch (symptr->howlen) {
-        case e_star:
+         case e_star:
            Perl_croak(aTHX_ "Within []-length '*' not allowed in %s",
                        symptr->flags & FLAG_PACK ? "pack" : "unpack" );
             break;
@@ -714,11 +757,7 @@ S_measure_struct(pTHX_ tempsym_t* symptr)
            break;
         }
 
-       if ((offset >= 0) && (offset < packsize[which].size))
-           size = packsize[which].array[offset] & PACK_SIZE_MASK;
-       else
-           size = 0;
-
+       size = packprops[TYPE_NO_ENDIANNESS(symptr->code)] & PACK_SIZE_MASK;
        if (!size) {
            /* endianness doesn't influence the size of a type */
            switch(TYPE_NO_ENDIANNESS(symptr->code)) {
@@ -726,30 +765,35 @@ S_measure_struct(pTHX_ tempsym_t* symptr)
                Perl_croak(aTHX_ "Invalid type '%c' in %s",
                           (int)TYPE_NO_MODIFIERS(symptr->code),
                           symptr->flags & FLAG_PACK ? "pack" : "unpack" );
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           case '.' | TYPE_IS_SHRIEKING:
+           case '@' | TYPE_IS_SHRIEKING:
+#endif
            case '@':
+           case '.':
            case '/':
            case 'U':                   /* XXXX Is it correct? */
            case 'w':
            case 'u':
                Perl_croak(aTHX_ "Within []-length '%c' not allowed in %s",
-                          (int)symptr->code,
+                          (int) TYPE_NO_MODIFIERS(symptr->code),
                           symptr->flags & FLAG_PACK ? "pack" : "unpack" );
            case '%':
                size = 0;
                break;
            case '(':
-               {
-                   tempsym_t savsym = *symptr;
-                   symptr->patptr = savsym.grpbeg;
-                   symptr->patend = savsym.grpend;
-                   /* XXXX Theoretically, we need to measure many times at
-                      different positions, since the subexpression may contain
-                      alignment commands, but be not of aligned length.
-                      Need to detect this and croak().  */
-                   size = measure_struct(symptr);
-                   *symptr = savsym;
-                   break;
-               }
+           {
+               tempsym_t savsym = *symptr;
+               symptr->patptr = savsym.grpbeg;
+               symptr->patend = savsym.grpend;
+               /* XXXX Theoretically, we need to measure many times at
+                  different positions, since the subexpression may contain
+                  alignment commands, but be not of aligned length.
+                  Need to detect this and croak().  */
+               size = measure_struct(symptr);
+               *symptr = savsym;
+               break;
+           }
            case 'X' | TYPE_IS_SHRIEKING:
                /* XXXX Is this useful?  Then need to treat MEASURE_BACKWARDS.
                 */
@@ -832,7 +876,7 @@ S_group_end(pTHX_ register char *patptr, register char *patend, char ender)
 /* Convert unsigned decimal number to binary.
  * Expects a pointer to the first digit and address of length variable
  * Advances char pointer to 1st non-digit char and returns number
- */ 
+ */
 STATIC char *
 S_get_num(pTHX_ register char *patptr, I32 *lenptr )
 {
@@ -852,8 +896,8 @@ S_get_num(pTHX_ register char *patptr, I32 *lenptr )
 STATIC bool
 S_next_symbol(pTHX_ tempsym_t* symptr )
 {
-  char* patptr = symptr->patptr; 
-  char* patend = symptr->patend; 
+  char* patptr = symptr->patptr;
+  char* patend = symptr->patend;
   const char *allowed = "";
 
   symptr->flags &= ~FLAG_SLASH;
@@ -868,7 +912,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
       if (patptr < patend)
        patptr++;
     } else {
-      /* We should have found a template code */ 
+      /* We should have found a template code */
       I32 code = *patptr++ & 0xFF;
       U32 inherited_modifiers = 0;
 
@@ -881,9 +925,9 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
         }
        continue;
       }
-      
+
       /* for '(', skip to ')' */
-      if (code == '(') {  
+      if (code == '(') {
         if( isDIGIT(*patptr) || *patptr == '*' || *patptr == '[' )
           Perl_croak(aTHX_ "()-group starts with a count in %s",
                      symptr->flags & FLAG_PACK ? "pack" : "unpack" );
@@ -953,7 +997,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
       /* inherit modifiers */
       code |= inherited_modifiers;
 
-      /* look for count and/or / */ 
+      /* look for count and/or / */
       if (patptr < patend) {
        if (isDIGIT(*patptr)) {
          patptr = get_num( patptr, &symptr->length );
@@ -964,7 +1008,7 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
           symptr->howlen = e_star;
 
         } else if (*patptr == '[') {
-          char* lenptr = ++patptr;            
+          char* lenptr = ++patptr;
           symptr->howlen = e_number;
           patptr = group_end( patptr, patend, ']' ) + 1;
           /* what kind of [] is it? */
@@ -1014,31 +1058,31 @@ S_next_symbol(pTHX_ tempsym_t* symptr )
       }
 
       symptr->code = code;
-      symptr->patptr = patptr; 
+      symptr->patptr = patptr;
       return TRUE;
     }
   }
-  symptr->patptr = patptr; 
+  symptr->patptr = patptr;
   return FALSE;
 }
 
 /*
-   There is no way to cleanly handle the case where we should process the 
+   There is no way to cleanly handle the case where we should process the
    string per byte in its upgraded form while it's really in downgraded form
-   (e.g. estimates like strend-s as an upper bound for the number of 
-   characters left wouldn't work). So if we foresee the need of this 
-   (pattern starts with U or contains U0), we want to work on the encoded 
-   version of the string. Users are advised to upgrade their pack string 
+   (e.g. estimates like strend-s as an upper bound for the number of
+   characters left wouldn't work). So if we foresee the need of this
+   (pattern starts with U or contains U0), we want to work on the encoded
+   version of the string. Users are advised to upgrade their pack string
    themselves if they need to do a lot of unpacks like this on it
 */
-STATIC bool 
+STATIC bool
 need_utf8(const char *pat, const char *patend)
 {
     bool first = TRUE;
     while (pat < patend) {
        if (pat[0] == '#') {
            pat++;
-           pat = memchr(pat, '\n', patend-pat);
+           pat = (char *) memchr(pat, '\n', patend-pat);
            if (!pat) return FALSE;
        } else if (pat[0] == 'U') {
            if (first || pat[1] == '0') return TRUE;
@@ -1053,7 +1097,7 @@ first_symbol(const char *pat, const char *patend) {
     while (pat < patend) {
        if (pat[0] != '#') return pat[0];
        pat++;
-       pat = memchr(pat, '\n', patend-pat);
+       pat = (char *) memchr(pat, '\n', patend-pat);
        if (!pat) return 0;
        pat++;
     }
@@ -1078,7 +1122,7 @@ Perl_unpack_str(pTHX_ char *pat, char *patend, char *s, char *strbeg, char *stre
        /* We probably should try to avoid this in case a scalar context call
           wouldn't get to the "U0" */
        STRLEN len = strend - s;
-       s = (char *) bytes_to_utf8(s, &len);
+       s = (char *) bytes_to_utf8((U8 *) s, &len);
        SAVEFREEPV(s);
        strend = s + len;
        flags |= FLAG_DO_UTF8;
@@ -1113,7 +1157,7 @@ Perl_unpackstring(pTHX_ char *pat, char *patend, char *s, char *strend, U32 flag
        /* We probably should try to avoid this in case a scalar context call
           wouldn't get to the "U0" */
        STRLEN len = strend - s;
-       s = (char *) bytes_to_utf8(s, &len);
+       s = (char *) bytes_to_utf8((U8 *) s, &len);
        SAVEFREEPV(s);
        strend = s + len;
        flags |= FLAG_DO_UTF8;
@@ -1142,13 +1186,14 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
     UV cuv = 0;
     NV cdouble = 0.0;
     const int bits_in_uv = CHAR_BIT * sizeof(cuv);
-    char* strrelbeg = s;
     bool beyond = FALSE;
     bool explicit_length;
     bool unpack_only_one = (symptr->flags & FLAG_UNPACK_ONLY_ONE) != 0;
     bool utf8 = (symptr->flags & FLAG_PARSE_UTF8) ? 1 : 0;
+    symptr->strbeg = s - strbeg;
 
     while (next_symbol(symptr)) {
+       packprops_t props;
        I32 len, ai32;
         I32 datumtype = symptr->code;
        /* do first one only unless in list context
@@ -1160,8 +1205,8 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
             break;
 
         switch (howlen = symptr->howlen) {
-        case e_star:
-           len = strend - strbeg;      /* long enough */          
+         case e_star:
+           len = strend - strbeg;      /* long enough */
            break;
          default:
            /* e_no_len and e_number */
@@ -1172,32 +1217,22 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
         explicit_length = TRUE;
       redo_switch:
         beyond = s >= strend;
-       {
-           struct packsize_t *pack_props =
-               &packsize[(symptr->code & TYPE_IS_SHRIEKING) ?
-                         PACK_SIZE_SHRIEKING : PACK_SIZE_NORMAL];
-           const int rawtype = TYPE_NO_MODIFIERS(datumtype);
-           int offset = rawtype - pack_props->first;
-
-           if (offset >= 0 && offset < pack_props->size) {
-               /* Data about this template letter  */
-               unsigned char data = pack_props->array[offset];
-
-               if (data) {
-                   /* data nonzero means we can process this letter.  */
-                   long size = data & PACK_SIZE_MASK;
-                   long howmany = (strend - s) / size;
-                   if (len > howmany)
-                       len = howmany;
-
-                   if (!checksum || (data & PACK_SIZE_CANNOT_CSUM)) {
-                       if (len && unpack_only_one) len = 1;
-                       EXTEND(SP, len);
-                       EXTEND_MORTAL(len);
-                   }
-               }
+
+       props = packprops[TYPE_NO_ENDIANNESS(datumtype)];
+       if (props) {
+           /* props nonzero means we can process this letter. */
+           long size = props & PACK_SIZE_MASK;
+           long howmany = (strend - s) / size;
+           if (len > howmany)
+               len = howmany;
+
+           if (!checksum || (props & PACK_SIZE_CANNOT_CSUM)) {
+               if (len && unpack_only_one) len = 1;
+               EXTEND(SP, len);
+               EXTEND_MORTAL(len);
            }
        }
+
        switch(TYPE_NO_ENDIANNESS(datumtype)) {
        default:
            Perl_croak(aTHX_ "Invalid type '%c' in unpack", (int)TYPE_NO_MODIFIERS(datumtype) );
@@ -1216,6 +1251,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            U32 group_modifiers = TYPE_MODIFIERS(datumtype & ~symptr->flags);
            symptr->flags |= group_modifiers;
             symptr->patend = savsym.grpend;
+           symptr->previous = &savsym;
             symptr->level++;
            PUTBACK;
            while (len--) {
@@ -1227,14 +1263,46 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    break; /* No way to continue */
            }
            SPAGAIN;
-           symptr->flags &= ~group_modifiers;
-            savsym.flags = symptr->flags;
+            savsym.flags = symptr->flags & ~group_modifiers;
             *symptr = savsym;
            break;
        }
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '.' | TYPE_IS_SHRIEKING:
+#endif
+       case '.': {
+           char *from;
+           SV *sv;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           bool u8 = utf8 && !(datumtype & TYPE_IS_SHRIEKING);
+#else /* PERL_PACK_CAN_SHRIEKSIGN */
+           bool u8 = utf8;
+#endif
+           if (howlen == e_star) from = strbeg;
+           else if (len <= 0) from = s;
+           else {
+               tempsym_t *group = symptr;
+
+               while (--len && group) group = group->previous;
+               from = group ? strbeg + group->strbeg : strbeg;
+           }
+           sv = from <= s ?
+               newSVuv(  u8 ? (UV) utf8_length((const U8*)from, (const U8*)s) : (UV) (s-from)) :
+               newSViv(-(u8 ? (IV) utf8_length((const U8*)s, (const U8*)from) : (IV) (from-s)));
+           XPUSHs(sv_2mortal(sv));
+           break;
+       }
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '@' | TYPE_IS_SHRIEKING:
+#endif
        case '@':
-           if (utf8) {
-               s = strrelbeg;
+           s = strbeg + symptr->strbeg;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           if (utf8  && !(datumtype & TYPE_IS_SHRIEKING))
+#else /* PERL_PACK_CAN_SHRIEKSIGN */
+           if (utf8)
+#endif
+           {
                while (len > 0) {
                    if (s >= strend)
                        Perl_croak(aTHX_ "'@' outside of string in unpack");
@@ -1244,9 +1312,9 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                if (s > strend)
                    Perl_croak(aTHX_ "'@' outside of string with malformed UTF-8 in unpack");
            } else {
-           if (len > strend - strrelbeg)
-               Perl_croak(aTHX_ "'@' outside of string in unpack");
-           s = strrelbeg + len;
+               if (strend-s < len)
+                   Perl_croak(aTHX_ "'@' outside of string in unpack");
+               s += len;
            }
            break;
        case 'X' | TYPE_IS_SHRIEKING:
@@ -1262,7 +1330,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                        last = hop;
                        l = len;
                    }
-                   }
+               }
                if (last > s)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                s = last;
@@ -1282,16 +1350,16 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    len--;
                }
            } else {
-           if (len > s - strbeg)
-               Perl_croak(aTHX_ "'X' outside of string in unpack" );
-           s -= len;
+               if (len > s - strbeg)
+                   Perl_croak(aTHX_ "'X' outside of string in unpack" );
+               s -= len;
            }
            break;
        case 'x' | TYPE_IS_SHRIEKING:
            if (!len)                   /* Avoid division by 0 */
                len = 1;
-           if (utf8) ai32 = utf8_length(strbeg, s) % len;
-           else      ai32 = (s - strbeg)           % len;
+           if (utf8) ai32 = utf8_length((U8 *) strbeg, (U8 *) s) % len;
+           else      ai32 = (s - strbeg)                         % len;
            if (ai32 == 0) break;
            len -= ai32;
            /* FALL THROUGH */
@@ -1304,9 +1372,9 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    len--;
                }
            } else {
-           if (len > strend - s)
-               Perl_croak(aTHX_ "'x' outside of string in unpack");
-           s += len;
+               if (len > strend - s)
+                   Perl_croak(aTHX_ "'x' outside of string in unpack");
+               s += len;
            }
            break;
        case '/':
@@ -1328,7 +1396,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                        if (hop > strend)
                            Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
                        break;
-               }
+                   }
                }
                if (hop > strend)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
@@ -1347,9 +1415,19 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            } else if (datumtype == 'A') {
                /* 'A' strips both nulls and spaces */
                char *ptr;
-               for (ptr = s+len-1; ptr >= s; ptr--)
-                   if (*ptr != 0 && !isSPACE(*ptr)) break;
-               ptr++;
+               if (utf8 && (symptr->flags & FLAG_WAS_UTF8)) {
+                   for (ptr = s+len-1; ptr >= s; ptr--)
+                       if (*ptr != 0 && !UTF8_IS_CONTINUATION(*ptr) &&
+                           !is_utf8_space((U8 *) ptr)) break;
+                   if (ptr >= s) ptr += UTF8SKIP(ptr);
+                   else ptr++;
+                   if (ptr > s+len)
+                       Perl_croak(aTHX_ "Malformed UTF-8 string in unpack");
+               } else {
+                   for (ptr = s+len-1; ptr >= s; ptr--)
+                       if (*ptr != 0 && !isSPACE(*ptr)) break;
+                   ptr++;
+               }
                sv = newSVpvn(s, ptr-s);
            } else sv = newSVpvn(s, len);
 
@@ -1388,10 +1466,10 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                        len -= 8;
                    }
                else
-               while (len >= 8) {
+                   while (len >= 8) {
                        cuv += PL_bitcount[*(U8 *)s++];
-                   len -= 8;
-               }
+                       len -= 8;
+                   }
                if (len && s < strend) {
                    U8 bits;
                    bits = SHIFT_BYTE(utf8, s, strend, datumtype);
@@ -1405,7 +1483,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                            if (bits & 0x80) cuv++;
                            bits <<= 1;
                        }
-                   }
+               }
                break;
            }
 
@@ -1443,21 +1521,21 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
        case 'H':
        case 'h': {
            char *str;
-             /* Preliminary length estimate, acceptable for utf8 too */
+           /* Preliminary length estimate, acceptable for utf8 too */
            if (howlen == e_star || len > (strend - s) * 2)
                len = (strend - s) * 2;
-             sv = sv_2mortal(NEWSV(35, len ? len : 1));
+           sv = sv_2mortal(NEWSV(35, len ? len : 1));
            SvPOK_on(sv);
            str = SvPVX(sv);
            if (datumtype == 'h') {
                U8 bits = 0;
-                 ai32 = len;
-                 for (len = 0; len < ai32; len++) {
-                     if (len & 1) bits >>= 4;
-                     else if (utf8) {
-                         if (s >= strend) break;
+               ai32 = len;
+               for (len = 0; len < ai32; len++) {
+                   if (len & 1) bits >>= 4;
+                   else if (utf8) {
+                       if (s >= strend) break;
                        bits = uni_to_byte(aTHX_ &s, strend, datumtype);
-                     } else bits = * (U8 *) s++;
+                   } else bits = * (U8 *) s++;
                    *str++ = PL_hexdigit[bits & 15];
                }
            } else {
@@ -1494,12 +1572,12 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
        case 'W':
          W_checksum:
             if (len == 0) {
-                if (explicit_length && datumtype == 'C') 
+                if (explicit_length && datumtype == 'C')
                    /* Switch to "character" mode */
                    utf8 = (symptr->flags & FLAG_DO_UTF8) ? 1 : 0;
                break;
            }
-           if (datumtype == 'C' ? 
+           if (datumtype == 'C' ?
                 (symptr->flags & FLAG_DO_UTF8) &&
                !(symptr->flags & FLAG_WAS_UTF8) : utf8) {
                while (len-- > 0 && s < strend) {
@@ -1516,7 +1594,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                        cdouble += (NV) val;
                    else
                        cuv += val;
-           }
+               }
            } else if (!checksum)
                while (len-- > 0) {
                    U8 ch = *(U8 *) s++;
@@ -1539,11 +1617,11 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                break;
            }
            if (len > strend - s) len = strend - s;
-               if (!checksum) {
+           if (!checksum) {
                if (len && unpack_only_one) len = 1;
                EXTEND(SP, len);
                EXTEND_MORTAL(len);
-               }
+           }
            while (len-- > 0 && s < strend) {
                STRLEN retlen;
                UV auv;
@@ -1554,11 +1632,12 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    ptr = s;
                    /* Bug: warns about bad utf8 even if we are short on bytes
                       and will break out of the loop */
-                   if (!uni_to_bytes(aTHX_ &ptr, strend, result, 1, 'U'))
+                   if (!uni_to_bytes(aTHX_ &ptr, strend, (char *) result, 1,
+                                     'U'))
                        break;
                    len = UTF8SKIP(result);
-                   if (!uni_to_bytes(aTHX_ &ptr, strend, 
-                                     &result[1], len-1, 'U')) break;
+                   if (!uni_to_bytes(aTHX_ &ptr, strend,
+                                     (char *) &result[1], len-1, 'U')) break;
                    auv = utf8n_to_uvuni(result, len, &retlen, ckWARN(WARN_UTF8) ? 0 : UTF8_ALLOW_ANYUV);
                    s = ptr;
                } else {
@@ -1824,11 +1903,11 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    au32 = vtohl(au32);
 #endif
                if (!checksum)
-                    PUSHs(sv_2mortal(newSVuv((UV)au32)));
-                else if (checksum > bits_in_uv)
-                    cdouble += (NV)au32;
-                else
-                    cuv += au32;
+                   PUSHs(sv_2mortal(newSVuv((UV)au32)));
+               else if (checksum > bits_in_uv)
+                   cdouble += (NV)au32;
+               else
+                   cuv += au32;
            }
            break;
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
@@ -1861,7 +1940,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            while (len-- > 0) {
                char *aptr;
                SHIFT_VAR(utf8, s, strend, aptr, datumtype);
-               DO_BO_UNPACK_P(aptr);
+               DO_BO_UNPACK_PC(aptr);
                /* newSVpv generates undef if aptr is NULL */
                PUSHs(sv_2mortal(newSVpv(aptr, 0)));
            }
@@ -1870,7 +1949,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            {
                UV auv = 0;
                U32 bytes = 0;
-               
+
                while (len > 0 && s < strend) {
                    U8 ch;
                    ch = SHIFT_BYTE(utf8, s, strend, datumtype);
@@ -1916,9 +1995,9 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            if (sizeof(char*) <= strend - s) {
                char *aptr;
                SHIFT_VAR(utf8, s, strend, aptr, datumtype);
-               DO_BO_UNPACK_P(aptr);
-           /* newSVpvn generates undef if aptr is NULL */
-           PUSHs(sv_2mortal(newSVpvn(aptr, len)));
+               DO_BO_UNPACK_PC(aptr);
+               /* newSVpvn generates undef if aptr is NULL */
+               PUSHs(sv_2mortal(newSVpvn(aptr, len)));
            }
            break;
 #ifdef HAS_QUAD
@@ -1961,7 +2040,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    PUSHs(sv_2mortal(newSVnv((NV)afloat)));
                else
                    cdouble += afloat;
-               }
+           }
            break;
        case 'd':
            while (len-- > 0) {
@@ -1972,7 +2051,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    PUSHs(sv_2mortal(newSVnv((NV)adouble)));
                else
                    cdouble += adouble;
-               }
+           }
            break;
        case 'F':
            while (len-- > 0) {
@@ -1983,7 +2062,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    PUSHs(sv_2mortal(newSVnv(anv)));
                else
                    cdouble += anv;
-               }
+           }
            break;
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
        case 'D':
@@ -2047,41 +2126,41 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
                    }
                }
            } else {
-           while (s < strend && *s > ' ' && ISUUCHAR(*s)) {
-               I32 a, b, c, d;
-               char hunk[4];
+               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] = (char)((a << 2) | (b >> 4));
-                   hunk[1] = (char)((b << 4) | (c >> 2));
-                   hunk[2] = (char)((c << 6) | d);
-                   sv_catpvn(sv, hunk, (len > 3) ? 3 : len);
-                   len -= 3;
+                   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] = (char)((a << 2) | (b >> 4));
+                       hunk[1] = (char)((b << 4) | (c >> 2));
+                       hunk[2] = (char)((c << 6) | d);
+                       sv_catpvn(sv, hunk, (len > 3) ? 3 : len);
+                       len -= 3;
+                   }
+                   if (*s == '\n')
+                       s++;
+                   else        /* possible checksum byte */
+                       if (s + 1 < strend && s[1] == '\n')
+                           s += 2;
                }
-               if (*s == '\n')
-                   s++;
-               else    /* possible checksum byte */
-                   if (s + 1 < strend && s[1] == '\n')
-                       s += 2;
-           }
            }
            XPUSHs(sv);
            break;
@@ -2113,7 +2192,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, char *s, char *strbeg, char *strend, char
            XPUSHs(sv_2mortal(sv));
            checksum = 0;
        }
-    
+
         if (symptr->flags & FLAG_SLASH){
             if (SP - PL_stack_base - start_sp_offset <= 0)
                 Perl_croak(aTHX_ "'/' must follow a numeric type in unpack");
@@ -2335,8 +2414,7 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
        return;
     }
 
-    /* We assume a char translates to at most 2 UTF-8 bytes */
-    len = (from_end-from_ptr)*2+(from_ptr-from_start)+1;
+    len = (from_end-from_ptr)*UTF8_EXPAND+(from_ptr-from_start)+1;
     New('U', to_start, len, char);
     Copy(from_start, to_start, from_ptr-from_start, char);
     to_ptr = to_start + (from_ptr-from_start);
@@ -2350,7 +2428,7 @@ marked_upgrade(pTHX_ SV *sv, tempsym_t *sym_ptr) {
 
     for (;from_ptr < from_end; from_ptr++) {
        while (*m == from_ptr) *m++ = to_ptr;
-       to_ptr = uvchr_to_utf8(to_ptr, *(U8 *) from_ptr);
+       to_ptr = (char *) uvchr_to_utf8((U8 *) to_ptr, *(U8 *) from_ptr);
     }
     *to_ptr = 0;
 
@@ -2423,7 +2501,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
 #define NEXTFROM (lengthcode ? lengthcode : items-- > 0 ? *beglist++ : &PL_sv_no)
 
         switch (howlen) {
-        case e_star:
+         case e_star:
            len = strchr("@Xxu", TYPE_NO_MODIFIERS(datumtype)) ?
                0 : items;
            break;
@@ -2434,39 +2512,42 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
         }
 
        if (len) {
-           struct packsize_t *pack_props =
-               &packsize[(symptr->code & TYPE_IS_SHRIEKING) ?
-                         PACK_SIZE_SHRIEKING : PACK_SIZE_NORMAL];
-           const int rawtype = TYPE_NO_MODIFIERS(datumtype);
-           int offset = rawtype - pack_props->first;
-
-           if (offset >= 0 && offset < pack_props->size) {
-               /* Data about this template letter  */
-               unsigned char data = pack_props->array[offset];
-
-               if (data && !(data & PACK_SIZE_UNPREDICTABLE)) {
-                   /* We can process this letter.  */
-                   STRLEN size = data & PACK_SIZE_MASK;
-                   GROWING(utf8, cat, start, cur, (STRLEN) len * size);
-               }
-           }
+           packprops_t props = packprops[TYPE_NO_ENDIANNESS(datumtype)];
 
+           if (props && !(props & PACK_SIZE_UNPREDICTABLE)) {
+               /* We can process this letter. */
+               STRLEN size = props & PACK_SIZE_MASK;
+               GROWING(utf8, cat, start, cur, (STRLEN) len * size);
+           }
         }
 
         /* Look ahead for next symbol. Do we have code/code? */
         lookahead = *symptr;
         found = next_symbol(&lookahead);
-       if ( symptr->flags & FLAG_SLASH ) {
+       if (symptr->flags & FLAG_SLASH) {
+           IV count;
            if (!found) Perl_croak(aTHX_ "Code missing after '/' in pack");
-               if ( 0 == strchr( "aAZ", lookahead.code ) ||
-                     e_star != lookahead.howlen )
-                   Perl_croak(aTHX_ "'/' must be followed by 'a*', 'A*' or 'Z*' in pack");
-           lengthcode =
-               sv_2mortal(newSViv((items > 0 ? DO_UTF8(*beglist) ? sv_len_utf8(*beglist) : sv_len(*beglist) : 0) + (lookahead.code == 'Z' ? 1 : 0)));
+           if (strchr("aAZ", lookahead.code)) {
+               if (lookahead.howlen == e_number) count = lookahead.length;
+               else {
+                   if (items > 0)
+                       count = DO_UTF8(*beglist) ?
+                           sv_len_utf8(*beglist) : sv_len(*beglist);
+                   else count = 0;
+                   if (lookahead.code == 'Z') count++;
+               }
+           } else {
+               if (lookahead.howlen == e_number && lookahead.length < items)
+                   count = lookahead.length;
+               else count = items;
+           }
+           lookahead.howlen = e_number;
+           lookahead.length = count;
+           lengthcode = sv_2mortal(newSViv(count));
        }
 
-       /* Code inside the switch must take care to properly update 
-          cat (CUR length and '\0' termination) if it updated *cur and 
+       /* Code inside the switch must take care to properly update
+          cat (CUR length and '\0' termination) if it updated *cur and
           doesn't simply leave using break */
        switch(TYPE_NO_ENDIANNESS(datumtype)) {
        default:
@@ -2474,31 +2555,66 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                       (int) TYPE_NO_MODIFIERS(datumtype));
        case '%':
            Perl_croak(aTHX_ "'%%' may not be used in pack");
+       {
+           char *from;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '.' | TYPE_IS_SHRIEKING:
+#endif
+       case '.':
+           if (howlen == e_star) from = start;
+           else if (len == 0) from = cur;
+           else {
+               tempsym_t *group = symptr;
+
+               while (--len && group) group = group->previous;
+               from = group ? start + group->strbeg : start;
+           }
+           fromstr = NEXTFROM;
+           len = SvIV(fromstr);
+           goto resize;
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+       case '@' | TYPE_IS_SHRIEKING:
+#endif
        case '@':
-           if (utf8) {
-               char *s = start + symptr->strbeg;
-               while (len > 0 && s < cur) {
-                   s += UTF8SKIP(s);
-                   len--;
+           from = start + symptr->strbeg;
+         resize:
+#ifdef PERL_PACK_CAN_SHRIEKSIGN
+           if (utf8  && !(datumtype & TYPE_IS_SHRIEKING))
+#else /* PERL_PACK_CAN_SHRIEKSIGN */
+           if (utf8)
+#endif
+               if (len >= 0) {
+                   while (len && from < cur) {
+                       from += UTF8SKIP(from);
+                       len--;
+                   }
+                   if (from > cur)
+                       Perl_croak(aTHX_ "Malformed UTF-8 string in pack");
+                   if (len) {
+                       /* Here we know from == cur */
+                     grow:
+                       GROWING(0, cat, start, cur, len);
+                       Zero(cur, len, char);
+                       cur += len;
+                   } else if (from < cur) {
+                       len = cur - from;
+                       goto shrink;
+                   } else goto no_change;
+               } else {
+                   cur = from;
+                   len = -len;
+                   goto utf8_shrink;
                }
-               if (s > cur)
-                   Perl_croak(aTHX_ "Malformed UTF-8 string in pack");
-               if (len > 0) {
-                 grow:
-                   GROWING(0, cat, start, cur, len);
-                   Zero(cur, len, char);
-                   cur += len;
-               } else if (s < cur) cur = s;
-               else goto no_change;
-           } else {
-               len -= cur - (start+symptr->strbeg);
+           else {
+               len -= cur - from;
                if (len > 0) goto grow;
-           len = -len;
-               if (len > 0) goto shrink;
-               else goto no_change;
+               if (len == 0) goto no_change;
+               len = -len;
+               goto shrink;
            }
            break;
-         case '(': {
+       }
+       case '(': {
             tempsym_t savsym = *symptr;
            U32 group_modifiers = TYPE_MODIFIERS(datumtype & ~symptr->flags);
            symptr->flags |= group_modifiers;
@@ -2546,19 +2662,23 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
        case 'X':
            if (utf8) {
                if (len < 1) goto no_change;
+             utf8_shrink:
                while (len > 0) {
                    if (cur <= start)
-                       Perl_croak(aTHX_ "'X' outside of string in pack");
+                       Perl_croak(aTHX_ "'%c' outside of string in pack",
+                                  (int) TYPE_NO_MODIFIERS(datumtype));
                    while (--cur, UTF8_IS_CONTINUATION(*cur)) {
                        if (cur <= start)
-                           Perl_croak(aTHX_ "'X' outside of string in pack");
+                           Perl_croak(aTHX_ "'%c' outside of string in pack",
+                                      (int) TYPE_NO_MODIFIERS(datumtype));
                    }
                    len--;
                }
            } else {
-         shrink:
+             shrink:
                if (cur - start < len)
-               Perl_croak(aTHX_ "'X' outside of string in pack");
+                   Perl_croak(aTHX_ "'%c' outside of string in pack",
+                              (int) TYPE_NO_MODIFIERS(datumtype));
                cur -= len;
            }
            if (cur < start+symptr->strbeg) {
@@ -2571,16 +2691,16 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                lookahead.strbeg = length;
            }
            break;
-         case 'x' | TYPE_IS_SHRIEKING: {
-             I32 ai32;
+       case 'x' | TYPE_IS_SHRIEKING: {
+           I32 ai32;
            if (!len)                   /* Avoid division by 0 */
                len = 1;
-             if (utf8) ai32 = utf8_length(start, cur) % len;
-             else      ai32 = (cur - start) % len;
-             if (ai32 == 0) goto no_change;
-             len -= ai32;
-         }
-           /* FALL THROUGH */
+           if (utf8) ai32 = utf8_length((U8 *) start, (U8 *) cur) % len;
+           else      ai32 = (cur - start) % len;
+           if (ai32 == 0) goto no_change;
+           len -= ai32;
+       }
+       /* FALL THROUGH */
        case 'x':
            goto grow;
        case 'A':
@@ -2601,7 +2721,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    start = SvPVX(cat);
                    cur = start + SvCUR(cat);
                }
-           if (howlen == e_star) {   
+               if (howlen == e_star) {
                    if (utf8) goto string_copy;
                    len = fromlen+1;
                }
@@ -2615,21 +2735,21 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                if (s > end)
                    Perl_croak(aTHX_ "Malformed UTF-8 string in pack");
                if (utf8) {
-               len = fromlen;
+                   len = fromlen;
                    if (datumtype == 'Z') len++;
                    fromlen = s-aptr;
                    len += fromlen;
-                     
+
                    goto string_copy;
-           }
+               }
                fromlen = len - fromlen;
                if (datumtype == 'Z') fromlen--;
                if (howlen == e_star) {
                    len = fromlen;
                    if (datumtype == 'Z') len++;
-           }
+               }
                GROWING(0, cat, start, cur, len);
-               if (!uni_to_bytes(aTHX_ &aptr, end, cur, fromlen, 
+               if (!uni_to_bytes(aTHX_ &aptr, end, cur, fromlen,
                                  datumtype | TYPE_IS_PACK))
                    Perl_croak(aTHX_ "Perl bug: predicted utf8 length not available");
                cur += fromlen;
@@ -2643,15 +2763,16 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    fromlen = len;
                    if (datumtype == 'Z' && fromlen > 0) fromlen--;
                }
-               /* assumes a byte expands to at most 2 bytes on upgrade:
-                  expected_length <= from_len*2 + (len-from_len) */
-               GROWING(0, cat, start, cur, fromlen+len);
+               /* assumes a byte expands to at most UTF8_EXPAND bytes on
+                  upgrade, so:
+                  expected_length <= from_len*UTF8_EXPAND + (len-from_len) */
+               GROWING(0, cat, start, cur, fromlen*(UTF8_EXPAND-1)+len);
                len -= fromlen;
                while (fromlen > 0) {
-                   cur = uvchr_to_utf8(cur, * (U8 *) aptr);
+                   cur = (char *) uvchr_to_utf8((U8 *) cur, * (U8 *) aptr);
                    aptr++;
                    fromlen--;
-                   }
+               }
            } else {
              string_copy:
                if (howlen == e_star) {
@@ -2679,8 +2800,8 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            bool utf8_source;
            U32 utf8_flags;
 
-               fromstr = NEXTFROM;
-               str = SvPV(fromstr, fromlen);
+           fromstr = NEXTFROM;
+           str = SvPV(fromstr, fromlen);
            end = str + fromlen;
            if (DO_UTF8(fromstr)) {
                utf8_source = TRUE;
@@ -2703,7 +2824,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                        bits |= val & 1;
                    } else bits |= *str++ & 1;
                    if (l & 7) bits <<= 1;
-                       else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
@@ -2718,16 +2839,16 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    } else if (*str++ & 1)
                        bits |= 0x80;
                    if (l & 7) bits >>= 1;
-               else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
                }
            l--;
            if (l & 7) {
-                   if (datumtype == 'B')
+               if (datumtype == 'B')
                    bits <<= 7 - (l & 7);
-                   else
+               else
                    bits >>= 7 - (l & 7);
                PUSH_BYTE(utf8, cur, bits);
                l += 7;
@@ -2748,8 +2869,8 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            bool utf8_source;
            U32 utf8_flags;
 
-               fromstr = NEXTFROM;
-               str = SvPV(fromstr, fromlen);
+           fromstr = NEXTFROM;
+           str = SvPV(fromstr, fromlen);
            end = str + fromlen;
            if (DO_UTF8(fromstr)) {
                utf8_source = TRUE;
@@ -2778,7 +2899,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    else
                        bits |= *str++ & 0xf;
                    if (l & 1) bits <<= 4;
-                       else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
                    }
@@ -2797,11 +2918,11 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                    else
                        bits |= (*str++ & 0xf) << 4;
                    if (l & 1) bits >>= 4;
-               else {
+                   else {
                        PUSH_BYTE(utf8, cur, bits);
                        bits = 0;
-                       }
                    }
+               }
            l--;
            if (l & 1) {
                PUSH_BYTE(utf8, cur, bits);
@@ -2814,8 +2935,8 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            Zero(cur, field_len, char);
            cur += field_len;
            break;
-               }
-         case 'c':
+       }
+       case 'c':
            while (len-- > 0) {
                IV aiv;
                fromstr = NEXTFROM;
@@ -2838,110 +2959,112 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                fromstr = NEXTFROM;
                aiv = SvIV(fromstr);
                if ((0 > aiv || aiv > 0xff) &&
-                       ckWARN(WARN_PACK))
-                       Perl_warner(aTHX_ packWARN(WARN_PACK),
-                                   "Character in 'C' format wrapped in pack");
+                   ckWARN(WARN_PACK))
+                   Perl_warner(aTHX_ packWARN(WARN_PACK),
+                               "Character in 'C' format wrapped in pack");
                *cur++ = aiv & 0xff;
            }
-                   break;
-         case 'W': {
-             char *end;
-             U8 in_bytes = IN_BYTES;
-
-             end = start+SvLEN(cat)-1;
-             if (utf8) end -= UTF8_MAXLEN-1;
-             while (len-- > 0) {
-                 UV auv;
-                 fromstr = NEXTFROM;
-                 auv = SvUV(fromstr);
-                 if (in_bytes) auv = auv % 0x100;
-                 if (utf8) {
-                   W_utf8:
-                     if (cur > end) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         
-                         GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
-                         end = start+SvLEN(cat)-UTF8_MAXLEN;
-                     }
-                     cur = uvuni_to_utf8_flags(cur, NATIVE_TO_UNI(auv),
-                                               ckWARN(WARN_UTF8) ?
-                                               0 : UNICODE_ALLOW_ANY);
-                 } else {
-                     if (auv >= 0x100) {
-                         if (!SvUTF8(cat)) {
-                             *cur = '\0';
-                             SvCUR(cat) = cur - start;
-                             marked_upgrade(aTHX_ cat, symptr);
-                             lookahead.flags |= FLAG_DO_UTF8;
-                             lookahead.strbeg = symptr->strbeg;
-                             utf8 = 1;
-                             start = SvPVX(cat);
-                             cur = start + SvCUR(cat);
-                             end = start+SvLEN(cat)-UTF8_MAXLEN;
-                             goto W_utf8;
-                         }
-                         if (ckWARN(WARN_PACK))
-                       Perl_warner(aTHX_ packWARN(WARN_PACK),
-                                         "Character in 'W' format wrapped in pack");
-                         auv &= 0xff;
-                     }
-                     if (cur >= end) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         GROWING(0, cat, start, cur, len+1);
-                         end = start+SvLEN(cat)-1;
-                     }
-                     *(U8 *) cur++ = auv;
+           break;
+       case 'W': {
+           char *end;
+           U8 in_bytes = IN_BYTES;
+
+           end = start+SvLEN(cat)-1;
+           if (utf8) end -= UTF8_MAXLEN-1;
+           while (len-- > 0) {
+               UV auv;
+               fromstr = NEXTFROM;
+               auv = SvUV(fromstr);
+               if (in_bytes) auv = auv % 0x100;
+               if (utf8) {
+                 W_utf8:
+                   if (cur > end) {
+                       *cur = '\0';
+                       SvCUR(cat) = cur - start;
+
+                       GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
+                       end = start+SvLEN(cat)-UTF8_MAXLEN;
+                   }
+                   cur = (char *) uvuni_to_utf8_flags((U8 *) cur,
+                                                      NATIVE_TO_UNI(auv),
+                                                      ckWARN(WARN_UTF8) ?
+                                                      0 : UNICODE_ALLOW_ANY);
+               } else {
+                   if (auv >= 0x100) {
+                       if (!SvUTF8(cat)) {
+                           *cur = '\0';
+                           SvCUR(cat) = cur - start;
+                           marked_upgrade(aTHX_ cat, symptr);
+                           lookahead.flags |= FLAG_DO_UTF8;
+                           lookahead.strbeg = symptr->strbeg;
+                           utf8 = 1;
+                           start = SvPVX(cat);
+                           cur = start + SvCUR(cat);
+                           end = start+SvLEN(cat)-UTF8_MAXLEN;
+                           goto W_utf8;
+                       }
+                       if (ckWARN(WARN_PACK))
+                           Perl_warner(aTHX_ packWARN(WARN_PACK),
+                                       "Character in 'W' format wrapped in pack");
+                       auv &= 0xff;
+                   }
+                   if (cur >= end) {
+                       *cur = '\0';
+                       SvCUR(cat) = cur - start;
+                       GROWING(0, cat, start, cur, len+1);
+                       end = start+SvLEN(cat)-1;
+                   }
+                   *(U8 *) cur++ = (U8)auv;
                }
            }
            break;
-         }
-         case 'U': {
-             char *end;
-
-             if (len == 0) {
-                 if (!(symptr->flags & FLAG_DO_UTF8)) {
-                     marked_upgrade(aTHX_ cat, symptr);
-                     lookahead.flags |= FLAG_DO_UTF8;
-                     lookahead.strbeg = symptr->strbeg;
-                 }
-                 utf8 = 0;
-                 goto no_change;
-             }
-
-             end = start+SvLEN(cat);
-             if (!utf8) end -= UTF8_MAXLEN;
+       }
+       case 'U': {
+           char *end;
+
+           if (len == 0) {
+               if (!(symptr->flags & FLAG_DO_UTF8)) {
+                   marked_upgrade(aTHX_ cat, symptr);
+                   lookahead.flags |= FLAG_DO_UTF8;
+                   lookahead.strbeg = symptr->strbeg;
+               }
+               utf8 = 0;
+               goto no_change;
+           }
+
+           end = start+SvLEN(cat);
+           if (!utf8) end -= UTF8_MAXLEN;
            while (len-- > 0) {
-                 UV auv;
+               UV auv;
                fromstr = NEXTFROM;
-                 auv = SvUV(fromstr);
-                 if (utf8) {
-                     char buffer[UTF8_MAXLEN], *endb;
-                     endb = uvuni_to_utf8_flags(buffer, auv,
-                                                    ckWARN(WARN_UTF8) ?
-                                               0 : UNICODE_ALLOW_ANY);
-                     if (cur >= end-(endb-buffer)*2) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         GROWING(0, cat, start, cur, len+(endb-buffer)*2);
-                         end = start+SvLEN(cat)-UTF8_MAXLEN;
-                     }
-                     bytes_to_uni(aTHX_ buffer, endb-buffer, &cur);
-                 } else {
-                     if (cur >= end) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
-                         end = start+SvLEN(cat)-UTF8_MAXLEN;
-                     }
-                     cur = uvuni_to_utf8_flags(cur, auv,
-                                                    ckWARN(WARN_UTF8) ?
-                                                0 : UNICODE_ALLOW_ANY);
-                 }
+               auv = SvUV(fromstr);
+               if (utf8) {
+                   U8 buffer[UTF8_MAXLEN], *endb;
+                   endb = uvuni_to_utf8_flags(buffer, auv,
+                                              ckWARN(WARN_UTF8) ?
+                                              0 : UNICODE_ALLOW_ANY);
+                   if (cur+(endb-buffer)*UTF8_EXPAND >= end) {
+                       *cur = '\0';
+                       SvCUR(cat) = cur - start;
+                       GROWING(0, cat, start, cur,
+                               len+(endb-buffer)*UTF8_EXPAND);
+                       end = start+SvLEN(cat);
+                   }
+                   bytes_to_uni(aTHX_ buffer, endb-buffer, &cur);
+               } else {
+                   if (cur >= end) {
+                       *cur = '\0';
+                       SvCUR(cat) = cur - start;
+                       GROWING(0, cat, start, cur, len+UTF8_MAXLEN);
+                       end = start+SvLEN(cat)-UTF8_MAXLEN;
+                   }
+                   cur = (char *) uvuni_to_utf8_flags((U8 *) cur, auv,
+                                                      ckWARN(WARN_UTF8) ?
+                                                      0 : UNICODE_ALLOW_ANY);
+               }
            }
            break;
-         }
+       }
        /* Float and double added by gnb@melba.bby.oz.au  22/11/89 */
        case 'f':
            while (len-- > 0) {
@@ -2951,25 +3074,25 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                anv = SvNV(fromstr);
 #ifdef __VOS__
                /* VOS does not automatically map a floating-point overflow
-   during conversion from double to float into infinity, so we
-   do it by hand.  This code should either be generalized for
-   any OS that needs it, or removed if and when VOS implements
-   posix-976 (suggestion to support mapping to infinity).
-   Paul.Green@stratus.com 02-04-02.  */
+                  during conversion from double to float into infinity, so we
+                  do it by hand.  This code should either be generalized for
+                  any OS that needs it, or removed if and when VOS implements
+                  posix-976 (suggestion to support mapping to infinity).
+                  Paul.Green@stratus.com 02-04-02.  */
                if (anv > FLT_MAX)
-                    afloat = _float_constants[0];   /* single prec. inf. */
+                   afloat = _float_constants[0];   /* single prec. inf. */
                else if (anv < -FLT_MAX)
-                    afloat = _float_constants[0];   /* single prec. inf. */
+                   afloat = _float_constants[0];   /* single prec. inf. */
                else afloat = (float) anv;
 #else /* __VOS__ */
 # if defined(VMS) && !defined(__IEEE_FP)
                /* IEEE fp overflow shenanigans are unavailable on VAX and optional
- * on Alpha; fake it if we don't have them.
- */
+                * on Alpha; fake it if we don't have them.
+                */
                if (anv > FLT_MAX)
-                    afloat = FLT_MAX;
+                   afloat = FLT_MAX;
                else if (anv < -FLT_MAX)
-                    afloat = -FLT_MAX;
+                   afloat = -FLT_MAX;
                else afloat = (float)anv;
 # else
                afloat = (float)anv;
@@ -2987,25 +3110,25 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                anv = SvNV(fromstr);
 #ifdef __VOS__
                /* VOS does not automatically map a floating-point overflow
-   during conversion from long double to double into infinity,
-   so we do it by hand.  This code should either be generalized
-   for any OS that needs it, or removed if and when VOS
-   implements posix-976 (suggestion to support mapping to
-   infinity).  Paul.Green@stratus.com 02-04-02.  */
+                  during conversion from long double to double into infinity,
+                  so we do it by hand.  This code should either be generalized
+                  for any OS that needs it, or removed if and when VOS
+                  implements posix-976 (suggestion to support mapping to
+                  infinity).  Paul.Green@stratus.com 02-04-02.  */
                if (anv > DBL_MAX)
-                    adouble = _double_constants[0];   /* double prec. inf. */
+                   adouble = _double_constants[0];   /* double prec. inf. */
                else if (anv < -DBL_MAX)
-                    adouble = _double_constants[0];   /* double prec. inf. */
+                   adouble = _double_constants[0];   /* double prec. inf. */
                else adouble = (double) anv;
 #else /* __VOS__ */
 # if defined(VMS) && !defined(__IEEE_FP)
                /* IEEE fp overflow shenanigans are unavailable on VAX and optional
- * on Alpha; fake it if we don't have them.
- */
+                * on Alpha; fake it if we don't have them.
+                */
                if (anv > DBL_MAX)
-                    adouble = DBL_MAX;
+                   adouble = DBL_MAX;
                else if (anv < -DBL_MAX)
-                    adouble = -DBL_MAX;
+                   adouble = -DBL_MAX;
                else adouble = (double)anv;
 # else
                adouble = (double)anv;
@@ -3015,30 +3138,30 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                PUSH_VAR(utf8, cur, adouble);
            }
            break;
-         case 'F': {
-             NV anv;
+       case 'F': {
+           NV anv;
            Zero(&anv, 1, NV); /* can be long double with unused bits */
            while (len-- > 0) {
                fromstr = NEXTFROM;
                anv = SvNV(fromstr);
                DO_BO_PACK_N(anv, NV);
-                 PUSH_VAR(utf8, cur, anv);
+               PUSH_VAR(utf8, cur, anv);
            }
            break;
-         }
+       }
 #if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
-         case 'D': {
-             long double aldouble;
+       case 'D': {
+           long double aldouble;
            /* long doubles can have unused bits, which may be nonzero */
            Zero(&aldouble, 1, long double);
            while (len-- > 0) {
                fromstr = NEXTFROM;
                aldouble = (long double)SvNV(fromstr);
                DO_BO_PACK_N(aldouble, long double);
-                 PUSH_VAR(utf8, cur, aldouble);
+               PUSH_VAR(utf8, cur, aldouble);
            }
            break;
-         }
+       }
 #endif
 #ifdef PERL_PACK_CAN_SHRIEKSIGN
        case 'n' | TYPE_IS_SHRIEKING:
@@ -3070,33 +3193,33 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            break;
         case 'S' | TYPE_IS_SHRIEKING:
 #if SHORTSIZE != SIZE16
-               while (len-- > 0) {
+           while (len-- > 0) {
                unsigned short aushort;
-                   fromstr = NEXTFROM;
-                   aushort = SvUV(fromstr);
-                   DO_BO_PACK(aushort, s);
+               fromstr = NEXTFROM;
+               aushort = SvUV(fromstr);
+               DO_BO_PACK(aushort, s);
                PUSH_VAR(utf8, cur, aushort);
-            }
+           }
             break;
 #else
             /* Fall through! */
 #endif
        case 'S':
-               while (len-- > 0) {
+           while (len-- > 0) {
                U16 au16;
-                   fromstr = NEXTFROM;
-                   au16 = (U16)SvUV(fromstr);
-                   DO_BO_PACK(au16, 16);
+               fromstr = NEXTFROM;
+               au16 = (U16)SvUV(fromstr);
+               DO_BO_PACK(au16, 16);
                PUSH16(utf8, cur, &au16);
            }
            break;
        case 's' | TYPE_IS_SHRIEKING:
 #if SHORTSIZE != SIZE16
-               while (len-- > 0) {
+           while (len-- > 0) {
                short ashort;
-                   fromstr = NEXTFROM;
-                   ashort = SvIV(fromstr);
-                   DO_BO_PACK(ashort, s);
+               fromstr = NEXTFROM;
+               ashort = SvIV(fromstr);
+               DO_BO_PACK(ashort, s);
                PUSH_VAR(utf8, cur, ashort);
            }
             break;
@@ -3239,7 +3362,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                                       in, (result + len) - in);
                    Safefree(result);
                    SvREFCNT_dec(norm); /* free norm */
-               }
+               }
            }
             break;
        case 'i':
@@ -3282,11 +3405,11 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
            break;
        case 'L' | TYPE_IS_SHRIEKING:
 #if LONGSIZE != SIZE32
-               while (len-- > 0) {
+           while (len-- > 0) {
                unsigned long aulong;
-                   fromstr = NEXTFROM;
-                   aulong = SvUV(fromstr);
-                   DO_BO_PACK(aulong, l);
+               fromstr = NEXTFROM;
+               aulong = SvUV(fromstr);
+               DO_BO_PACK(aulong, l);
                PUSH_VAR(utf8, cur, aulong);
            }
            break;
@@ -3294,21 +3417,21 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
             /* Fall though! */
 #endif
        case 'L':
-               while (len-- > 0) {
+           while (len-- > 0) {
                U32 au32;
-                   fromstr = NEXTFROM;
-                   au32 = SvUV(fromstr);
-                   DO_BO_PACK(au32, 32);
+               fromstr = NEXTFROM;
+               au32 = SvUV(fromstr);
+               DO_BO_PACK(au32, 32);
                PUSH32(utf8, cur, &au32);
            }
            break;
        case 'l' | TYPE_IS_SHRIEKING:
 #if LONGSIZE != SIZE32
-               while (len-- > 0) {
+           while (len-- > 0) {
                long along;
-                   fromstr = NEXTFROM;
-                   along = SvIV(fromstr);
-                   DO_BO_PACK(along, l);
+               fromstr = NEXTFROM;
+               along = SvIV(fromstr);
+               DO_BO_PACK(along, l);
                PUSH_VAR(utf8, cur, along);
            }
            break;
@@ -3366,61 +3489,61 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist )
                        (SvTEMP(fromstr) || (SvPADTMP(fromstr) &&
                                             !SvREADONLY(fromstr)))) {
                        Perl_warner(aTHX_ packWARN(WARN_PACK),
-                               "Attempt to pack pointer to temporary value");
+                                   "Attempt to pack pointer to temporary value");
                    }
                    if (SvPOK(fromstr) || SvNIOK(fromstr))
                        aptr = SvPV_flags(fromstr, n_a, 0);
                    else
                        aptr = SvPV_force_flags(fromstr, n_a, 0);
                }
-               DO_BO_PACK_P(aptr);
+               DO_BO_PACK_PC(aptr);
                PUSH_VAR(utf8, cur, aptr);
            }
            break;
-         case 'u': {
-             char *aptr, *aend;
-             bool from_utf8;
+       case 'u': {
+           char *aptr, *aend;
+           bool from_utf8;
 
            fromstr = NEXTFROM;
-             if (len <= 2) len = 45;
-             else len = len / 3 * 3;
-             if (len >= 64) {
-                 Perl_warner(aTHX_ packWARN(WARN_PACK),
-                             "Field too wide in 'u' format in pack");
-                 len = 63;
-             }
+           if (len <= 2) len = 45;
+           else len = len / 3 * 3;
+           if (len >= 64) {
+               Perl_warner(aTHX_ packWARN(WARN_PACK),
+                           "Field too wide in 'u' format in pack");
+               len = 63;
+           }
            aptr = SvPV(fromstr, fromlen);
-             from_utf8 = DO_UTF8(fromstr);
-             if (from_utf8) {
-                 aend = aptr + fromlen;
-                 fromlen = sv_len_utf8(fromstr);
-             } else aend = NULL; /* Unused, but keep compilers happy */
-             GROWING(utf8, cat, start, cur, (fromlen+2) / 3 * 4 + (fromlen+len-1)/len * 2);
+           from_utf8 = DO_UTF8(fromstr);
+           if (from_utf8) {
+               aend = aptr + fromlen;
+               fromlen = sv_len_utf8(fromstr);
+           } else aend = NULL; /* Unused, but keep compilers happy */
+           GROWING(utf8, cat, start, cur, (fromlen+2) / 3 * 4 + (fromlen+len-1)/len * 2);
            while (fromlen > 0) {
-                 U8 *end;
+               U8 *end;
                I32 todo;
-                 U8 hunk[1+63/3*4+1];
+               U8 hunk[1+63/3*4+1];
 
                if ((I32)fromlen > len)
                    todo = len;
                else
                    todo = fromlen;
-                 if (from_utf8) {
-                     char buffer[64];
-                     if (!uni_to_bytes(aTHX_ &aptr, aend, buffer, todo, 
-                                       'u' | TYPE_IS_PACK)) {
-                         *cur = '\0';
-                         SvCUR(cat) = cur - start;
-                         Perl_croak(aTHX_ "Assertion: string is shorter than advertised");
-                     }
-                     end = doencodes(hunk, buffer, todo);
-                 } else {
-                     end = doencodes(hunk, aptr, todo);
-               aptr += todo;
-           }
-                 PUSH_BYTES(utf8, cur, hunk, end-hunk);
-                 fromlen -= todo;
-             }
+               if (from_utf8) {
+                   char buffer[64];
+                   if (!uni_to_bytes(aTHX_ &aptr, aend, buffer, todo,
+                                     'u' | TYPE_IS_PACK)) {
+                       *cur = '\0';
+                       SvCUR(cat) = cur - start;
+                       Perl_croak(aTHX_ "Assertion: string is shorter than advertised");
+                   }
+                   end = doencodes(hunk, buffer, todo);
+               } else {
+                   end = doencodes(hunk, aptr, todo);
+                   aptr += todo;
+               }
+               PUSH_BYTES(utf8, cur, hunk, end-hunk);
+               fromlen -= todo;
+           }
            break;
        }
        }