Merge branch '1.2.x' into 1.3.x
authorMark Wooding <mdw@distorted.org.uk>
Wed, 27 Nov 2019 15:11:08 +0000 (15:11 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 27 Nov 2019 15:11:08 +0000 (15:11 +0000)
* 1.2.x: (89 commits)
  t/: Add a test suite.
  ec.c: Don't lose error status when constructing points from a sequence.
  ec.c: Free partially constructed points coordinatewise.
  *.c: Be more careful about `PySequence_Size'.
  key.c: Reformat the rest of the `KeyError' constructor.
  key.c: Parse `KeyError' constructor arguments by hand.
  catacomb-python.h: Add a macro for raising `OverflowError'.
  key.c: Collect `KeyError' argument count as a separate step.
  key.c: Use tuple functions on `KeyError' argument tuple.
  key.c: Rename sad-path label to `end'.
  key.c: Delete duplicate setting of `errstring'.
  util.c (mkexc): Populate dictionary before constructing exception class.
  key.c: Only set the error code.
  catacomb.c, util.c: Publish negative constants correctly.
  field.c: Delete the completely unused `getfe' function.
  key.c (convfilter): Fix sense of error tests.
  buffer.c, ec.c: Fix required size for EC `buffer' encoding.
  algorithms.c: Fix `max' property name in docstrings.
  catacomb/__init__.py (_HashBase): Check that integers are within bounds.
  debian/rules: Build using the provided Makefile.
  ...

1  2 
catacomb-python.h
catacomb.c
util.c

diff --combined catacomb-python.h
@@@ -61,7 -61,6 +61,7 @@@
  #include <catacomb/blkc.h>
  
  #include <catacomb/gcipher.h>
 +#include <catacomb/gaead.h>
  #include <catacomb/ghash.h>
  #include <catacomb/gmac.h>
  #include <catacomb/md5.h>
    goto end;                                                           \
  } while (0)
  #define VALERR(str) EXCERR(PyExc_ValueError, str)
+ #define OVFERR(str) EXCERR(PyExc_OverflowError, str)
  #define TYERR(str) EXCERR(PyExc_TypeError, str)
 +#define IXERR(str) EXCERR(PyExc_IndexError, str)
  #define ZDIVERR(str) EXCERR(PyExc_ZeroDivisionError, str)
  #define SYSERR(str) EXCERR(PyExc_SystemError, str)
  #define NIERR(str) EXCERR(PyExc_NotImplementedError, str)
    PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);                        \
    goto end;                                                           \
  } while (0)
- #define PGENERR do { pgenerr(); goto end; } while (0)
+ #define PGENERR(exc) do { pgenerr(exc); goto end; } while (0)
  
  #define CONVFUNC(ty, cty, ext)                                                \
    int conv##ty(PyObject *o, void *p)                                  \
  } while (0)
  #define INITTYPE(ty, base) INITTYPE_META(ty, base, type)
  
- #define INSERT(name, ob) do {                                         \
+ extern PyObject *home_module;
+ #define INSERT(name, ob) do { \
    PyObject *_o = (PyObject *)(ob);                                    \
    Py_INCREF(_o);                                                      \
    PyModule_AddObject(mod, name, _o);                                  \
@@@ -233,9 -234,8 +236,10 @@@ MODULES(DO
      return (d);                                                               \
    }
  
- struct nameval { const char *name; unsigned long value; };
 +#define KWLIST (/*unconst*/ char **)kwlist
 +
+ struct nameval { const char *name; unsigned f; unsigned long value; };
+ #define CF_SIGNED 1u
  extern void setconstants(PyObject *, const struct nameval *);
  
  extern PyObject *mexp_common(PyObject *, PyObject *, size_t,
@@@ -259,7 -259,34 +263,34 @@@ extern PyObject *getulong(unsigned long
  extern PyObject *getk64(kludge64);
  extern void *newtype(PyTypeObject *, const PyTypeObject *, const char *);
  
+ struct excinfo { PyObject *ty, *val, *tb; };
+ #define EXCINFO_INIT { 0, 0, 0 }
  extern PyObject *mkexc(PyObject *, PyObject *, const char *, PyMethodDef *);
+ #define INIT_EXCINFO(exc) do {                                                \
+   struct excinfo *_exc = (exc); _exc->ty = _exc->val = _exc->tb = 0;  \
+ } while (0)
+ #define RELEASE_EXCINFO(exc) do {                                     \
+   struct excinfo *_exc = (exc);                                               \
+   Py_XDECREF(_exc->ty);        _exc->ty  = 0;                                 \
+   Py_XDECREF(_exc->val); _exc->val = 0;                                       \
+   Py_XDECREF(_exc->tb);        _exc->tb  = 0;                                 \
+ } while (0)
+ #define STASH_EXCINFO(exc) do {                                               \
+   struct excinfo *_exc = (exc);                                               \
+   PyErr_Fetch(&_exc->ty, &_exc->val, &_exc->tb);                      \
+   PyErr_NormalizeException(&_exc->ty, &_exc->val, &_exc->tb);         \
+ } while (0)
+ #define RESTORE_EXCINFO(exc) do {                                     \
+   struct excinfo *_exc = (exc);                                               \
+   PyErr_Restore(_exc->ty, _exc->val, _exc->tb);                               \
+   _exc->ty = _exc->val = _exc->tb = 0;                                        \
+ } while (0)
+ extern void report_lost_exception(struct excinfo *, const char *, ...);
+ extern void report_lost_exception_v(struct excinfo *, const char *, va_list);
+ extern void stash_exception(struct excinfo *, const char *, ...);
+ extern void restore_exception(struct excinfo *, const char *, ...);
  extern void typeready(PyTypeObject *);
  extern PyTypeObject *inittype(PyTypeObject *, PyTypeObject *);
  extern void addmethods(const PyMethodDef *);
@@@ -328,6 -355,7 +359,7 @@@ extern mp *getgf(PyObject *)
  extern int convgf(PyObject *, void *);
  extern PyObject *mp_pywrap(mp *);
  extern PyObject *gf_pywrap(mp *);
+ extern long mphash(mp *);
  extern mp *mp_frompyobject(PyObject *, int);
  extern PyObject *mp_topystring(mp *, int,
                               const char *, const char *, const char *);
@@@ -346,7 -374,6 +378,6 @@@ extern PyTypeObject *fe_pytype
  #define FE_FOBJ(o) ((PyObject *)(o)->ob_type)
  #define FE_X(o) (((fe_pyobj *)(o))->x)
  extern PyObject *fe_pywrap(PyObject *, mp *);
- extern mp *getfe(field *, PyObject *);
  
  typedef struct fe_pyobj {
    PyObject_HEAD
@@@ -504,130 -531,24 +535,130 @@@ typedef struct gccipher_pyobj 
  extern PyTypeObject *gccipher_pytype;
  #define GCCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gccipher_pytype)
  #define GCCIPHER_CC(o) (((gccipher_pyobj *)(o))->cc)
 -#define GCCIPHER_F(o) (((gccipher_pyobj *)(o))->f)
  extern PyObject *gccipher_pywrap(gccipher *);
  extern int convgccipher(PyObject *, void *);
 -extern int convgcipher(PyObject *, void *);
  
  typedef struct gcipher_pyobj {
    PyObject_HEAD
 -  unsigned f;
    gcipher *c;
  } gcipher_pyobj;
  
  extern PyTypeObject *gcipher_pytype;
  #define GCIPHER_PYCHECK(o) PyObject_TypeCheck((o), gcipher_pytype)
  #define GCIPHER_C(o) (((gcipher_pyobj *)(o))->c)
 -#define GCIPHER_F(o) (((gcipher_pyobj *)(o))->f)
 -extern PyObject *gcipher_pywrap(PyObject *, gcipher *, unsigned);
 +extern PyObject *gcipher_pywrap(PyObject *, gcipher *);
  extern int convgcipher(PyObject *, void *);
  
 +typedef struct gcaead_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead *aec;
 +  struct gcaeadaad_pyobj *aad;
 +  struct gcaeadenc_pyobj *enc;
 +  struct gcaeaddec_pyobj *dec;
 +} gcaead_pyobj;
 +
 +extern PyTypeObject *gcaead_pytype;
 +#define GCAEAD_PYCHECK(o) PyObject_TypeCheck((o), gcaead_pytype)
 +#define GCAEAD_AEC(o) (((gcaead_pyobj *)(o))->aec)
 +#define GCAEAD_AAD(o) (((gcaead_pyobj *)(o))->aad)
 +#define GCAEAD_ENC(o) (((gcaead_pyobj *)(o))->enc)
 +#define GCAEAD_DEC(o) (((gcaead_pyobj *)(o))->dec)
 +extern PyObject *gcaead_pywrap(gcaead *);
 +extern int convgcaead(PyObject *, void *);
 +
 +typedef struct gaeadkey_pyobj {
 +  PyObject_HEAD
 +  gaead_key *k;
 +} gaeadkey_pyobj;
 +
 +extern PyTypeObject *gaeadkey_pytype;
 +#define GAEADKEY_PYCHECK(o) PyObject_TypeCheck((o), gaeadkey_pytype)
 +#define GAEADKEY_K(o) (((gaeadkey_pyobj *)(o))->k)
 +extern PyObject *gaeadkey_pywrap(PyObject *, gaead_key *);
 +extern int convgaeadkey(PyObject *, void *);
 +
 +typedef struct gcaeadaad_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead_pyobj *key;
 +} gcaeadaad_pyobj;
 +#define GCAEADAAD_KEY(o) (((gcaeadaad_pyobj *)(o))->key)
 +extern PyTypeObject *gcaeadaad_pytype;
 +
 +typedef struct gaeadaad_pyobj {
 +  PyObject_HEAD
 +  gaead_aad *a;
 +  unsigned f;
 +#define AEADF_DEAD 32768u
 +  size_t hsz, hlen;
 +} gaeadaad_pyobj;
 +
 +extern PyTypeObject *gaeadaad_pytype;
 +#define GAEADAAD_PYCHECK(o) PyObject_TypeCheck((o), gaeadaad_pytype)
 +#define GAEADAAD_A(o) (((gaeadaad_pyobj *)(o))->a)
 +#define GAEADAAD_F(o) (((gaeadaad_pyobj *)(o))->f)
 +#define GAEADAAD_HSZ(o) (((gaeadaad_pyobj *)(o))->hsz)
 +#define GAEADAAD_HLEN(o) (((gaeadaad_pyobj *)(o))->hlen)
 +extern PyObject *gaeadaad_pywrap(PyObject *, gaead_aad *, unsigned, size_t);
 +extern int convgaeadaad(PyObject *, void *);
 +
 +typedef struct gcaeadenc_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead_pyobj *key;
 +} gcaeadenc_pyobj;
 +#define GCAEADENC_KEY(o) (((gcaeadenc_pyobj *)(o))->key)
 +extern PyTypeObject *gcaeadenc_pytype;
 +
 +typedef struct gaeadenc_pyobj {
 +  PyObject_HEAD
 +  gaead_enc *e;
 +  gaeadaad_pyobj *aad;
 +  unsigned f;
 +  size_t hsz, msz, tsz;
 +  size_t mlen;
 +} gaeadenc_pyobj;
 +
 +extern PyTypeObject *gaeadenc_pytype;
 +#define GAEADENC_PYCHECK(o) PyObject_TypeCheck((o), gaeadenc_pytype)
 +#define GAEADENC_AAD(o) (((gaeadenc_pyobj *)(o))->aad)
 +#define GAEADENC_E(o) (((gaeadenc_pyobj *)(o))->e)
 +#define GAEADENC_F(o) (((gaeadenc_pyobj *)(o))->f)
 +#define GAEADENC_HSZ(o) (((gaeadenc_pyobj *)(o))->hsz)
 +#define GAEADENC_MSZ(o) (((gaeadenc_pyobj *)(o))->msz)
 +#define GAEADENC_TSZ(o) (((gaeadenc_pyobj *)(o))->tsz)
 +#define GAEADENC_MLEN(o) (((gaeadenc_pyobj *)(o))->mlen)
 +extern PyObject *gaeadenc_pywrap(PyObject *, gaead_enc *, unsigned,
 +                               size_t, size_t, size_t);
 +extern int convgaeadenc(PyObject *, void *);
 +
 +typedef struct gcaeaddec_pyobj {
 +  PyHeapTypeObject ty;
 +  gcaead_pyobj *key;
 +} gcaeaddec_pyobj;
 +#define GCAEADDEC_KEY(o) (((gcaeaddec_pyobj *)(o))->key)
 +extern PyTypeObject *gcaeaddec_pytype;
 +
 +typedef struct gaeaddec_pyobj {
 +  PyObject_HEAD
 +  gaead_dec *d;
 +  gaeadaad_pyobj *aad;
 +  unsigned f;
 +  size_t hsz, csz, tsz;
 +  size_t clen;
 +} gaeaddec_pyobj;
 +
 +extern PyTypeObject *gaeaddec_pytype;
 +#define GAEADDEC_PYCHECK(o) PyObject_TypeCheck((o), gaeaddec_pytype)
 +#define GAEADDEC_AAD(o) (((gaeaddec_pyobj *)(o))->aad)
 +#define GAEADDEC_D(o) (((gaeaddec_pyobj *)(o))->d)
 +#define GAEADDEC_F(o) (((gaeaddec_pyobj *)(o))->f)
 +#define GAEADDEC_HSZ(o) (((gaeaddec_pyobj *)(o))->hsz)
 +#define GAEADDEC_CSZ(o) (((gaeaddec_pyobj *)(o))->csz)
 +#define GAEADDEC_TSZ(o) (((gaeaddec_pyobj *)(o))->tsz)
 +#define GAEADDEC_CLEN(o) (((gaeaddec_pyobj *)(o))->clen)
 +extern PyObject *gaeaddec_pywrap(PyObject *, gaead_dec *, unsigned,
 +                               size_t, size_t, size_t);
 +extern int convgaeaddec(PyObject *, void *);
 +
  typedef struct gchash_pyobj {
    PyHeapTypeObject ty;
    gchash *ch;
  extern PyTypeObject *gchash_pytype;
  #define GCHASH_PYCHECK(o) PyObject_TypeCheck((o), gchash_pytype)
  #define GCHASH_CH(o) (((gchash_pyobj *)(o))->ch)
 -#define GCHASH_F(o) (((gchash_pyobj *)(o))->f)
  extern PyObject *gchash_pywrap(gchash *);
  extern int convgchash(PyObject *, void *);
  
  typedef struct ghash_pyobj {
    PyObject_HEAD
 -  unsigned f;
    ghash *h;
  } ghash_pyobj;
  
@@@ -648,7 -571,8 +679,7 @@@ extern PyTypeObject *ghash_pytype, *gmh
  extern PyObject *sha_pyobj, *has160_pyobj;
  #define GHASH_PYCHECK(o) PyObject_TypeCheck((o), ghash_pytype)
  #define GHASH_H(o) (((ghash_pyobj *)(o))->h)
 -#define GHASH_F(o) (((ghash_pyobj *)(o))->f)
 -extern PyObject *ghash_pywrap(PyObject *, ghash *, unsigned);
 +extern PyObject *ghash_pywrap(PyObject *, ghash *);
  extern int convghash(PyObject *, void *);
  extern int convgmhash(PyObject *, void *);
  
@@@ -666,6 -590,7 +697,6 @@@ extern int convgcmac(PyObject *, void *
  
  typedef struct gmac_pyobj {
    PyHeapTypeObject ty;
 -  unsigned f;
    gmac *m;
  } gmac_pyobj;
  
@@@ -673,7 -598,7 +704,7 @@@ extern PyTypeObject *gmac_pytype
  #define GMAC_PYCHECK(o) PyObject_TypeCheck((o), gmac_pytype)
  #define GMAC_M(o) (((gmac_pyobj *)(o))->m)
  #define GMAC_F(o) (((gmac_pyobj *)(o))->f)
 -extern PyObject *gmac_pywrap(PyObject *, gmac *, unsigned);
 +extern PyObject *gmac_pywrap(PyObject *, gmac *);
  extern int convgmac(PyObject *, void *);
  
  /*----- Key generation ----------------------------------------------------*/
@@@ -700,9 -625,15 +731,15 @@@ extern PyTypeObject *pgev_pytype
  #define PGEV_PYCHECK(o) PyObject_TypeCheck(o, pgev_pytype)
  #define PGEV_PG(o) (&((pgev_pyobj *)(o))->pg)
  
+ typedef struct pypgev {
+   pgev ev;
+   PyObject *obj;
+   struct excinfo *exc;
+ } pypgev;
  extern int convpgev(PyObject *, void *);
- extern void droppgev(pgev *);
- extern void pgenerr(void);
+ extern void droppgev(pypgev *);
+ extern void pgenerr(struct excinfo *exc);
  
  /*----- That's all, folks -------------------------------------------------*/
  
diff --combined catacomb.c
  /*----- Main code ---------------------------------------------------------*/
  
  static const struct nameval consts[] = {
- #define C(x) { #x, x }
+ #define CF(f, x) { #x, f, x }
+ #define C(x) { #x, (x) >= 0 ? 0 : CF_SIGNED, x }
    C(FTY_PRIME), C(FTY_BINARY),
    C(PGEN_PASS), C(PGEN_FAIL), C(PGEN_BEGIN), C(PGEN_TRY), C(PGEN_DONE),
    C(PGEN_ABORT),
    C(MPW_MAX),
+   C(RAND_IBITS),
    C(PMODE_READ), C(PMODE_VERIFY),
    C(KOPEN_READ), C(KOPEN_WRITE), C(KOPEN_NOFILE),
-   C(KEXP_FOREVER), C(KEXP_EXPIRE),
+   CF(0, KEXP_FOREVER), CF(0, KEXP_EXPIRE),
    C(KF_ENCMASK), C(KENC_BINARY), C(KENC_MP), C(KENC_STRUCT),
      C(KENC_ENCRYPT), C(KENC_STRING), C(KENC_EC),
    C(KF_CATMASK), C(KCAT_SYMM), C(KCAT_PRIV), C(KCAT_PUB), C(KCAT_SHARE),
    C(ED25519_KEYSZ), C(ED25519_PUBSZ), C(ED25519_SIGSZ),
      C(ED25519_MAXPERSOSZ),
    C(ED448_KEYSZ), C(ED448_PUBSZ), C(ED448_SIGSZ), C(ED448_MAXPERSOSZ),
 +  C(AEADF_PCHSZ), C(AEADF_PCMSZ), C(AEADF_PCTSZ),
 +  C(AEADF_AADNDEP), C(AEADF_AADFIRST), C(AEADF_NOAAD),
  #define ENTRY(tag, val, str) C(KERR_##tag),
    KEY_ERRORS(ENTRY)
  #undef ENTRY
  #undef C
+ #undef CF
    { 0 }
  };
  
@@@ -75,7 -76,8 +78,8 @@@ PyObject *mexp_common(PyObject *me, PyO
      arg = PyTuple_GetItem(arg, 0);
    Py_INCREF(arg);
    if (!PySequence_Check(arg)) TYERR("not a sequence");
-   n = PySequence_Size(arg); if (!n) { z = id(me); goto end; }
+   n = PySequence_Size(arg); if (n < 0) goto end;
+   if (!n) { z = id(me); goto end; }
    x = PySequence_GetItem(arg, 0);
    if (PySequence_Check(x))
      flat = 0;
diff --combined util.c
--- 1/util.c
--- 2/util.c
+++ b/util.c
  
  #include "catacomb-python.h"
  
+ /* #undef HAVE_LONG_LONG */
  /*----- External values ---------------------------------------------------*/
  
  static PyObject *modname = 0;
+ PyObject *home_module = 0;
  
  /*----- Conversions -------------------------------------------------------*/
  
@@@ -42,12 -45,17 +45,17 @@@ PyObject *getulong(unsigned long w
      return (PyLong_FromUnsignedLong(w));
  }
  
+ #ifndef HAVE_LONG_LONG
  static PyObject *i32 = 0;
  static int init_i32(void)
    { if (!i32 && (i32 = PyInt_FromLong(32)) == 0) return (-1); return (0); }
+ #endif
  
  PyObject *getk64(kludge64 u)
  {
+ #ifdef HAVE_LONG_LONG
+   return (PyLong_FromUnsignedLongLong(GET64(unsigned PY_LONG_LONG, u)));
+ #else
    PyObject *i = 0, *j = 0, *t;
    PyObject *rc = 0;
  
@@@ -63,6 -71,7 +71,7 @@@ end
    if (i) Py_DECREF(i);
    if (j) Py_DECREF(j);
    return (rc);
+ #endif
  }
  
  PyObject *getbool(int b)
@@@ -99,15 -108,32 +108,32 @@@ end
    return (0);
  }
  
+ #ifdef HAVE_UINT64
+ #  define CONVu64(n) do {                                             \
+      kludge64 k;                                                      \
+      uint64 t;                                                                \
+      if (!convk64(o, &k)) goto end;                                   \
+      t = GET64(uint64, k);                                            \
+      if (t > MASK##n) VALERR("out of range");                         \
+      *p = t;                                                          \
+    } while (0)
+ #else
+ #  define CONVu64(n) assert(!"shouldn't be possible")
+ #endif
  #define CONVU_(n)                                                     \
    int convu##n(PyObject *o, void *pp)                                 \
    {                                                                   \
      unsigned long u;                                                  \
      uint##n *p = pp;                                                  \
                                                                        \
-     if (!convulong(o, &u)) goto end;                                  \
-     if (u > MASK##n) VALERR("out of range");                          \
-     *p = u;                                                           \
+     if (MASK##n > ULONG_MAX)                                          \
+       CONVu64(n);                                                     \
+     else {                                                            \
+       if (!convulong(o, &u)) goto end;                                        \
+       if (u > MASK##n) VALERR("out of range");                                \
+       *p = u;                                                         \
+     }                                                                 \
      return (1);                                                               \
    end:                                                                        \
      return (0);                                                               \
@@@ -129,11 -155,22 +155,22 @@@ end
  
  int convk64(PyObject *o, void *pp)
  {
-   PyObject *i = 0, *t;
+   PyObject *i = 0;
    int rc = 0;
+ #if HAVE_LONG_LONG
+   unsigned PY_LONG_LONG t;
+ #else
+   PyObject *t;
    uint32 lo, hi;
+ #endif
  
    if (!o) VALERR("can't delete");
+ #if HAVE_LONG_LONG
+   if ((i = PyNumber_Long(o)) == 0) goto end;
+   t = PyLong_AsUnsignedLongLong(i);
+   if (t == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) goto end;
+   ASSIGN64(*(kludge64 *)pp, t);
+ #else
    if (init_i32()) goto end;
    if ((i = PyNumber_Int(o)) == 0) goto end;
    lo = PyInt_AsUnsignedLongMask(i);
    Py_DECREF(i); i = t;
    if (PyObject_IsTrue(i)) VALERR("out of range");
    SET64(*(kludge64 *)pp, hi, lo);
+ #endif
    rc = 1;
  end:
    if (i) Py_DECREF(i);
    return (rc);
@@@ -241,14 -280,14 +280,14 @@@ PyTypeObject *inittype(PyTypeObject *ty
  void setconstants(PyObject *mod, const struct nameval *c)
  {
    PyObject *x;
+   unsigned long u;
  
    while (c->name) {
-     if (c->value > LONG_MAX)
-       x = PyLong_FromUnsignedLong(c->value);
-     else
-       x = PyInt_FromLong(c->value);
-     PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x);
-     c++;
+     u = c->value;
+     if (u <= LONG_MAX) x = PyInt_FromLong(u);
+     else if (c->f&CF_SIGNED) x = PyInt_FromLong(-1 - (long)(ULONG_MAX - u));
+     else x = PyLong_FromUnsignedLong(u);
+     PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x); c++;
    }
  }
  
@@@ -285,13 -324,7 +324,7 @@@ PyObject *mkexc(PyObject *mod, PyObjec
    PyObject *func = 0;
    PyObject *meth = 0;
  
-   if ((nameobj = PyString_FromFormat("%s.%s",
-                                    PyModule_GetName(mod),
-                                    name)) == 0 ||
-       (dict = PyDict_New()) == 0 ||
-       (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
-                               base, dict)) == 0)
-     goto fail;
+   if ((dict = PyDict_New()) == 0) goto fail;
  
    if (mm) {
      while (mm->ml_name) {
      }
    }
  
+   if ((nameobj = PyString_FromFormat("%s.%s",
+                                    PyModule_GetName(mod),
+                                    name)) == 0 ||
+       (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
+                               base, dict)) == 0)
+     goto fail;
  done:
    Py_XDECREF(nameobj);
    Py_XDECREF(dict);
@@@ -318,6 -358,101 +358,101 @@@ fail
    goto done;
  }
  
+ void report_lost_exception_v(struct excinfo *exc,
+                            const char *why, va_list ap)
+ {
+   PyObject *hookfn = 0;
+   PyObject *whyobj = 0;
+   PyObject *obj = 0;
+   /* Make sure we start out without a pending exception, or this will get
+    * really confusing.
+    */
+   assert(!PyErr_Occurred());
+   /* Format the explanation. */
+   if (why) whyobj = PyString_FromFormatV(why, ap);
+   else { whyobj = Py_None; Py_INCREF(whyobj); }
+   /* Find our home module's `lostexchook' function.  This won't work if
+    * there's no module, or the function isn't defined, or it's `None'.
+    */
+   if (!home_module) goto sys;
+   hookfn = PyObject_GetAttrString(home_module, "lostexchook");
+   if (hookfn == Py_None) goto sys;
+   else if (hookfn) ;
+   else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto ouch;
+   else { PyErr_Clear(); goto sys; }
+   /* Call the hook function. */
+   obj = PyObject_CallFunction(hookfn, "(OOOO)",
+                             whyobj, exc->ty, exc->val, exc->tb);
+   if (!obj) goto ouch;
+   goto end;
+   /* Something went wrong reporting the problem. */
+ ouch:
+   PySys_WriteStderr("\n!!! FAILURE REPORTING LOST EXCEPTION\n");
+   PyErr_Print();
+   /* drop through... */
+   /* There was no hook, so try to do something sensible using
+    * `sys.excepthook'.
+    */
+ sys:
+   PySys_WriteStderr("\n!!! LOST EXCEPTION: %s\n",
+                   PyString_AS_STRING(whyobj));
+   RESTORE_EXCINFO(exc);
+   PyErr_Print();
+   /* drop through... */
+   /* Clean up afterwards. */
+ end:
+   Py_XDECREF(hookfn);
+   Py_XDECREF(whyobj);
+   Py_XDECREF(obj);
+ }
+ void report_lost_exception(struct excinfo *exc, const char *why, ...)
+ {
+   va_list ap;
+   va_start(ap, why);
+   report_lost_exception_v(exc, why, ap);
+   va_end(ap);
+ }
+ void stash_exception(struct excinfo *exc, const char *why, ...)
+ {
+   va_list ap;
+   struct excinfo stash;
+   if (!exc->ty)
+     STASH_EXCINFO(exc);
+   else {
+     va_start(ap, why);
+     STASH_EXCINFO(&stash);
+     report_lost_exception_v(&stash, why, ap);
+     va_end(ap);
+   }
+ }
+ void restore_exception(struct excinfo *exc, const char *why, ...)
+ {
+   va_list ap;
+   struct excinfo stash;
+   if (!PyErr_Occurred())
+     RESTORE_EXCINFO(exc);
+   else {
+     va_start(ap, why);
+     STASH_EXCINFO(&stash);
+     report_lost_exception_v(exc, why, ap);
+     RESTORE_EXCINFO(&stash);
+     va_end(ap);
+   }
+ }
  /*----- Generic dictionary methods ----------------------------------------*/
  
  static PyTypeObject *itemiter_pytype, *valiter_pytype;
