Upgrade to Cwd 2.16
[p5sagit/p5-mst-13.2.git] / ext / Storable / Storable.xs
index 6436d0d..5b3868b 100644 (file)
@@ -1,9 +1,5 @@
 /*
- * Store and retrieve mechanism.
- */
-
-/*
- * $Id: Storable.xs,v 1.0.1.10 2001/08/28 21:52:14 ram Exp $
+ *  Store and retrieve mechanism.
  *
  *  Copyright (c) 1995-2000, Raphael Manfredi
  *  
 
 #include <EXTERN.h>
 #include <perl.h>
-#include <patchlevel.h>                /* Perl's one, needed since 5.6 */
 #include <XSUB.h>
 
+#ifndef PATCHLEVEL
+#    include <patchlevel.h>            /* Perl's one, needed since 5.6 */
+#    if !(defined(PERL_VERSION) || (SUBVERSION > 0 && defined(PATCHLEVEL)))
+#        include <could_not_find_Perl_patchlevel.h>
+#    endif
+#endif
+
 #ifndef NETWARE
 #if 0
 #define DEBUGME /* Debug mode, turns assertions on as well */
@@ -153,7 +155,8 @@ typedef double NV;                  /* Older perls lack the NV type */
 #define SX_UTF8STR     C(23)   /* UTF-8 string forthcoming (small) */
 #define SX_LUTF8STR    C(24)   /* UTF-8 string forthcoming (large) */
 #define SX_FLAG_HASH   C(25)   /* Hash with flags forthcoming (size, flags, key/flags/value triplet list) */
-#define SX_ERROR       C(26)   /* Error */
+#define SX_CODE         C(26)   /* Code references as perl source code */
+#define SX_ERROR       C(27)   /* Error */
 
 /*
  * Those are only used to retrieve "old" pre-0.6 binary images.
@@ -293,6 +296,8 @@ typedef struct stcxt {
        int netorder;           /* true if network order used */
        int s_tainted;          /* true if input source is tainted, at retrieve time */
        int forgive_me;         /* whether to be forgiving... */
+       int deparse;        /* whether to deparse code refs */
+       SV *eval;           /* whether to eval source code */
        int canonical;          /* whether to store hashes sorted by key */
 #ifndef HAS_RESTRICTED_HASHES
         int derestrict;         /* whether to downgrade restrcted hashes */
@@ -355,14 +360,14 @@ typedef struct stcxt {
 
 #else /* !MULTIPLICITY && !PERL_OBJECT && !PERL_CAPI */
 
-static stcxt_t Context;
-static stcxt_t *Context_ptr = &Context;
+static stcxt_t *Context_ptr = NULL;
 #define dSTCXT                 stcxt_t *cxt = Context_ptr
+#define SET_STCXT(x)           Context_ptr = x
 #define INIT_STCXT                                             \
        dSTCXT;                                                         \
-       NEW_STORABLE_CXT_OBJ(cxt)
+       NEW_STORABLE_CXT_OBJ(cxt);                      \
+       SET_STCXT(cxt)
 
-#define SET_STCXT(x)           Context_ptr = x
 
 #endif /* MULTIPLICITY || PERL_OBJECT || PERL_CAPI */
 
@@ -465,7 +470,7 @@ static stcxt_t *Context_ptr = &Context;
        if (!mbase) {                                           \
                TRACEME(("** allocating mbase of %d bytes", MGROW)); \
                New(10003, mbase, MGROW, char); \
-               msiz = MGROW;                                   \
+               msiz = (STRLEN)MGROW;                                   \
        }                                                                       \
        mptr = mbase;                                           \
        if (x)                                                          \
@@ -632,7 +637,8 @@ static stcxt_t *Context_ptr = &Context;
 #define svis_HASH              3
 #define svis_TIED              4
 #define svis_TIED_ITEM 5
-#define svis_OTHER             6
+#define svis_CODE              6
+#define svis_OTHER             7
 
 /*
  * Flags for SX_HOOK.
@@ -760,7 +766,7 @@ static const char byteorderstr_56[] = {BYTEORDER_BYTES_56, 0};
 #endif
 
 #define STORABLE_BIN_MAJOR     2               /* Binary major "version" */
-#define STORABLE_BIN_MINOR     5               /* Binary minor "version" */
+#define STORABLE_BIN_MINOR     6               /* Binary minor "version" */
 
 /* If we aren't 5.7.3 or later, we won't be writing out files that use the
  * new flagged hash introdued in 2.5, so put 2.4 in the binary header to
@@ -774,13 +780,24 @@ static const char byteorderstr_56[] = {BYTEORDER_BYTES_56, 0};
  * As of perl 5.7.3, utf8 hash key is introduced.
  * So this must change -- dankogai
 */
-#define STORABLE_BIN_WRITE_MINOR       5
+#define STORABLE_BIN_WRITE_MINOR       6
 #endif /* (PATCHLEVEL <= 6) */
 
+#if (PATCHLEVEL < 8 || (PATCHLEVEL == 8 && SUBVERSION < 1))
+#define PL_sv_placeholder PL_sv_undef
+#endif
+
 /*
  * Useful store shortcuts...
  */
 
+/*
+ * Note that if you put more than one mark for storing a particular
+ * type of thing, *and* in the retrieve_foo() function you mark both
+ * the thingy's you get off with SEEN(), you *must* increase the
+ * tagnum with cxt->tagnum++ along with this macro!
+ *     - samv 20Jan04
+ */
 #define PUTMARK(x)                                                     \
   STMT_START {                                                         \
        if (!cxt->fio)                                                  \
@@ -844,12 +861,12 @@ static const char byteorderstr_56[] = {BYTEORDER_BYTES_56, 0};
 #define STORE_SCALAR(pv, len)  STORE_PV_LEN(pv, len, SX_SCALAR, SX_LSCALAR)
 
 /*
- * Store undef in arrays and hashes without recursing through store().
+ * Store &PL_sv_undef in arrays without recursing through store().
  */
-#define STORE_UNDEF()                                  \
+#define STORE_SV_UNDEF()                                       \
   STMT_START {                                                 \
        cxt->tagnum++;                                          \
-       PUTMARK(SX_UNDEF);                                      \
+       PUTMARK(SX_SV_UNDEF);                                   \
   } STMT_END
 
 /*
@@ -968,6 +985,7 @@ static int store_array(stcxt_t *cxt, AV *av);
 static int store_hash(stcxt_t *cxt, HV *hv);
 static int store_tied(stcxt_t *cxt, SV *sv);
 static int store_tied_item(stcxt_t *cxt, SV *sv);
+static int store_code(stcxt_t *cxt, CV *cv);
 static int store_other(stcxt_t *cxt, SV *sv);
 static int store_blessed(stcxt_t *cxt, SV *sv, int type, HV *pkg);
 
@@ -978,6 +996,7 @@ static int (*sv_store[])(stcxt_t *cxt, SV *sv) = {
        (int (*)(stcxt_t *cxt, SV *sv)) store_hash,             /* svis_HASH */
        store_tied,                                                                             /* svis_TIED */
        store_tied_item,                                                                /* svis_TIED_ITEM */
+       (int (*)(stcxt_t *cxt, SV *sv)) store_code,             /* svis_CODE */
        store_other,                                                                    /* svis_OTHER */
 };
 
@@ -1031,6 +1050,7 @@ static SV *(*sv_old_retrieve[])(stcxt_t *cxt, char *cname) = {
        retrieve_other,                 /* SX_UTF8STR not supported */
        retrieve_other,                 /* SX_LUTF8STR not supported */
        retrieve_other,                 /* SX_FLAG_HASH not supported */
+       retrieve_other,                 /* SX_CODE not supported */
        retrieve_other,                 /* SX_ERROR */
 };
 
@@ -1046,6 +1066,7 @@ static SV *retrieve_overloaded(stcxt_t *cxt, char *cname);
 static SV *retrieve_tied_key(stcxt_t *cxt, char *cname);
 static SV *retrieve_tied_idx(stcxt_t *cxt, char *cname);
 static SV *retrieve_flag_hash(stcxt_t *cxt, char *cname);
+static SV *retrieve_code(stcxt_t *cxt, char *cname);
 
 static SV *(*sv_retrieve[])(stcxt_t *cxt, char *cname) = {
        0,                      /* SX_OBJECT -- entry unused dynamically */
@@ -1074,6 +1095,7 @@ static SV *(*sv_retrieve[])(stcxt_t *cxt, char *cname) = {
        retrieve_utf8str,               /* SX_UTF8STR  */
        retrieve_lutf8str,              /* SX_LUTF8STR */
        retrieve_flag_hash,             /* SX_HASH */
+       retrieve_code,                  /* SX_CODE */
        retrieve_other,                 /* SX_ERROR */
 };
 
@@ -1126,6 +1148,8 @@ static void init_store_context(
 
        cxt->netorder = network_order;
        cxt->forgive_me = -1;                   /* Fetched from perl if needed */
+       cxt->deparse = -1;                              /* Idem */
+       cxt->eval = NULL;                               /* Idem */
        cxt->canonical = -1;                    /* Idem */
        cxt->tagnum = -1;                               /* Reset tag numbers */
        cxt->classnum = -1;                             /* Reset class numbers */
@@ -1272,6 +1296,11 @@ static void clean_store_context(stcxt_t *cxt)
        }
 
        cxt->forgive_me = -1;                   /* Fetched from perl if needed */
+       cxt->deparse = -1;                              /* Idem */
+       if (cxt->eval) {
+           SvREFCNT_dec(cxt->eval);
+       }
+       cxt->eval = NULL;                               /* Idem */
        cxt->canonical = -1;                    /* Idem */
 
        reset_context(cxt);
@@ -1304,7 +1333,8 @@ static void init_retrieve_context(stcxt_t *cxt, int optype, int is_tainted)
         * new retrieve routines.
         */
 
-       cxt->hseen = ((cxt->retrieve_vtbl == sv_old_retrieve) ? newHV() : 0);
+       cxt->hseen = (((void*)cxt->retrieve_vtbl == (void*)sv_old_retrieve)
+                     ? newHV() : 0);
 
        cxt->aseen = newAV();                   /* Where retrieved objects are kept */
        cxt->aclass = newAV();                  /* Where seen classnames are kept */
@@ -1942,7 +1972,7 @@ static int store_scalar(stcxt_t *cxt, SV *sv)
 #else
 
             SvIV_please(sv);
-            if (SvIOK(sv)) {
+           if (SvIOK_notUV(sv)) {
                 iv = SvIV(sv);
                 goto integer;          /* Share code above */
             }
@@ -2018,7 +2048,7 @@ static int store_array(stcxt_t *cxt, AV *av)
                sav = av_fetch(av, i, 0);
                if (!sav) {
                        TRACEME(("(#%d) undef item", i));
-                       STORE_UNDEF();
+                       STORE_SV_UNDEF();
                        continue;
                }
                TRACEME(("(#%d) item", i));
@@ -2189,7 +2219,7 @@ static int store_hash(stcxt_t *cxt, HV *hv)
                             = (((hash_flags & SHV_RESTRICTED)
                                 && SvREADONLY(val))
                                ? SHV_K_LOCKED : 0);
-                        if (val == &PL_sv_undef)
+                        if (val == &PL_sv_placeholder)
                             flags |= SHV_K_PLACEHOLDER;
 
                        keyval = SvPV(key, keylen_tmp);
@@ -2230,7 +2260,13 @@ static int store_hash(stcxt_t *cxt, HV *hv)
                             PUTMARK(flags);
                             TRACEME(("(#%d) key '%s' flags %x %u", i, keyval, flags, *keyval));
                         } else {
-                            assert (flags == 0);
+                            /* This is a workaround for a bug in 5.8.0
+                               that causes the HEK_WASUTF8 flag to be
+                               set on an HEK without the hash being
+                               marked as having key flags. We just
+                               cross our fingers and drop the flag.
+                               AMS 20030901 */
+                            assert (flags == 0 || flags == SHV_K_WASUTF8);
                             TRACEME(("(#%d) key '%s'", i, keyval));
                         }
                        WLEN(keylen);
@@ -2251,7 +2287,7 @@ static int store_hash(stcxt_t *cxt, HV *hv)
 
                /*
                 * Storing in "random" order (in the order the keys are stored
-                * within the the hash).  This is the default and will be faster!
+                * within the hash).  This is the default and will be faster!
                 */
   
                for (i = 0; i < len; i++) {
@@ -2285,7 +2321,7 @@ static int store_hash(stcxt_t *cxt, HV *hv)
                             = (((hash_flags & SHV_RESTRICTED)
                                 && SvREADONLY(val))
                                              ? SHV_K_LOCKED : 0);
-                        if (val == &PL_sv_undef)
+                        if (val == &PL_sv_placeholder)
                             flags |= SHV_K_PLACEHOLDER;
 
                         hek = HeKEY_hek(he);
@@ -2321,7 +2357,13 @@ static int store_hash(stcxt_t *cxt, HV *hv)
                             PUTMARK(flags);
                             TRACEME(("(#%d) key '%s' flags %x", i, key, flags));
                         } else {
-                            assert (flags == 0);
+                            /* This is a workaround for a bug in 5.8.0
+                               that causes the HEK_WASUTF8 flag to be
+                               set on an HEK without the hash being
+                               marked as having key flags. We just
+                               cross our fingers and drop the flag.
+                               AMS 20030901 */
+                            assert (flags == 0 || flags == SHV_K_WASUTF8);
                             TRACEME(("(#%d) key '%s'", i, key));
                         }
                         if (flags & SHV_K_ISSV) {
@@ -2344,6 +2386,110 @@ out:
 }
 
 /*
+ * store_code
+ *
+ * Store a code reference.
+ *
+ * Layout is SX_CODE <length> followed by a scalar containing the perl
+ * source code of the code reference.
+ */
+static int store_code(stcxt_t *cxt, CV *cv)
+{
+#if PERL_VERSION < 6
+    /*
+        * retrieve_code does not work with perl 5.005 or less
+        */
+       return store_other(cxt, (SV*)cv);
+#else
+       dSP;
+       I32 len;
+       int count, reallen;
+       SV *text, *bdeparse;
+
+       TRACEME(("store_code (0x%"UVxf")", PTR2UV(cv)));
+
+       if (
+               cxt->deparse == 0 ||
+               (cxt->deparse < 0 && !(cxt->deparse =
+                       SvTRUE(perl_get_sv("Storable::Deparse", TRUE)) ? 1 : 0))
+       ) {
+               return store_other(cxt, (SV*)cv);
+       }
+
+       /*
+        * Require B::Deparse. At least B::Deparse 0.61 is needed for
+        * blessed code references.
+        */
+       /* XXX sv_2mortal seems to be evil here. why? */
+       load_module(PERL_LOADMOD_NOIMPORT, newSVpvn("B::Deparse",10), newSVnv(0.61));
+
+       ENTER;
+       SAVETMPS;
+
+       /*
+        * create the B::Deparse object
+        */
+
+       PUSHMARK(sp);
+       XPUSHs(sv_2mortal(newSVpvn("B::Deparse",10)));
+       PUTBACK;
+       count = call_method("new", G_SCALAR);
+       SPAGAIN;
+       if (count != 1)
+               CROAK(("Unexpected return value from B::Deparse::new\n"));
+       bdeparse = POPs;
+
+       /*
+        * call the coderef2text method
+        */
+
+       PUSHMARK(sp);
+       XPUSHs(bdeparse); /* XXX is this already mortal? */
+       XPUSHs(sv_2mortal(newRV_inc((SV*)cv)));
+       PUTBACK;
+       count = call_method("coderef2text", G_SCALAR);
+       SPAGAIN;
+       if (count != 1)
+               CROAK(("Unexpected return value from B::Deparse::coderef2text\n"));
+
+       text = POPs;
+       len = SvLEN(text);
+       reallen = strlen(SvPV_nolen(text));
+
+       /*
+        * Empty code references or XS functions are deparsed as
+        * "(prototype) ;" or ";".
+        */
+
+       if (len == 0 || *(SvPV_nolen(text)+reallen-1) == ';') {
+           CROAK(("The result of B::Deparse::coderef2text was empty - maybe you're trying to serialize an XS function?\n"));
+       }
+
+       /* 
+        * Signal code by emitting SX_CODE.
+        */
+
+       PUTMARK(SX_CODE);
+       cxt->tagnum++;   /* necessary, as SX_CODE is a SEEN() candidate */
+       TRACEME(("size = %d", len));
+       TRACEME(("code = %s", SvPV_nolen(text)));
+
+       /*
+        * Now store the source code.
+        */
+
+       STORE_SCALAR(SvPV_nolen(text), len);
+
+       FREETMPS;
+       LEAVE;
+
+       TRACEME(("ok (code)"));
+
+       return 0;
+#endif
+}
+
+/*
  * store_tied
  *
  * When storing a tied object (be it a tied scalar, array or hash), we lay out
@@ -2354,6 +2500,7 @@ out:
 static int store_tied(stcxt_t *cxt, SV *sv)
 {
        MAGIC *mg;
+       SV *obj = NULL;
        int ret = 0;
        int svt = SvTYPE(sv);
        char mtype = 'P';
@@ -2399,7 +2546,9 @@ static int store_tied(stcxt_t *cxt, SV *sv)
         * accesses on the retrieved object will indeed call the magic methods...
         */
 
-       if ((ret = store(cxt, mg->mg_obj)))             /* Extra () for -Wall, grr... */
+       /* [#17040] mg_obj is NULL for scalar self-ties. AMS 20030416 */
+       obj = mg->mg_obj ? mg->mg_obj : newSV(0);
+       if ((ret = store(cxt, obj)))
                return ret;
 
        TRACEME(("ok (tied)"));
@@ -2649,7 +2798,7 @@ static int store_hook(
         * If they returned more than one item, we need to serialize some
         * extra references if not already done.
         *
-        * Loop over the array, starting at postion #1, and for each item,
+        * Loop over the array, starting at position #1, and for each item,
         * ensure it is a reference, serialize it if not already done, and
         * replace the entry with the tag ID of the corresponding serialized
         * object.
@@ -3077,6 +3226,8 @@ static int sv_type(SV *sv)
                if (SvRMAGICAL(sv) && (mg_find(sv, 'P')))
                        return svis_TIED;
                return svis_HASH;
+       case SVt_PVCV:
+               return svis_CODE;
        default:
                break;
        }
@@ -3109,7 +3260,7 @@ static int store(stcxt_t *cxt, SV *sv)
         *
         * NOTA BENE, for 64-bit machines: the "*svh" below does not yield a
         * real pointer, rather a tag number (watch the insertion code below).
-        * That means it pobably safe to assume it is well under the 32-bit limit,
+        * That means it probably safe to assume it is well under the 32-bit limit,
         * and makes the truncation safe.
         *              -- RAM, 14/09/1999
         */
@@ -3249,7 +3400,7 @@ static int magic_write(stcxt_t *cxt)
         length -= sizeof (magicstr) - 1;
     }        
 
-    WRITE(header, length);
+    WRITE( (unsigned char*) header, length);
 
     if (!cxt->netorder) {
        TRACEME(("ok (magic_write byteorder = 0x%lx [%d], I%d L%d P%d D%d)",
@@ -3681,6 +3832,10 @@ static SV *retrieve_hook(stcxt_t *cxt, char *cname)
         * We don't need to remember the addresses returned by retrieval, because
         * all the references will be obtained through indirection via the object
         * tags in the object-ID list.
+        *
+        * We need to decrement the reference count for these objects
+        * because, if the user doesn't save a reference to them in the hook,
+        * they must be freed when this context is cleaned.
         */
 
        while (flags & SHF_NEED_RECURSE) {
@@ -3688,6 +3843,7 @@ static SV *retrieve_hook(stcxt_t *cxt, char *cname)
                rv = retrieve(cxt, 0);
                if (!rv)
                        return (SV *) 0;
+               SvREFCNT_dec(rv);
                TRACEME(("retrieve_hook back with rv=0x%"UVxf,
                         PTR2UV(rv)));
                GETMARK(flags);
@@ -4003,7 +4159,14 @@ static SV *retrieve_ref(stcxt_t *cxt, char *cname)
         * an SX_OBJECT indication, a ref count increment was done.
         */
 
-       sv_upgrade(rv, SVt_RV);
+       if (cname) {
+               /* Do not use sv_upgrade to preserve STASH */
+               SvFLAGS(rv) &= ~SVTYPEMASK;
+               SvFLAGS(rv) |= SVt_RV;
+       } else {
+               sv_upgrade(rv, SVt_RV);
+       }
+
        SvRV(rv) = sv;                          /* $rv = \$sv */
        SvROK_on(rv);
 
@@ -4047,10 +4210,11 @@ static SV *retrieve_overloaded(stcxt_t *cxt, char *cname)
        /*
         * Restore overloading magic.
         */
-
-       stash = (HV *) SvSTASH (sv);
-       if (!stash || !Gv_AMG(stash))
-               CROAK(("Cannot restore overloading on %s(0x%"UVxf") (package %s)",
+       if (!SvTYPE(sv)
+           || !(stash = (HV *) SvSTASH (sv))
+           || !Gv_AMG(stash))
+               CROAK(("Cannot restore overloading on %s(0x%"UVxf
+                      ") (package %s)",
                       sv_reftype(sv, FALSE),
                       PTR2UV(sv),
                           stash ? HvNAME(stash) : "<unknown>"));
@@ -4128,19 +4292,27 @@ static SV *retrieve_tied_hash(stcxt_t *cxt, char *cname)
 static SV *retrieve_tied_scalar(stcxt_t *cxt, char *cname)
 {
        SV *tv;
-       SV *sv;
+       SV *sv, *obj = NULL;
 
        TRACEME(("retrieve_tied_scalar (#%d)", cxt->tagnum));
 
        tv = NEWSV(10002, 0);
        SEEN(tv, cname);                        /* Will return if rv is null */
        sv = retrieve(cxt, 0);          /* Retrieve <object> */
-       if (!sv)
+       if (!sv) {
                return (SV *) 0;                /* Failed */
+       }
+       else if (SvTYPE(sv) != SVt_NULL) {
+               obj = sv;
+       }
 
        sv_upgrade(tv, SVt_PVMG);
-       sv_magic(tv, sv, 'q', Nullch, 0);
-       SvREFCNT_dec(sv);                       /* Undo refcnt inc from sv_magic() */
+       sv_magic(tv, obj, 'q', Nullch, 0);
+
+       if (obj) {
+               /* Undo refcnt inc from sv_magic() */
+               SvREFCNT_dec(obj);
+       }
 
        TRACEME(("ok (retrieve_tied_scalar at 0x%"UVxf")", PTR2UV(tv)));
 
@@ -4532,6 +4704,7 @@ static SV *retrieve_sv_no(stcxt_t *cxt, char *cname)
 
        TRACEME(("retrieve_sv_no"));
 
+       cxt->tagnum--; /* undo the tagnum increment in retrieve_l?scalar */
        SEEN(sv, cname);
        return sv;
 }
@@ -4749,7 +4922,7 @@ static SV *retrieve_flag_hash(stcxt_t *cxt, char *cname)
 
             if (flags & SHV_K_PLACEHOLDER) {
                 SvREFCNT_dec (sv);
-                sv = &PL_sv_undef;
+                sv = &PL_sv_placeholder;
                store_flags |= HVhek_PLACEHOLD;
            }
             if (flags & SHV_K_UTF8) {
@@ -4782,7 +4955,7 @@ static SV *retrieve_flag_hash(stcxt_t *cxt, char *cname)
              */
 
 #ifdef HAS_RESTRICTED_HASHES
-            if (hv_store_flags(hv, kbuf, size, sv, 0, flags) == 0)
+            if (hv_store_flags(hv, kbuf, size, sv, 0, store_flags) == 0)
                 return (SV *) 0;
 #else
             if (!(store_flags & HVhek_PLACEHOLD))
@@ -4802,6 +4975,122 @@ static SV *retrieve_flag_hash(stcxt_t *cxt, char *cname)
 }
 
 /*
+ * retrieve_code
+ *
+ * Return a code reference.
+ */
+static SV *retrieve_code(stcxt_t *cxt, char *cname)
+{
+#if PERL_VERSION < 6
+    CROAK(("retrieve_code does not work with perl 5.005 or less\n"));
+#else
+       dSP;
+       int type, count, tagnum;
+       SV *cv;
+       SV *sv, *text, *sub;
+
+       TRACEME(("retrieve_code (#%d)", cxt->tagnum));
+
+       /*
+        *  Insert dummy SV in the aseen array so that we don't screw
+        *  up the tag numbers.  We would just make the internal
+        *  scalar an untagged item in the stream, but
+        *  retrieve_scalar() calls SEEN().  So we just increase the
+        *  tag number.
+        */
+       tagnum = cxt->tagnum;
+       sv = newSViv(0);
+       SEEN(sv, cname);
+
+       /*
+        * Retrieve the source of the code reference
+        * as a small or large scalar
+        */
+
+       GETMARK(type);
+       switch (type) {
+       case SX_SCALAR:
+               text = retrieve_scalar(cxt, cname);
+               break;
+       case SX_LSCALAR:
+               text = retrieve_lscalar(cxt, cname);
+               break;
+       default:
+               CROAK(("Unexpected type %d in retrieve_code\n", type));
+       }
+
+       /*
+        * prepend "sub " to the source
+        */
+
+       sub = newSVpvn("sub ", 4);
+       sv_catpv(sub, SvPV_nolen(text)); /* XXX no sv_catsv! */
+       SvREFCNT_dec(text);
+
+       /*
+        * evaluate the source to a code reference and use the CV value
+        */
+
+       if (cxt->eval == NULL) {
+               cxt->eval = perl_get_sv("Storable::Eval", TRUE);
+               SvREFCNT_inc(cxt->eval);
+       }
+       if (!SvTRUE(cxt->eval)) {
+               if (
+                       cxt->forgive_me == 0 ||
+                       (cxt->forgive_me < 0 && !(cxt->forgive_me =
+                               SvTRUE(perl_get_sv("Storable::forgive_me", TRUE)) ? 1 : 0))
+               ) {
+                       CROAK(("Can't eval, please set $Storable::Eval to a true value"));
+               } else {
+                       sv = newSVsv(sub);
+                       /* fix up the dummy entry... */
+                       av_store(cxt->aseen, tagnum, SvREFCNT_inc(sv));
+                       return sv;
+               }
+       }
+
+       ENTER;
+       SAVETMPS;
+
+       if (SvROK(cxt->eval) && SvTYPE(SvRV(cxt->eval)) == SVt_PVCV) {
+               SV* errsv = get_sv("@", TRUE);
+               sv_setpv(errsv, "");                                    /* clear $@ */
+               PUSHMARK(sp);
+               XPUSHs(sv_2mortal(newSVsv(sub)));
+               PUTBACK;
+               count = call_sv(cxt->eval, G_SCALAR);
+               SPAGAIN;
+               if (count != 1)
+                       CROAK(("Unexpected return value from $Storable::Eval callback\n"));
+               cv = POPs;
+               if (SvTRUE(errsv)) {
+                       CROAK(("code %s caused an error: %s",
+                               SvPV_nolen(sub), SvPV_nolen(errsv)));
+               }
+               PUTBACK;
+       } else {
+               cv = eval_pv(SvPV_nolen(sub), TRUE);
+       }
+       if (cv && SvROK(cv) && SvTYPE(SvRV(cv)) == SVt_PVCV) {
+           sv = SvRV(cv);
+       } else {
+           CROAK(("code %s did not evaluate to a subroutine reference\n", SvPV_nolen(sub)));
+       }
+
+       SvREFCNT_inc(sv); /* XXX seems to be necessary */
+       SvREFCNT_dec(sub);
+
+       FREETMPS;
+       LEAVE;
+       /* fix up the dummy entry... */
+       av_store(cxt->aseen, tagnum, SvREFCNT_inc(sv));
+
+       return sv;
+#endif
+}
+
+/*
  * old_retrieve_array
  *
  * Retrieve a whole array in pre-0.6 binary format.
@@ -5125,7 +5414,7 @@ static SV *magic_check(stcxt_t *cxt)
 
     /* sizeof(char *) */
     if ((int) *current != sizeof(char *))
-        CROAK(("Pointer integer size is not compatible"));
+        CROAK(("Pointer size is not compatible"));
 
     if (use_NV_size) {
         /* sizeof(NV) */
@@ -5419,7 +5708,22 @@ static SV *do_retrieve(
 
        if (!sv) {
                TRACEME(("retrieve ERROR"));
+#if (PATCHLEVEL <= 4) 
+               /* perl 5.00405 seems to screw up at this point with an
+                  'attempt to modify a read only value' error reported in the
+                  eval { $self = pretrieve(*FILE) } in _retrieve.
+                  I can't see what the cause of this error is, but I suspect a
+                  bug in 5.004, as it seems to be capable of issuing spurious
+                  errors or core dumping with matches on $@. I'm not going to
+                  spend time on what could be a fruitless search for the cause,
+                  so here's a bodge. If you're running 5.004 and don't like
+                  this inefficiency, either upgrade to a newer perl, or you are
+                  welcome to find the problem and send in a patch.
+                */
+               return newSV(0);
+#else
                return &PL_sv_undef;            /* Something went wrong, return undef */
+#endif
        }
 
        TRACEME(("retrieve got %s(0x%"UVxf")",
@@ -5612,6 +5916,7 @@ PROTOTYPES: ENABLE
 
 BOOT:
     init_perinterp();
+    gv_fetchpv("Storable::drop_utf8",   GV_ADDMULTI, SVt_PV);
 #ifdef DEBUGME
     /* Only disable the used only once warning if we are in debugging mode.  */
     gv_fetchpv("Storable::DEBUGME",   GV_ADDMULTI, SVt_PV);
@@ -5620,6 +5925,9 @@ BOOT:
     gv_fetchpv("Storable::interwork_56_64bit",   GV_ADDMULTI, SVt_PV);
 #endif
 
+void
+init_perinterp()
+
 int
 pstore(f,obj)
 OutputStream   f