@@@ -369,7 -504,7 +504,7 @@@ static PyTypeObject itemiter_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
- "Iterates over the items of a mapping.",
+ "Iterates over the keys of a mapping.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -427,7 -562,7 +562,7 @@@ static PyTypeObject valiter_pytype_ske
      Py_TPFLAGS_BASETYPE,
  
    /* @tp_doc@ */
- "Iterates over the items of a mapping.",
+ "Iterates over the values of a mapping.",
  
    0,                                  /* @tp_traverse@ */
    0,                                  /* @tp_clear@ */
@@@ -466,8 -601,7 +601,7 @@@ PySequenceMethods gmap_pysequence = 
  Py_ssize_t gmap_pysize(PyObject *me)
  {
    PyObject *i = 0, *x = 0;
-   int rc = -1;
-   int n = 0;
+   Py_ssize_t rc = -1, n = 0;
  
    if ((i = PyObject_GetIter(me)) == 0) goto done;
    while ((x = PyIter_Next(i)) != 0) { n++; Py_DECREF(x); x = 0; }
@@@ -603,15 -737,13 +737,15 @@@ end
    return (rc);
  }
  
 -static char *def_kwlist[] = { "key", "default", 0 };
 +static const char *const def_kwlist[] = { "key", "default", 0 };
  
  PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
  {
    PyObject *k, *def = Py_None, *v;
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:get",
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", def_kwlist, &k, &def))
++  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get",
 +                                 (/*unconst*/ char **)def_kwlist,
 +                                 &k, &def))
      return (0);
    if ((v = PyObject_GetItem(me, k)) != 0) return (v);
    PyErr_Clear();
@@@ -622,9 -754,8 +756,9 @@@ PyObject *gmapmeth_setdefault(PyObject 
  {
    PyObject *k, *def = Py_None, *v;
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:setdefault",
+   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
 -                                 def_kwlist, &k, &def))
 +                                 (/*unconst*/ char **)def_kwlist,
 +                                 &k, &def))
      return (0);
    if ((v = PyObject_GetItem(me, k)) != 0) return (v);
    PyErr_Clear();
@@@ -636,16 -767,16 +770,18 @@@ PyObject *gmapmeth_pop(PyObject *me, Py
  {
    PyObject *k, *def = 0, *v;
  
-   if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:pop",
 -  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", def_kwlist, &k, &def))
++  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop",
 +                                 (/*unconst*/ char **)def_kwlist,
 +                                 &k, &def))
      return (0);
    if ((v = PyObject_GetItem(me, k)) != 0) {
      PyObject_DelItem(me, k);
      return (v);
-   }
-   PyErr_Clear();
-   RETURN_OBJ(def);
+   } else if (def) {
+     PyErr_Clear();
+     RETURN_OBJ(def);
+   } else
+     return (0);
  }
  
  PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
@@@ -675,7 -806,7 +811,7 @@@ PyObject *gmapmeth_popitem(PyObject *me
    PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
  
    if (!PyArg_ParseTuple(arg, ":popitem") ||
-       (i = PyObject_GetIter(me)))
+       (i = PyObject_GetIter(me)) == 0)
      goto end;
    if ((k = PyIter_Next(i)) == 0) {
      if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
@@@ -697,11 -828,29 +833,29 @@@ PyMethodDef gmap_pymethods[] = 
  
  /*----- Initialization ----------------------------------------------------*/
  
+ static PyObject *meth__set_home_module(PyObject *me, PyObject *arg)
+ {
+   PyObject *mod;
+   if (!PyArg_ParseTuple(arg, "O!:_set_home_module", &PyModule_Type, &mod))
+     return (0);
+   Py_XDECREF(home_module); home_module = mod; Py_INCREF(home_module);
+   RETURN_NONE;
+ }
+ static const PyMethodDef methods[] = {
+ #define METHNAME(func) meth_##func
+   METH        (_set_home_module,      "_set_home_module(MOD)")
+ #undef METHNAME
+   { 0 }
+ };
  void util_pyinit(void)
  {
    modname = PyString_FromString("catacomb");
    INITTYPE(itemiter, root);
    INITTYPE(valiter, root);
+   addmethods(methods);
  }
  
  void util_pyinsert(PyObject *mod)