pyke/, ...: Extract utilities into a sort-of reusable library.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 20 Oct 2019 16:51:13 +0000 (17:51 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Fri, 10 Apr 2020 22:04:15 +0000 (23:04 +0100)
This commit changes no code, but it moves a lot of it about.  Tidying up
will come later.

.skelrc [new file with mode: 0644]
catacomb-python.h [deleted file]
catacomb.c [deleted file]
mapping.c [new file with mode: 0644]
pyke-mLib.c [new file with mode: 0644]
pyke-mLib.h [new file with mode: 0644]
pyke.c [new file with mode: 0644]
pyke.h [new file with mode: 0644]
util.c [deleted file]

diff --git a/.skelrc b/.skelrc
new file mode 100644 (file)
index 0000000..21912db
--- /dev/null
+++ b/.skelrc
@@ -0,0 +1,9 @@
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+      (append
+       '((author . "Straylight/Edgeware")
+        (licence-text . "[[gpl]]")
+        (full-title . "Pyke: the Python Kit for Extensions")
+        (program . "Pyke"))
+       skel-alist))
diff --git a/catacomb-python.h b/catacomb-python.h
deleted file mode 100644 (file)
index 5c5611e..0000000
+++ /dev/null
@@ -1,564 +0,0 @@
-/* -*-c-*-
- *
- * Definitions for Catacomb bindings
- *
- * (c) 2004 Straylight/Edgeware
- */
-
-/*----- Licensing notice --------------------------------------------------*
- *
- * This file is part of the Python interface to Catacomb.
- *
- * Catacomb/Python is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Catacomb/Python is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Catacomb/Python; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef CATACOMB_PYTHON_H
-#define CATACOMB_PYTHON_H
-
-#ifdef __cplusplus
-  extern "C" {
-#endif
-
-/*----- Header files ------------------------------------------------------*/
-
-#define PY_SSIZE_T_CLEAN
-
-#include <Python.h>
-#include <longintrepr.h>
-#include <structmember.h>
-
-#include <mLib/dstr.h>
-#include <mLib/macros.h>
-#include <mLib/quis.h>
-#include <mLib/unihash.h>
-
-#include <catacomb/buf.h>
-#include <catacomb/ct.h>
-
-#include <catacomb/grand.h>
-#include <catacomb/rand.h>
-#include <catacomb/noise.h>
-#include <catacomb/bbs.h>
-#include <catacomb/mprand.h>
-#include <catacomb/lcrand.h>
-#include <catacomb/fibrand.h>
-#include <catacomb/dsarand.h>
-#include <catacomb/sslprf.h>
-#include <catacomb/tlsprf.h>
-#include <catacomb/blkc.h>
-
-#include <catacomb/gcipher.h>
-#include <catacomb/gaead.h>
-#include <catacomb/ghash.h>
-#include <catacomb/gmac.h>
-#include <catacomb/md5.h>
-#include <catacomb/md5-hmac.h>
-#include <catacomb/poly1305.h>
-#include <catacomb/sha.h>
-#include <catacomb/sha-mgf.h>
-#include <catacomb/sha-hmac.h>
-#include <catacomb/keccak1600.h>
-#include <catacomb/sha3.h>
-
-#include <catacomb/mp.h>
-#include <catacomb/mpint.h>
-#include <catacomb/mpmul.h>
-#include <catacomb/mpcrt.h>
-#include <catacomb/mpmont.h>
-#include <catacomb/mpbarrett.h>
-#include <catacomb/mpreduce.h>
-#include <catacomb/mp-fibonacci.h>
-
-#include <catacomb/pgen.h>
-#include <catacomb/pfilt.h>
-#include <catacomb/strongprime.h>
-#include <catacomb/limlee.h>
-#include <catacomb/dh.h>
-#include <catacomb/ptab.h>
-#include <catacomb/bintab.h>
-#include <catacomb/dsa.h>
-#include <catacomb/x25519.h>
-#include <catacomb/x448.h>
-#include <catacomb/ed25519.h>
-#include <catacomb/ed448.h>
-
-#include <catacomb/gf.h>
-#include <catacomb/gfreduce.h>
-#include <catacomb/gfn.h>
-
-#include <catacomb/field.h>
-#include <catacomb/field-guts.h>
-
-#include <catacomb/ec.h>
-#include <catacomb/ec-raw.h>
-#include <catacomb/ectab.h>
-
-#include <catacomb/group.h>
-#include <catacomb/group-guts.h>
-
-#include <catacomb/gdsa.h>
-#include <catacomb/gkcdsa.h>
-#include <catacomb/rsa.h>
-
-#include <catacomb/key.h>
-#include <catacomb/passphrase.h>
-#include <catacomb/pixie.h>
-
-#include <catacomb/share.h>
-#include <catacomb/gfshare.h>
-
-/*----- Other preliminaries -----------------------------------------------*/
-
-#define GOBBLE_SEMI extern int notexist
-#if defined(__GNUC__) && defined(__ELF__)
-#  define PRIVATE_SYMBOLS _Pragma("GCC visibility push(hidden)") GOBBLE_SEMI
-#  define PUBLIC_SYMBOLS _Pragma("GCC visibility pop") GOBBLE_SEMI
-#  define EXPORT __attribute__((__visibility__("default")))
-#else
-#  define PRIVATE_SYMBOLS GOBBLE_SEMI
-#  define PUBLIC_SYMBOLS GOBBLE_SEMI
-#  define EXPORT
-#endif
-
-PRIVATE_SYMBOLS;
-
-/*----- Utility macros ----------------------------------------------------*/
-
-#define RETURN_OBJ(obj) do { Py_INCREF(obj); return (obj); } while (0)
-#define RETURN_NONE RETURN_OBJ(Py_None)
-#define RETURN_NOTIMPL RETURN_OBJ(Py_NotImplemented)
-#define RETURN_TRUE RETURN_OBJ(Py_True)
-#define RETURN_FALSE RETURN_OBJ(Py_False)
-#define RETURN_ME RETURN_OBJ(me)
-
-#define EXCERR(exc, str) do {                                          \
-  PyErr_SetString(exc, str);                                           \
-  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)
-#define INDEXERR(idx) do {                                             \
-  PyErr_SetObject(PyExc_KeyError, idx);                                        \
-  goto end;                                                            \
-} while (0)
-#define OSERR(name) do {                                               \
-  PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);                 \
-  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)                                   \
-  {                                                                    \
-    if (!PyObject_TypeCheck(o, ty##_pytype))                           \
-      TYERR("wanted a " #ty);                                          \
-    *(cty *)p = ext(o);                                                        \
-    return (1);                                                                \
-  end:                                                                 \
-    return (0);                                                                \
-  }
-
-#define root_pytype 0
-#define type_pytype &PyType_Type
-#define INITTYPE_META(ty, base, meta) do {                             \
-  ty##_pytype_skel.tp_base = base##_pytype;                            \
-  ty##_pytype = inittype(&ty##_pytype_skel, meta##_pytype);            \
-} while (0)
-#define INITTYPE(ty, base) INITTYPE_META(ty, base, type)
-
-extern PyObject *home_module;
-extern PyObject *modname;
-
-#define INSERT(name, ob) do {  \
-  PyObject *_o = (PyObject *)(ob);                                     \
-  Py_INCREF(_o);                                                       \
-  PyModule_AddObject(mod, name, _o);                                   \
-} while (0)
-
-#define INSEXC(name, var, base, meth)                                  \
-  INSERT(name, var = mkexc(mod, base, name, meth))
-
-#define METH(func, doc)                                                        \
-  { #func, METHNAME(func), METH_VARARGS, doc },
-#define KWMETH(func, doc)                                              \
-  { #func, (PyCFunction)METHNAME(func),                                        \
-    METH_VARARGS | METH_KEYWORDS, doc },
-
-#define GET(func, doc)                                                 \
-  { #func, GETSETNAME(get, func), 0, doc },
-#define GETSET(func, doc)                                              \
-  { #func, GETSETNAME(get, func), GETSETNAME(set, func), doc },
-
-#define MEMBER(name, ty, f, doc)                                       \
-  { #name, ty, offsetof(MEMBERSTRUCT, name), f, doc },
-
-#define MODULES(_)                                                     \
-  _(util)                                                              \
-  _(bytestring) _(buffer)                                              \
-  _(rand) _(algorithms) _(pubkey) _(pgen)                              \
-  _(mp) _(field) _(ec) _(group)                                                \
-  _(passphrase) _(share) _(key)
-#define DOMODINIT(m) m##_pyinit();
-#define DOMODINSERT(m) m##_pyinsert(mod);
-#define INIT_MODULES do { MODULES(DOMODINIT) } while (0)
-#define INSERT_MODULES do { MODULES(DOMODINSERT) } while (0)
-#define DECLARE_MODINIT(m)                                             \
-  extern void m##_pyinit(void);                                                \
-  extern void m##_pyinsert(PyObject *);
-
-MODULES(DECLARE_MODINIT)
-
-#define FREEOBJ(obj)                                                   \
-  (((PyObject *)(obj))->ob_type->tp_free((PyObject *)(obj)))
-
-#define GEN(func, base)                                                        \
-  static PyObject *func(void)                                          \
-  {                                                                    \
-    PyObject *d = PyDict_New();                                                \
-    PyObject *o;                                                       \
-    int i;                                                             \
-                                                                       \
-    for (i = 0; g##base##tab[i]; i++) {                                        \
-      o = gc##base##_pywrap((/*unconst*/ gc##base *)g##base##tab[i]);  \
-      PyDict_SetItemString(d,                                          \
-                          (/*unconst*/ char *)g##base##tab[i]->name,   \
-                          o);                                          \
-      Py_DECREF(o);                                                    \
-    }                                                                  \
-    return (d);                                                                \
-  }
-
-#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,
-                            PyObject *(*id)(PyObject *),
-                            int (*fill)(void *, PyObject *,
-                                        PyObject *, PyObject *),
-                            PyObject *(*exp)(PyObject *, void *, int),
-                            void (*drop)(void *));
-
-extern int convulong(PyObject *, void *);
-#define DECL_CONVU_(n) extern int convu##n(PyObject *, void *);
-DOUINTSZ(DECL_CONVU_)
-extern int convmpw(PyObject *, void *);
-extern int convuint(PyObject *, void *);
-extern int convk64(PyObject *, void *);
-extern int convszt(PyObject *, void *);
-extern int convbool(PyObject *, void *);
-extern PyObject *abstract_pynew(PyTypeObject *, PyObject *, PyObject *);
-extern PyObject *getbool(int);
-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 *);
-extern PyMethodDef *donemethods(void);
-
-/*----- Mapping methods ---------------------------------------------------*/
-
-#define GMAP_METH(func, doc) { #func, gmapmeth_##func, METH_VARARGS, doc },
-#define GMAP_KWMETH(func, doc)                                         \
-  { #func, (PyCFunction)gmapmeth_##func, METH_VARARGS|METH_KEYWORDS, doc },
-#define GMAP_METHDECL(func, doc)                                       \
-  extern PyObject *gmapmeth_##func(PyObject *, PyObject *);
-#define GMAP_KWMETHDECL(func, doc)                                     \
-  extern PyObject *gmapmeth_##func(PyObject *, PyObject *, PyObject *);
-
-#define GMAP_DOROMETHODS(METH, KWMETH)                                 \
-  METH (has_key,       "D.has_key(KEY) -> BOOL")                       \
-  METH (keys,          "D.keys() -> LIST")                             \
-  METH (values,        "D.values() -> LIST")                           \
-  METH (items,         "D.items() -> LIST")                            \
-  METH (iterkeys,      "D.iterkeys() -> ITER")                         \
-  METH (itervalues,    "D.itervalues() -> ITER")                       \
-  METH (iteritems,     "D.iteritems() -> ITER")                        \
-  KWMETH(get,          "D.get(KEY, [default = None]) -> VALUE")
-
-#define GMAP_DOMETHODS(METH, KWMETH)                                   \
-  GMAP_DOROMETHODS(METH, KWMETH)                                       \
-  METH (clear,         "D.clear()")                                    \
-  KWMETH(setdefault,   "D.setdefault(K, [default = None]) -> VALUE")   \
-  KWMETH(pop,          "D.pop(KEY, [default = <error>]) -> VALUE")     \
-  METH (popitem,       "D.popitem() -> (KEY, VALUE)")                  \
-  METH (update,        "D.update(MAP)")
-
-GMAP_DOMETHODS(GMAP_METHDECL, GMAP_KWMETHDECL)
-#define GMAP_ROMETHODS GMAP_DOROMETHODS(GMAP_METH, GMAP_KWMETH)
-#define GMAP_METHODS GMAP_DOMETHODS(GMAP_METH, GMAP_KWMETH)
-extern Py_ssize_t gmap_pysize(PyObject *);
-extern PySequenceMethods gmap_pysequence;
-extern PyMethodDef gmap_pymethods[];
-
-/*----- Bytestrings -------------------------------------------------------*/
-
-PyObject *bytestring_pywrap(const void *, size_t);
-PyObject *bytestring_pywrapbuf(buf *);
-
-/*----- Multiprecision arithmetic -----------------------------------------*/
-
-typedef struct mp_pyobj {
-  PyObject_HEAD
-  mp *x;
-} mp_pyobj;
-
-extern PyTypeObject *mp_pytype;
-extern PyTypeObject *gf_pytype;
-#define MP_X(o) (((mp_pyobj *)(o))->x)
-#define MP_PYCHECK(o) PyObject_TypeCheck((o), mp_pytype)
-#define GF_PYCHECK(o) PyObject_TypeCheck((o), gf_pytype)
-
-extern mp *mp_frompylong(PyObject *);
-extern PyObject *mp_topylong(mp *);
-extern mp *tomp(PyObject *);
-extern mp *getmp(PyObject *);
-extern int convmp(PyObject *, void *);
-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 *);
-extern int mp_tolong_checked(mp *, long *, int);
-
-/*----- Abstract fields ---------------------------------------------------*/
-
-typedef struct field_pyobj {
-  PyHeapTypeObject ty;
-  field *f;
-} field_pyobj;
-
-extern PyTypeObject *field_pytype;
-extern PyTypeObject *primefield_pytype;
-extern PyTypeObject *niceprimefield_pytype;
-extern PyTypeObject *binfield_pytype;
-extern PyTypeObject *binpolyfield_pytype;
-extern PyTypeObject *binnormfield_pytype;
-#define FIELD_PYCHECK(o) PyObject_TypeCheck((o), field_pytype)
-#define FIELD_F(o) (((field_pyobj *)(o))->f)
-extern PyObject *field_pywrap(field *);
-extern field *field_copy(field *);
-
-typedef struct fe_pyobj {
-  PyObject_HEAD
-  field *f;
-  mp *x;
-} fe_pyobj;
-
-extern PyTypeObject *fe_pytype;
-#define FE_PYCHECK(o) PyObject_TypeCheck((o), fe_pytype)
-#define FE_F(o) (((fe_pyobj *)(o))->f)
-#define FE_FOBJ(o) ((PyObject *)(o)->ob_type)
-#define FE_X(o) (((fe_pyobj *)(o))->x)
-extern PyObject *fe_pywrap(PyObject *, mp *);
-
-/*----- Elliptic curves ---------------------------------------------------*/
-
-typedef struct eccurve_pyobj {
-  PyHeapTypeObject ty;
-  ec_curve *c;
-  PyObject *fobj;
-} eccurve_pyobj;
-
-extern PyTypeObject *eccurve_pytype;
-extern PyTypeObject *ecprimecurve_pytype;
-extern PyTypeObject *ecprimeprojcurve_pytype;
-extern PyTypeObject *ecbincurve_pytype;
-extern PyTypeObject *ecbinprojcurve_pytype;
-#define ECCURVE_PYCHECK(o) PyObject_TypeCheck((o), eccurve_pytype)
-#define ECCURVE_C(o) (((eccurve_pyobj *)(o))->c)
-#define ECCURVE_FOBJ(o) (((eccurve_pyobj *)(o))->fobj)
-extern PyObject *eccurve_pywrap(PyObject *, ec_curve *);
-extern ec_curve *eccurve_copy(ec_curve *);
-
-typedef struct ecpt_pyobj {
-  PyObject_HEAD
-  ec_curve *c;
-  ec p;
-} ecpt_pyobj;
-
-extern PyTypeObject *ecpt_pytype, *ecptcurve_pytype;
-#define ECPT_PYCHECK(o) PyObject_TypeCheck((o), ecpt_pytype)
-#define ECPTCURVE_PYCHECK(o) PyObject_TypeCheck((o), ecptcurve_pytype)
-#define ECPT_C(o) (((ecpt_pyobj *)(o))->c)
-#define ECPT_COBJ(o) ((PyObject *)(o)->ob_type)
-#define ECPT_FOBJ(o) ECCURVE_FOBJ(ECPT_COBJ((o)))
-#define ECPT_P(o) (&((ecpt_pyobj *)(o))->p)
-extern PyObject *ecpt_pywrap(PyObject *, ec *);
-extern PyObject *ecpt_pywrapout(void *, ec *);
-extern int toecpt(ec_curve *, ec *, PyObject *);
-extern int getecpt(ec_curve *, ec *, PyObject *);
-extern void getecptout(ec *, PyObject *);
-extern int convecpt(PyObject *, void *);
-
-typedef struct ecinfo_pyobj {
-  PyObject_HEAD
-  ec_info ei;
-  PyObject *cobj;
-} ecinfo_pyobj;
-
-extern PyTypeObject *ecinfo_pytype;
-#define ECINFO_PYCHECK(o) PyObject_TypeCheck((o), ecinfo_pytype)
-#define ECINFO_EI(o) (&((ecinfo_pyobj *)(o))->ei)
-#define ECINFO_COBJ(o) (((ecinfo_pyobj *)(o))->cobj)
-extern void ecinfo_copy(ec_info *, const ec_info *);
-extern PyObject *ecinfo_pywrap(ec_info *);
-
-/*----- Cyclic groups -----------------------------------------------------*/
-
-typedef struct ge_pyobj {
-  PyObject_HEAD
-  ge *x;
-  group *g;
-} ge_pyobj;
-
-extern PyTypeObject *ge_pytype;
-#define GE_PYCHECK(o) PyObject_TypeCheck((o), ge_pytype)
-#define GE_X(o) (((ge_pyobj *)(o))->x)
-#define GE_G(o) (((ge_pyobj *)(o))->g)
-#define GE_GOBJ(o) ((PyObject *)(group_pyobj *)(o)->ob_type)
-extern PyObject *ge_pywrap(PyObject *, ge *);
-
-typedef struct group_pyobj {
-  PyHeapTypeObject ty;
-  group *g;
-} group_pyobj;
-
-extern PyTypeObject *group_pytype;
-#define GROUP_G(o) (((group_pyobj *)(o))->g)
-extern PyObject *group_pywrap(group *);
-extern group *group_copy(group *);
-
-/*----- Random number generators ------------------------------------------*/
-
-#define f_freeme 1u
-
-typedef struct grand_pyobj {
-  PyObject_HEAD
-  unsigned f;
-  grand *r;
-} grand_pyobj;
-
-extern PyTypeObject *grand_pytype;
-extern PyObject *rand_pyobj;
-#define GRAND_PYCHECK(o) PyObject_TypeCheck((o), grand_pytype)
-#define GRAND_F(o) (((grand_pyobj *)(o))->f)
-#define GRAND_R(o) (((grand_pyobj *)(o))->r)
-extern PyObject *grand_pywrap(grand *, unsigned);
-extern int convgrand(PyObject *, void *);
-
-/*----- Symmetric cryptography --------------------------------------------*/
-
-extern PyObject *keysz_pywrap(const octet *);
-
-extern int convgccipher(PyObject *, void *);
-extern PyObject *gccipher_pywrap(gccipher *);
-
-typedef struct gchash_pyobj {
-  PyHeapTypeObject ty;
-  gchash *ch;
-} gchash_pyobj;
-
-extern PyTypeObject *gchash_pytype;
-extern PyObject *sha_pyobj, *has160_pyobj;
-#define GCHASH_PYCHECK(o) PyObject_TypeCheck((o), gchash_pytype)
-#define GCHASH_CH(o) (((gchash_pyobj *)(o))->ch)
-extern PyObject *ghash_pywrap(PyObject *, ghash *);
-extern int convgchash(PyObject *, void *);
-extern int convghash(PyObject *, void *);
-
-extern int convgcmac(PyObject *, void *);
-
-/*----- Key generation ----------------------------------------------------*/
-
-typedef struct pfilt_pyobj {
-  PyObject_HEAD
-  pfilt f;
-  int st;
-} pfilt_pyobj;
-
-extern PyTypeObject *pfilt_pytype;
-#define PFILT_PYCHECK(o) PyObject_TypeCheck(o, pfilt_pytype)
-#define PFILT_F(o) (&((pfilt_pyobj *)(o))->f)
-#define PFILT_ST(o) (((pfilt_pyobj *)(o))->st)
-
-typedef struct { pgen_proc *proc; void *ctx; } pgev;
-#define PGEV_HEAD PyObject_HEAD pgev pg;
-
-typedef struct pgev_pyobj {
-  PGEV_HEAD
-} pgev_pyobj;
-
-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(pypgev *);
-extern void pgenerr(struct excinfo *exc);
-
-/*----- That's all, folks -------------------------------------------------*/
-
-#ifdef __cplusplus
-  }
-#endif
-
-#endif
diff --git a/catacomb.c b/catacomb.c
deleted file mode 100644 (file)
index b54227f..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/* -*-c-*-
- *
- * Where the fun begins
- *
- * (c) 2004 Straylight/Edgeware
- */
-
-/*----- Licensing notice --------------------------------------------------*
- *
- * This file is part of the Python interface to Catacomb.
- *
- * Catacomb/Python is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Catacomb/Python is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Catacomb/Python; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-/*----- Header files ------------------------------------------------------*/
-
-#include "catacomb-python.h"
-
-/*----- Main code ---------------------------------------------------------*/
-
-static const struct nameval consts[] = {
-#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),
-  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(KF_NONSECRET),
-  C(KF_BURN), C(KF_OPT),
-  C(EC_XONLY), C(EC_YBIT), C(EC_LSB), C(EC_CMPR), C(EC_EXPLY), C(EC_SORT),
-  C(X25519_KEYSZ), C(X25519_PUBSZ), C(X25519_OUTSZ),
-  C(X448_KEYSZ), C(X448_PUBSZ), C(X448_OUTSZ),
-  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 }
-};
-
-PyObject *mexp_common(PyObject *me, PyObject *arg,
-                     size_t efsz,
-                     PyObject *(*id)(PyObject *),
-                     int (*fill)(void *, PyObject *,
-                                 PyObject *, PyObject *),
-                     PyObject *(*exp)(PyObject *, void *, int),
-                     void (*drop)(void *))
-{
-  int i = 0, j, n, flat;
-  PyObject *qq, *x, *y, *z = 0;
-  char *v = 0, *vv;
-
-  if (PyTuple_GET_SIZE(arg) == 1)
-    arg = PyTuple_GET_ITEM(arg, 0);
-  Py_INCREF(arg);
-  if (!PySequence_Check(arg)) TYERR("not a sequence");
-  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;
-  else {
-    if (n % 2) VALERR("must have even number of arguments");
-    n /= 2;
-    flat = 1;
-  }
-  Py_DECREF(x);
-
-  v = xmalloc(n * efsz);
-  for (i = j = 0, vv = v; i < n; i++, vv += efsz) {
-    if (flat) {
-      x = PySequence_GetItem(arg, j++);
-      y = PySequence_GetItem(arg, j++);
-    } else {
-      qq = PySequence_GetItem(arg, j++);
-      if (!qq) goto end;
-      if (!PySequence_Check(qq) || PySequence_Size(qq) != 2) {
-       Py_DECREF(qq);
-       TYERR("want a sequence of pairs");
-      }
-      x = PySequence_GetItem(qq, 0);
-      y = PySequence_GetItem(qq, 1);
-      Py_DECREF(qq);
-    }
-    if (!x || !y) goto end;
-    if (fill(vv, me, x, y)) {
-      Py_DECREF(x);
-      Py_DECREF(y);
-      if (!PyErr_Occurred())
-       PyErr_SetString(PyExc_TypeError, "type mismatch");
-      goto end;
-    }
-    Py_DECREF(x);
-    Py_DECREF(y);
-  }
-  z = exp(me, v, n);
-
-end:
-  if (v) {
-    for (j = 0, vv = v; j < i; j++, vv += efsz)
-      drop(vv);
-    xfree(v);
-  }
-  Py_DECREF(arg);
-  return (z);
-}
-
-static PyObject *smallprimes(void)
-{
-  PyObject *v = PyList_New(NPRIME);
-  int i;
-
-  for (i = 0; i < NPRIME; i++)
-    PyList_SET_ITEM(v, i, PyInt_FromLong(primetab[i]));
-  return (v);
-}
-
-static PyObject *meth__ego(PyObject *me, PyObject *arg)
-{
-  char *argv0;
-  if (!PyArg_ParseTuple(arg, "s:_ego", &argv0))
-    return (0);
-  if (STRCMP(QUIS, ==, "<UNNAMED>"))
-    ego(argv0);
-  RETURN_NONE;
-}
-
-static PyMethodDef methods[] = {
-#define METHNAME(func) meth_##func
-  METH (_ego,          "_ego(ARGV0)")
-#undef METHNAME
-  { 0 }
-};
-
-static void init_random(void)
-{
-#if PY_VERSION_HEX >= 0x02060000
-  char *seed;
-  uint32 r;
-
-  if (!Py_HashRandomizationFlag) return;
-  seed = getenv("PYTHONHASHSEED");
-  if (!seed || STRCMP(seed, ==, "random")) r = GR_WORD(&rand_global);
-  else r = strtoul(seed, 0, 0);
-  if (!r) r = 0xe011f220; /* zero doesn't work well */
-  unihash_setkey(&unihash_global, r);
-#endif
-}
-
-EXPORT void init_base(void)
-{
-  PyObject *mod;
-
-  modname = PyString_FromString("catacomb");
-  addmethods(methods);
-  INIT_MODULES;
-  init_random();
-  mod = Py_InitModule("catacomb._base", donemethods());
-  INSERT_MODULES;
-  INSERT("smallprimes", smallprimes());
-  setconstants(mod, consts);
-}
-
-/*----- That's all, folks -------------------------------------------------*/
diff --git a/mapping.c b/mapping.c
new file mode 100644 (file)
index 0000000..229dfa9
--- /dev/null
+++ b/mapping.c
@@ -0,0 +1,425 @@
+/* -*-c-*-
+ *
+ * Generic mapping support
+ *
+ * (c) 2019 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Pyke: the Python Kit for Extensions.
+ *
+ * Pyke is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Pyke is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pyke.  If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "pyke.h"
+
+/*----- Iteration ---------------------------------------------------------*/
+
+static PyTypeObject *itemiter_pytype, *valiter_pytype;
+
+typedef struct iter_pyobj {
+  PyObject_HEAD
+  PyObject *map;
+  PyObject *i;
+} iter_pyobj;
+#define ITER_MAP(o) (((iter_pyobj *)(o))->map)
+#define ITER_I(o) (((iter_pyobj *)(o))->i)
+
+static void iter_pydealloc(PyObject *me)
+  { Py_DECREF(ITER_MAP(me)); Py_DECREF(ITER_I(me)); FREEOBJ(me); }
+
+static PyObject *itemiter_pynext(PyObject *me)
+{
+  PyObject *k = 0, *v = 0, *rc = 0;
+
+  if ((k = PyIter_Next(ITER_I(me))) != 0 &&
+      (v = PyObject_GetItem(ITER_MAP(me), k)) != 0)
+    rc = Py_BuildValue("(OO)", k, v);
+  Py_XDECREF(k); Py_XDECREF(v);
+  return (rc);
+}
+
+static PyTypeObject itemiter_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "ItemIter",                          /* @tp_name@ */
+  sizeof(iter_pyobj),                  /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  iter_pydealloc,                      /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "Iterates over the items of a mapping.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  PyObject_SelfIter,                   /* @tp_iter@ */
+  itemiter_pynext,                     /* @tp_iternext@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  0,                                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+static PyObject *valiter_pynext(PyObject *me)
+{
+  PyObject *k = 0, *rc = 0;
+
+  if ((k = PyIter_Next(ITER_I(me))) != 0)
+    rc = PyObject_GetItem(ITER_MAP(me), k);
+  Py_XDECREF(k);
+  return (rc);
+}
+
+static PyTypeObject valiter_pytype_skel = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "ValueIter",                         /* @tp_name@ */
+  sizeof(iter_pyobj),                  /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  iter_pydealloc,                      /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  0,                                   /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  0,                                   /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_BASETYPE,
+
+  /* @tp_doc@ */
+  "Iterates over the values of a mapping.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  PyObject_SelfIter,                   /* @tp_iter@ */
+  valiter_pynext,                      /* @tp_iternext@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  0,                                   /* @tp_getset@ */
+  0,                                   /* @tp_base@ */
+  0,                                   /* @tp_dict@ */
+  0,                                   /* @tp_descr_get@ */
+  0,                                   /* @tp_descr_set@ */
+  0,                                   /* @tp_dictoffset@ */
+  0,                                   /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  abstract_pynew,                      /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+PySequenceMethods gmap_pysequence = {
+  0,                                   /* @sq_length@ */
+  0,                                   /* @sq_concat@ */
+  0,                                   /* @sq_repeat@ */
+  0,                                   /* @sq_item@ */
+  0,                                   /* @sq_slice@ */
+  0,                                   /* @sq_ass_item@ */
+  0,                                   /* @sq_ass_slice@ */
+  PyMapping_HasKey,                    /* @sq_contains@ */
+  0,                                   /* @sq_inplace_concat@ */
+  0                                    /* @sq_inplace_repeat@ */
+};
+
+/*----- Other mapping protocol support ------------------------------------*/
+
+Py_ssize_t gmap_pysize(PyObject *me)
+{
+  PyObject *i = 0, *x = 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; }
+  if (PyErr_Occurred()) goto done;
+  rc = n;
+done:
+  Py_XDECREF(i); Py_XDECREF(x);
+  return (rc);
+}
+
+PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg)
+{
+  PyObject *k;
+  if (!PyArg_ParseTuple(arg, "O:has_key", &k)) return (0);
+  return (getbool(PyMapping_HasKey(me, k)));
+}
+
+PyObject *gmapmeth_keys(PyObject *me, PyObject *arg)
+{
+  PyObject *l = 0, *i = 0, *k, *rc = 0;
+  int err;
+
+  if (!PyArg_ParseTuple(arg, ":keys") ||
+      (l = PyList_New(0)) == 0 ||
+      (i = PyObject_GetIter(me)) == 0)
+    goto done;
+  while ((k = PyIter_Next(i)) != 0)
+    { err = PyList_Append(l, k); Py_DECREF(k); if (err) goto done; }
+  if (PyErr_Occurred()) goto done;
+  rc = l; l = 0;
+done:
+  Py_XDECREF(l); Py_XDECREF(i);
+  return (rc);
+}
+
+PyObject *gmapmeth_values(PyObject *me, PyObject *arg)
+{
+  PyObject *l = 0, *i = 0, *k, *v, *rc = 0;
+  int err = 0;
+
+  if (!PyArg_ParseTuple(arg, ":values") ||
+      (l = PyList_New(0)) == 0 ||
+      (i = PyObject_GetIter(me)) == 0)
+    goto done;
+  while ((k = PyIter_Next(i)) != 0) {
+    if ((v = PyObject_GetItem(me, k)) == 0 ||
+       PyList_Append(l, v))
+      err = -1;
+    Py_DECREF(k); Py_XDECREF(v);
+    if (err) goto done;
+  }
+  if (PyErr_Occurred()) goto done;
+  rc = l; l = 0;
+done:
+  Py_XDECREF(l); Py_XDECREF(i);
+  return (rc);
+}
+
+PyObject *gmapmeth_items(PyObject *me, PyObject *arg)
+{
+  PyObject *l = 0, *i = 0, *k, *v, *z, *rc = 0;
+  int err = 0;
+
+  if (!PyArg_ParseTuple(arg, ":items") ||
+      (l = PyList_New(0)) == 0 ||
+      (i = PyObject_GetIter(me)) == 0)
+    goto done;
+  while ((k = PyIter_Next(i)) != 0) {
+    z = 0;
+    if ((v = PyObject_GetItem(me, k)) == 0 ||
+       (z = Py_BuildValue("(OO)", k, v)) == 0 ||
+       PyList_Append(l, z))
+      err = -1;
+    Py_DECREF(k); Py_XDECREF(v); Py_XDECREF(z);
+    if (err) goto done;
+  }
+  if (PyErr_Occurred()) goto done;
+  rc = l; l = 0;
+done:
+  Py_XDECREF(l); Py_XDECREF(i);
+  return (rc);
+}
+
+PyObject *gmapmeth_iterkeys(PyObject *me, PyObject *arg)
+{
+  if (!PyArg_ParseTuple(arg, ":iterkeys")) return (0);
+  return (PyObject_GetIter(me));
+}
+
+PyObject *gmapmeth_itervalues(PyObject *me, PyObject *arg)
+{
+  PyObject *i;
+  iter_pyobj *ii;
+
+  if (!PyArg_ParseTuple(arg, ":itervalues") ||
+      (i = PyObject_GetIter(me)) == 0)
+    return (0);
+  ii = PyObject_NEW(iter_pyobj, valiter_pytype);
+  ii->map = me; Py_INCREF(me);
+  ii->i = i;
+  return ((PyObject *)ii);
+}
+
+PyObject *gmapmeth_iteritems(PyObject *me, PyObject *arg)
+{
+  PyObject *i;
+  iter_pyobj *ii;
+
+  if (!PyArg_ParseTuple(arg, ":iteritems") ||
+      (i = PyObject_GetIter(me)) == 0)
+    return (0);
+  ii = PyObject_NEW(iter_pyobj, itemiter_pytype);
+  ii->map = me; Py_INCREF(me);
+  ii->i = i;
+  return ((PyObject *)ii);
+}
+
+PyObject *gmapmeth_clear(PyObject *me, PyObject *arg)
+{
+  PyObject *i = 0, *k = 0, *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, ":clear") ||
+      (i = PyObject_GetIter(me)) == 0)
+    goto end;
+  while ((k = PyIter_Next(i)) != 0) {
+    PyObject_DelItem(me, k);
+    Py_DECREF(k);
+  }
+  if (PyErr_Occurred()) goto end;
+  rc = me; Py_INCREF(me);
+end:
+  Py_XDECREF(i);
+  return (rc);
+}
+
+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, "O|O:get",
+                                  (/*unconst*/ char **)def_kwlist,
+                                  &k, &def))
+    return (0);
+  if ((v = PyObject_GetItem(me, k)) != 0) return (v);
+  PyErr_Clear();
+  RETURN_OBJ(def);
+}
+
+PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  PyObject *k, *def = Py_None, *v;
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
+                                  (/*unconst*/ char **)def_kwlist,
+                                  &k, &def))
+    return (0);
+  if ((v = PyObject_GetItem(me, k)) != 0) return (v);
+  PyErr_Clear();
+  if (PyObject_SetItem(me, k, def)) return (0);
+  RETURN_OBJ(def);
+}
+
+PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  PyObject *k, *def = 0, *v;
+
+  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);
+  } else if (def) {
+    PyErr_Clear();
+    RETURN_OBJ(def);
+  } else
+    return (0);
+}
+
+PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
+{
+  PyObject *map, *i = 0, *k, *v, *rc = 0;
+  int err = 0;
+
+  if (!PyArg_ParseTuple(arg, "O:update", &map) ||
+      (i = PyObject_GetIter(map)) == 0)
+    goto end;
+  while ((k = PyIter_Next(i)) != 0) {
+    if ((v = PyObject_GetItem(map, k)) == 0 ||
+       PyObject_SetItem(me, k, v))
+      err = -1;
+    Py_DECREF(k); Py_XDECREF(v);
+    if (err) goto end;
+  }
+  if (PyErr_Occurred()) goto end;
+  rc = me; Py_INCREF(me);
+end:
+  Py_XDECREF(i);
+  return (rc);
+}
+
+PyObject *gmapmeth_popitem(PyObject *me, PyObject *arg)
+{
+  PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
+
+  if (!PyArg_ParseTuple(arg, ":popitem") ||
+      (i = PyObject_GetIter(me)) == 0)
+    goto end;
+  if ((k = PyIter_Next(i)) == 0) {
+    if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
+    goto end;
+  }
+  if ((v = PyObject_GetItem(me, k)) == 0 ||
+      PyObject_DelItem(me, k))
+    goto end;
+  rc = Py_BuildValue("(OO)", k, v);
+end:
+  Py_XDECREF(i); Py_XDECREF(k); Py_XDECREF(v);
+  return (rc);
+}
+
+PyMethodDef gmap_pymethods[] = {
+  GMAP_METHODS
+  { 0 }
+};
+
+/*----- Submodule initialization ------------------------------------------*/
+
+void pyke_gmap_pyinit(void)
+{
+  INITTYPE(itemiter, root);
+  INITTYPE(valiter, root);
+}
+
+void pyke_gmap_pyinsert(PyObject *mod)
+{
+  INSERT("ItemIter", itemiter_pytype);
+  INSERT("ValueIter", valiter_pytype);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/pyke-mLib.c b/pyke-mLib.c
new file mode 100644 (file)
index 0000000..a29f34c
--- /dev/null
@@ -0,0 +1,131 @@
+/* -*-c-*-
+ *
+ * Pyke: the Python Kit for Extensions, mLib integration
+ *
+ * (c) 2019 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Pyke: the Python Kit for Extensions.
+ *
+ * Pyke is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Pyke is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pyke.  If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "pyke-mLib.h"
+
+/* #undef HAVE_LONG_LONG */
+
+/*----- Conversions -------------------------------------------------------*/
+
+#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;
+
+  if (init_i32()) goto end;
+  if ((i = PyLong_FromUnsignedLong(HI64(u))) == 0) goto end;
+  if ((t = PyNumber_InPlaceLshift(i, i32)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  if ((j = PyLong_FromUnsignedLong(LO64(u))) == 0) goto end;
+  if ((t = PyNumber_InPlaceOr(i, j)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  if ((rc = PyNumber_Int(i)) == 0) goto end;
+end:
+  Py_XDECREF(i);
+  Py_XDECREF(j);
+  return (rc);
+#endif
+}
+
+#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 (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);                                                                \
+  }
+DOUINTSZ(CONVU_)
+
+int convk64(PyObject *o, void *pp)
+{
+  PyObject *i = 0;
+  int rc = 0;
+#if HAVE_LONG_LONG
+  unsigned PY_LONG_LONG t;
+#else
+  PyObject *t;
+  uint32 lo, hi;
+#endif
+
+#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);
+  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  hi = PyInt_AsUnsignedLongMask(i);
+  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
+  Py_DECREF(i); i = t;
+  if (PyObject_IsTrue(i)) VALERR("out of range");
+  SET64(*(kludge64 *)pp, hi, lo);
+#endif
+  rc = 1;
+
+end:
+  Py_XDECREF(i);
+  return (rc);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/pyke-mLib.h b/pyke-mLib.h
new file mode 100644 (file)
index 0000000..f1e8cd2
--- /dev/null
@@ -0,0 +1,62 @@
+/* -*-c-*-
+ *
+ * Pyke: the Python Kit for Extensions, mLib integration
+ *
+ * (c) 2019 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Pyke: the Python Kit for Extensions.
+ *
+ * Pyke is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Pyke is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pyke.  If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PYKE_MLIB_H
+#define PYKE_MLIB_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef PYKE_H
+#  include "pyke.h"
+#endif
+
+PUBLIC_SYMBOLS;
+#include <mLib/bits.h>
+PRIVATE_SYMBOLS;
+
+/*----- Conversions -------------------------------------------------------*/
+
+#define DECL_CONVU_(n) extern int convu##n(PyObject *, void *);
+DOUINTSZ(DECL_CONVU_)
+  /* Define an `O&' input conversion `convuN' for each mLib type `uintN'. */
+
+extern int convk64(PyObject *, void *);
+  /* Input conversion for `kludge64'. */
+
+extern PyObject *getk64(kludge64);
+  /* Output conversion for `kludge64'. */
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/pyke.c b/pyke.c
new file mode 100644 (file)
index 0000000..883223a
--- /dev/null
+++ b/pyke.c
@@ -0,0 +1,371 @@
+/* -*-c-*-
+ *
+ * Pyke: the Python Kit for Extensions
+ *
+ * (c) 2019 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Pyke: the Python Kit for Extensions.
+ *
+ * Pyke is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Pyke is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pyke.  If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "pyke.h"
+
+/*----- External variables ------------------------------------------------*/
+
+PyObject *modname;
+PyObject *home_module;
+
+/*----- Conversions -------------------------------------------------------*/
+
+PyObject *getulong(unsigned long w)
+{
+  if (w <= LONG_MAX) return (PyInt_FromLong(w));
+  else return (PyLong_FromUnsignedLong(w));
+}
+
+PyObject *getbool(int b)
+  { if (b) RETURN_TRUE; else RETURN_FALSE; }
+
+int convulong(PyObject *o, void *pp)
+{
+  long i;
+  unsigned long *p = pp;
+  PyObject *t;
+
+  if (!o) VALERR("can't delete");
+  if (PyInt_Check(o)) {
+    i = PyInt_AS_LONG(o);
+    if (i < 0) VALERR("must be nonnegative");
+    *p = i;
+  } else {
+    if ((t = PyNumber_Long(o)) == 0) goto end;
+    *p = PyLong_AsUnsignedLong(t);
+    Py_DECREF(t);
+    if (PyErr_Occurred()) goto end;
+  }
+  return (1);
+end:
+  return (0);
+}
+
+int convuint(PyObject *o, void *pp)
+{
+  unsigned long u;
+  unsigned *p = pp;
+
+  if (!convulong(o, &u)) goto end;
+  if (u > UINT_MAX) VALERR("out of range");
+  *p = u;
+  return (1);
+end:
+  return (0);
+}
+
+int convszt(PyObject *o, void *pp)
+{
+  unsigned long u;
+  size_t *p = pp;
+
+  if (!convulong(o, &u)) goto end;
+  if (u > ~(size_t)0) VALERR("out of range");
+  *p = u;
+  return (1);
+end:
+  return (0);
+}
+
+int convbool(PyObject *o, void *pp)
+{
+  if (!o) VALERR("can't delete");
+  *(int *)pp = PyObject_IsTrue(o);
+  return (1);
+end:
+  return (0);
+}
+
+/*----- Miscellaneous utilities -------------------------------------------*/
+
+PyObject *abstract_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  PyErr_SetString(PyExc_TypeError, "can't instantiate this class");
+  return (0);
+}
+
+/*----- Saving and restoring exceptions ----------------------------------*/
+
+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);
+  }
+}
+
+/*----- Type definitions --------------------------------------------------*/
+
+static const PyTypeObject emptytype = { 0 };
+
+void *newtype(PyTypeObject *metaty,
+             const PyTypeObject *skel,
+             const char *name)
+{
+  PyHeapTypeObject *ty =
+    (PyHeapTypeObject *)_PyObject_GC_Malloc(_PyObject_VAR_SIZE(metaty, 0));
+  if (!skel) skel = &emptytype;
+  memcpy(ty, skel, sizeof(*skel));
+  if (ty->ht_type.tp_base) Py_INCREF(ty->ht_type.tp_base);
+#define COPY(blah) do {                                                        \
+    if (ty->ht_type.tp_as_##blah) {                                    \
+      memcpy(&ty->as_##blah,                                           \
+            ty->ht_type.tp_as_##blah,                                  \
+            sizeof(ty->as_##blah));                                    \
+      ty->ht_type.tp_as_##blah = &ty->as_##blah;                       \
+    }                                                                  \
+  } while (0)
+  COPY(number);
+  COPY(sequence);
+  COPY(mapping);
+  COPY(buffer);
+#undef COPY
+  if (name)
+    ty->ht_name = PyString_FromString(name);
+  else if (ty->ht_type.tp_name)
+    ty->ht_name = PyString_FromString(ty->ht_type.tp_name);
+  if (ty->ht_name)
+    ty->ht_type.tp_name = PyString_AS_STRING(ty->ht_name);
+  (void)PyObject_INIT(&ty->ht_type, metaty);
+  Py_INCREF(metaty);
+  return (ty);
+}
+
+void typeready(PyTypeObject *ty)
+{
+  PyType_Ready(ty);
+  PyDict_SetItemString(ty->tp_dict, "__module__", modname);
+}
+
+PyTypeObject *inittype(PyTypeObject *tyskel, PyTypeObject *meta)
+{
+  PyTypeObject *ty = newtype(meta, tyskel, 0);
+  ty->tp_flags |= Py_TPFLAGS_HEAPTYPE;
+  typeready(ty);
+  return (ty);
+}
+
+/*----- Populating modules ------------------------------------------------*/
+
+PyObject *mkexc(PyObject *mod, PyObject *base,
+               const char *name, PyMethodDef *mm)
+{
+  PyObject *nameobj = 0;
+  PyObject *dict = 0;
+  PyObject *exc = 0;
+  PyObject *func = 0;
+  PyObject *meth = 0;
+
+  if ((dict = PyDict_New()) == 0) goto fail;
+
+  if (mm) {
+    while (mm->ml_name) {
+      if ((func = PyCFunction_NewEx(mm, 0, mod)) == 0 ||
+         (meth = PyMethod_New(func, 0, exc)) == 0 ||
+         PyDict_SetItemString(dict, mm->ml_name, meth))
+       goto fail;
+      Py_DECREF(func); func = 0;
+      Py_DECREF(meth); meth = 0;
+      mm++;
+    }
+  }
+
+  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);
+  return (exc);
+
+fail:
+  Py_XDECREF(exc);
+  Py_XDECREF(func);
+  Py_XDECREF(meth);
+  exc = 0;
+  goto done;
+}
+
+void setconstants(PyObject *mod, const struct nameval *c)
+{
+  PyObject *x;
+  unsigned long u;
+
+  while (c->name) {
+    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++;
+  }
+}
+
+/*----- Submodules --------------------------------------------------------*/
+
+static PyMethodDef *global_methods;
+static size_t nmethods, methodsz;
+
+void addmethods(const PyMethodDef *m)
+{
+  size_t n, want, newsz;
+
+  for (n = 0; m[n].ml_name; n++);
+  want = nmethods + n + 1;
+  if (want > methodsz) {
+    newsz = methodsz ? 2*methodsz : 16;
+    while (want > newsz) newsz *= 2;
+    if (!global_methods)
+      global_methods = PyObject_Malloc(newsz*sizeof(PyMethodDef));
+    else
+      global_methods = PyObject_Realloc(global_methods,
+                                       newsz*sizeof(PyMethodDef));
+    assert(global_methods);
+    methodsz = newsz;
+  }
+  memcpy(global_methods + nmethods, m, n*sizeof(PyMethodDef));
+  nmethods += n;
+  global_methods[nmethods].ml_name = 0;
+}
+
+PyMethodDef *donemethods(void) { return (global_methods); }
+
+/*----- Low-level Python interface ----------------------------------------*/
+
+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 pyke_core_pyinit(void) { addmethods(methods); }
+void pyke_core_pyinsert(PyObject *mod) { ; }
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/pyke.h b/pyke.h
new file mode 100644 (file)
index 0000000..1f6232c
--- /dev/null
+++ b/pyke.h
@@ -0,0 +1,363 @@
+/* -*-c-*-
+ *
+ * Pyke: the Python Kit for Extensions
+ *
+ * (c) 2019 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Pyke: the Python Kit for Extensions.
+ *
+ * Pyke is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Pyke is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pyke.  If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PYKE_H
+#define PYKE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#define PY_SSIZE_T_CLEAN
+
+#include <Python.h>
+#include <structmember.h>
+
+/*----- Other preliminaries -----------------------------------------------*/
+
+#define NOTHING
+#define COMMA ,
+
+/*----- Symbol visibility -------------------------------------------------*
+ *
+ * This library is very messy regarding symbol namespace.  Keep this mess
+ * within our shared-object.
+ */
+
+#define GOBBLE_SEMI extern int notexist
+#if defined(__GNUC__) && defined(__ELF__)
+#  define PRIVATE_SYMBOLS _Pragma("GCC visibility push(hidden)") GOBBLE_SEMI
+#  define PUBLIC_SYMBOLS _Pragma("GCC visibility pop") GOBBLE_SEMI
+#  define EXPORT __attribute__((__visibility__("default")))
+#else
+#  define PRIVATE_SYMBOLS GOBBLE_SEMI
+#  define PUBLIC_SYMBOLS GOBBLE_SEMI
+#  define EXPORT
+#endif
+
+PRIVATE_SYMBOLS;
+
+/*----- Utilities for returning values and exceptions ---------------------*/
+
+/* Returning values. */
+#define RETURN_OBJ(obj) do { Py_INCREF(obj); return (obj); } while (0)
+#define RETURN_NONE RETURN_OBJ(Py_None)
+#define RETURN_NOTIMPL RETURN_OBJ(Py_NotImplemented)
+#define RETURN_TRUE RETURN_OBJ(Py_True)
+#define RETURN_FALSE RETURN_OBJ(Py_False)
+#define RETURN_ME RETURN_OBJ(me)
+
+/* Returning exceptions.  (Note that `KeyError' is `MAPERR' here, because
+ * Catacomb has its own kind of `KeyError'.)
+ */
+#define EXCERR(exc, str) do {                                          \
+  PyErr_SetString(exc, str);                                           \
+  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)
+#define INDEXERR(idx) do {                                             \
+  PyErr_SetObject(PyExc_KeyError, idx);                                        \
+  goto end;                                                            \
+} while (0)
+#define OSERR(name) do {                                               \
+  PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);                 \
+  goto end;                                                            \
+} while (0)
+
+/* Saving and restoring exceptions. */
+struct excinfo { PyObject *ty, *val, *tb; };
+#define EXCINFO_INIT { 0, 0, 0 }
+#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 *, ...);
+
+/*----- Conversions -------------------------------------------------------*/
+
+/* Define an input conversion (`O&') function: check that the object has
+ * Python type TY, and extract a C pointer to CTY by calling EXT on the
+ * object (which may well be a macro).
+ */
+#define CONVFUNC(ty, cty, ext)                                         \
+  int conv##ty(PyObject *o, void *p)                                   \
+  {                                                                    \
+    if (!PyObject_TypeCheck(o, ty##_pytype))                           \
+      TYERR("wanted a " #ty);                                          \
+    *(cty *)p = ext(o);                                                        \
+    return (1);                                                                \
+  end:                                                                 \
+    return (0);                                                                \
+  }
+
+/* Input conversion functions for standard kinds of objects, with overflow
+ * checking where applicable.
+ */
+extern int convulong(PyObject *, void *); /* unsigned long */
+extern int convuint(PyObject *, void *); /* unsigned int */
+extern int convszt(PyObject *, void *);        /* size_t */
+extern int convbool(PyObject *, void *); /* bool */
+
+/* Output conversions. */
+extern PyObject *getbool(int);         /* bool */
+extern PyObject *getulong(unsigned long); /* any kind of unsigned integer */
+
+/*----- Miscellaneous utilities -------------------------------------------*/
+
+#define FREEOBJ(obj)                                                   \
+  (((PyObject *)(obj))->ob_type->tp_free((PyObject *)(obj)))
+  /* Actually free OBJ, e.g., in a deallocation function. */
+
+extern PyObject *abstract_pynew(PyTypeObject *, PyObject *, PyObject *);
+  /* A `tp_new' function which refuses to make the object. */
+
+#define KWLIST (/*unconst*/ char **)kwlist
+  /* Strip `const' qualifiers from the keyword list `kwlist'.  Useful when
+   * calling `PyArg_ParseTupleAndKeywords', which isn't `const'-correct.
+   */
+
+/*----- Type definitions --------------------------------------------------*
+ *
+ * Pyke types are defined in a rather unusual way.
+ *
+ * The main code defines a `type skeleton' of type `PyTypeObject',
+ * conventionally named `TY_pytype_skel'.  Unlike typical Python type
+ * definitions in extensions, this can (and should) be read-only.  Also,
+ * there's no point in setting the `tp_base' pointer here, because the actual
+ * runtime base type object won't, in general, be known at compile time.
+ * Instead, the type skeletons are converted into Python `heap types' by the
+ * `INITTYPE' macro.  The main difference is that Python code can add
+ * attributes to heap types, and we make extensive use of this ability.
+ */
+
+extern void *newtype(PyTypeObject */*meta*/,
+                    const PyTypeObject */*skel*/, const char */*name*/);
+  /* Make and return a new Python type object, of type META (typically
+   * `PyType_Type', but may be a subclass), filled in from the skeleton SKEL
+   * (null to inherit everything), and named NAME.  The caller can mess with
+   * the type object further at this time: call `typeready' when it's set up
+   * properly.
+   */
+
+extern void typeready(PyTypeObject *);
+  /* The type object is now ready to be used. */
+
+extern PyTypeObject *inittype(PyTypeObject */*skel*/,
+                             PyTypeObject */*meta*/);
+  /* All-in-one function to construct a working type from a type skeleton
+   * SKEL, with metaclass META.  The caller is expected to have filled in the
+   * direct superclass in SKEL->tp_base.
+   */
+
+/* Alias for built-in types, to fit in with Pyke naming conventions. */
+#define root_pytype 0
+#define type_pytype &PyType_Type
+
+#define INITTYPE_META(ty, base, meta) do {                             \
+  ty##_pytype_skel.tp_base = base##_pytype;                            \
+  ty##_pytype = inittype(&ty##_pytype_skel, meta##_pytype);            \
+} while (0)
+#define INITTYPE(ty, base) INITTYPE_META(ty, base, type)
+  /* Macros to initialize a type from its skeleton. */
+
+/* Convenience wrappers for filling in `PyMethodDef' tables, following
+ * Pyke naming convention.  Define `METHNAME' locally as
+ *
+ *     #define METHNAME(name) foometh_##func
+ *
+ * around the method table.
+ */
+#define METH(func, doc)                                                        \
+  { #func, METHNAME(func), METH_VARARGS, doc },
+#define KWMETH(func, doc)                                              \
+  { #func, (PyCFunction)METHNAME(func),                                        \
+    METH_VARARGS | METH_KEYWORDS, doc },
+
+/* Convenience wrappers for filling in `PyGetSetDef' tables, following Pyke
+ * naming convention.  Define `GETSETNAME' locally as
+ *
+ *     #define GETSETNAME(op, name) foo##op##_##func
+ *
+ * around the get/set table.
+ */
+#define GET(func, doc)                                                 \
+  { #func, GETSETNAME(get, func), 0, doc },
+#define GETSET(func, doc)                                              \
+  { #func, GETSETNAME(get, func), GETSETNAME(set, func), doc },
+
+/* Convenience wrapper for filling in `PyMemberDef' tables.  Define
+ * `MEMBERSTRUCT' locally as
+ *
+ *     #define MEMBERSTRUCT foo_pyobj
+ *
+ * around the member table.
+ */
+#define MEMBER(name, ty, f, doc)                                       \
+  { #name, ty, offsetof(MEMBERSTRUCT, name), f, doc },
+
+/*----- Populating modules ------------------------------------------------*/
+
+extern PyObject *modname;
+  /* The overall module name.  Set this with `PyString_FromString'. */
+
+extern PyObject *home_module;
+  /* The overall module object. */
+
+extern PyObject *mkexc(PyObject */*mod*/, PyObject */*base*/,
+                      const char */*name*/, PyMethodDef */*methods*/);
+  /* Make and return an exception class called NAME, which will end up in
+   * module MOD (though it is not added at this time).  The new class is a
+   * subclass of BASE.  Attach the METHODS to it.
+   */
+
+#define INSERT(name, ob) do {                                          \
+  PyObject *_o = (PyObject *)(ob);                                     \
+  Py_INCREF(_o);                                                       \
+  PyModule_AddObject(mod, name, _o);                                   \
+} while (0)
+  /* Insert a Python object OB into the module `mod' under the given NAME. */
+
+/* Numeric constants. */
+struct nameval { const char *name; unsigned f; unsigned long value; };
+#define CF_SIGNED 1u
+extern void setconstants(PyObject *, const struct nameval *);
+
+#define INSEXC(name, var, base, meth)                                  \
+  INSERT(name, var = mkexc(mod, base, name, meth))
+  /* Insert an exception class into the module `mod'; other arguments are as
+   * for `mkexc'.
+   */
+
+/*----- Submodules --------------------------------------------------------*
+ *
+ * It's useful to split the Python module up into multiple source files, and
+ * have each one contribute its definitions into the main module.
+ *
+ * Define a list-macro `MODULES' in the master header file naming the
+ * submodules to be processed, and run
+ *
+ *     MODULES(DECLARE_MODINIT)
+ *
+ * to declare the interface functions.
+ *
+ * Each submodule FOO defines two functions: `FOO_pyinit' initializes types
+ * (see `INITTYPE' above) and accumulates methods (`addmethods' below), while
+ * `FOO_pyinsert' populates the module with additional definitions
+ * (especially types, though also constants).
+ *
+ * The top-level module initialization should call `INIT_MODULES' before
+ * creating the Python module, and `INSERT_MODULES' afterwards to make
+ * everything work.
+ */
+
+extern void addmethods(const PyMethodDef *);
+extern PyMethodDef *donemethods(void);
+  /* Accumulate method-table fragments, and return the combined table of all
+   * of the fragments.
+   */
+
+#define DECLARE_MODINIT(m)                                             \
+  extern void m##_pyinit(void);                                                \
+  extern void m##_pyinsert(PyObject *);
+  /* Declare submodule interface functions. */
+
+#define DOMODINIT(m) m##_pyinit();
+#define DOMODINSERT(m) m##_pyinsert(mod);
+#define INIT_MODULES do { MODULES(DOMODINIT) } while (0)
+#define INSERT_MODULES do { MODULES(DOMODINSERT) } while (0)
+  /* Top-level dispatch to the various submodules. */
+
+/*----- Generic mapping support -------------------------------------------*/
+
+/* Mapping methods. */
+#define GMAP_METH(func, doc) { #func, gmapmeth_##func, METH_VARARGS, doc },
+#define GMAP_KWMETH(func, doc)                                         \
+  { #func, (PyCFunction)gmapmeth_##func, METH_VARARGS|METH_KEYWORDS, doc },
+#define GMAP_METHDECL(func, doc)                                       \
+  extern PyObject *gmapmeth_##func(PyObject *, PyObject *);
+#define GMAP_KWMETHDECL(func, doc)                                     \
+  extern PyObject *gmapmeth_##func(PyObject *, PyObject *, PyObject *);
+
+#define GMAP_DOROMETHODS(METH, KWMETH)                                 \
+  METH (has_key,       "D.has_key(KEY) -> BOOL")                       \
+  METH (keys,          "D.keys() -> LIST")                             \
+  METH (values,        "D.values() -> LIST")                           \
+  METH (items,         "D.items() -> LIST")                            \
+  METH (iterkeys,      "D.iterkeys() -> ITER")                         \
+  METH (itervalues,    "D.itervalues() -> ITER")                       \
+  METH (iteritems,     "D.iteritems() -> ITER")                        \
+  KWMETH(get,          "D.get(KEY, [default = None]) -> VALUE")
+
+#define GMAP_DOMETHODS(METH, KWMETH)                                   \
+  GMAP_DOROMETHODS(METH, KWMETH)                                       \
+  METH (clear,         "D.clear()")                                    \
+  KWMETH(setdefault,   "D.setdefault(K, [default = None]) -> VALUE")   \
+  KWMETH(pop,          "D.pop(KEY, [default = <error>]) -> VALUE")     \
+  METH (popitem,       "D.popitem() -> (KEY, VALUE)")                  \
+  METH (update,        "D.update(MAP)")
+
+GMAP_DOMETHODS(GMAP_METHDECL, GMAP_KWMETHDECL)
+#define GMAP_ROMETHODS GMAP_DOROMETHODS(GMAP_METH, GMAP_KWMETH)
+#define GMAP_METHODS GMAP_DOMETHODS(GMAP_METH, GMAP_KWMETH)
+
+/* Mapping protocol implementation. */
+extern Py_ssize_t gmap_pysize(PyObject *); /* for `mp_length' */
+extern PySequenceMethods gmap_pysequence; /* for `tp_as_sequence' */
+extern PyMethodDef gmap_pymethods[]; /* all the standard methods */
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/util.c b/util.c
deleted file mode 100644 (file)
index 03a39af..0000000
--- a/util.c
+++ /dev/null
@@ -1,873 +0,0 @@
-/* -*-c-*-
- *
- * Miscellaneous utilities (not Catacomb-specific)
- *
- * (c) 2005 Straylight/Edgeware
- */
-
-/*----- Licensing notice --------------------------------------------------*
- *
- * This file is part of the Python interface to Catacomb.
- *
- * Catacomb/Python is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Catacomb/Python is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Catacomb/Python; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-/*----- Header files ------------------------------------------------------*/
-
-#include "catacomb-python.h"
-
-/* #undef HAVE_LONG_LONG */
-
-/*----- External values ---------------------------------------------------*/
-
-PyObject *modname = 0;
-PyObject *home_module = 0;
-
-/*----- Conversions -------------------------------------------------------*/
-
-PyObject *getulong(unsigned long w)
-{
-  if (w <= LONG_MAX)
-    return (PyInt_FromLong(w));
-  else
-    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;
-
-  if (init_i32()) goto end;
-  if ((i = PyLong_FromUnsignedLong(HI64(u))) == 0) goto end;
-  if ((t = PyNumber_InPlaceLshift(i, i32)) == 0) goto end;
-  Py_DECREF(i); i = t;
-  if ((j = PyLong_FromUnsignedLong(LO64(u))) == 0) goto end;
-  if ((t = PyNumber_InPlaceOr(i, j)) == 0) goto end;
-  Py_DECREF(i); i = t;
-  if ((rc = PyNumber_Int(i)) == 0) goto end;
-end:
-  Py_XDECREF(i);
-  Py_XDECREF(j);
-  return (rc);
-#endif
-}
-
-PyObject *getbool(int b)
-{
-  if (b) RETURN_TRUE;
-  else RETURN_FALSE;
-}
-
-PyObject *abstract_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
-{
-  PyErr_SetString(PyExc_TypeError, "can't instantiate this class");
-  return (0);
-}
-
-int convulong(PyObject *o, void *pp)
-{
-  long i;
-  unsigned long *p = pp;
-  PyObject *t;
-
-  if (!o) VALERR("can't delete");
-  if (PyInt_Check(o)) {
-    i = PyInt_AS_LONG(o);
-    if (i < 0) VALERR("must be nonnegative");
-    *p = i;
-  } else {
-    if ((t = PyNumber_Long(o)) == 0) goto end;
-    *p = PyLong_AsUnsignedLong(t);
-    Py_DECREF(t);
-    if (PyErr_Occurred()) goto end;
-  }
-  return (1);
-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 (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);                                                                \
-  }
-DOUINTSZ(CONVU_)
-
-int convuint(PyObject *o, void *pp)
-{
-  unsigned long u;
-  unsigned *p = pp;
-
-  if (!convulong(o, &u)) goto end;
-  if (u > UINT_MAX) VALERR("out of range");
-  *p = u;
-  return (1);
-end:
-  return (0);
-}
-
-int convk64(PyObject *o, void *pp)
-{
-  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);
-  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
-  Py_DECREF(i); i = t;
-  hi = PyInt_AsUnsignedLongMask(i);
-  if ((t = PyNumber_InPlaceRshift(i, i32)) == 0) goto end;
-  Py_DECREF(i); i = t;
-  if (PyObject_IsTrue(i)) VALERR("out of range");
-  SET64(*(kludge64 *)pp, hi, lo);
-#endif
-  rc = 1;
-
-end:
-  Py_XDECREF(i);
-  return (rc);
-}
-
-int convmpw(PyObject *o, void *pp)
-{
-  unsigned long u;
-  unsigned *p = pp;
-
-  if (!convulong(o, &u)) goto end;
-  if (u > MPW_MAX) VALERR("out of range");
-  *p = u;
-  return (1);
-end:
-  return (0);
-}
-
-int convszt(PyObject *o, void *pp)
-{
-  unsigned long u;
-  size_t *p = pp;
-
-  if (!convulong(o, &u)) goto end;
-  if (u > ~(size_t)0) VALERR("out of range");
-  *p = u;
-  return (1);
-end:
-  return (0);
-}
-
-int convbool(PyObject *o, void *pp)
-{
-  if (!o) VALERR("can't delete");
-  *(int *)pp = PyObject_IsTrue(o);
-  return (1);
-end:
-  return (0);
-}
-
-/*----- Type messing ------------------------------------------------------*/
-
-static const PyTypeObject emptytype = { 0 };
-
-void *newtype(PyTypeObject *metaty,
-             const PyTypeObject *skel,
-             const char *name)
-{
-  PyHeapTypeObject *ty =
-    (PyHeapTypeObject *)_PyObject_GC_Malloc(_PyObject_VAR_SIZE(metaty, 0));
-  if (!skel) skel = &emptytype;
-  memcpy(ty, skel, sizeof(*skel));
-  if (ty->ht_type.tp_base) Py_INCREF(ty->ht_type.tp_base);
-#define COPY(blah) do {                                                        \
-    if (ty->ht_type.tp_as_##blah) {                                    \
-      memcpy(&ty->as_##blah,                                           \
-            ty->ht_type.tp_as_##blah,                                  \
-            sizeof(ty->as_##blah));                                    \
-      ty->ht_type.tp_as_##blah = &ty->as_##blah;                       \
-    }                                                                  \
-  } while (0)
-  COPY(number);
-  COPY(sequence);
-  COPY(mapping);
-  COPY(buffer);
-#undef COPY
-  if (name)
-    ty->ht_name = PyString_FromString(name);
-  else if (ty->ht_type.tp_name)
-    ty->ht_name = PyString_FromString(ty->ht_type.tp_name);
-  if (ty->ht_name)
-    ty->ht_type.tp_name = PyString_AS_STRING(ty->ht_name);
-  (void)PyObject_INIT(&ty->ht_type, metaty);
-  Py_INCREF(metaty);
-  return (ty);
-}
-
-void typeready(PyTypeObject *ty)
-{
-  PyType_Ready(ty);
-  PyDict_SetItemString(ty->tp_dict, "__module__", modname);
-}
-
-PyTypeObject *inittype(PyTypeObject *tyskel, PyTypeObject *meta)
-{
-  PyTypeObject *ty = newtype(meta, tyskel, 0);
-  ty->tp_flags |= Py_TPFLAGS_HEAPTYPE;
-  typeready(ty);
-  return (ty);
-}
-
-/*----- Constants ---------------------------------------------------------*/
-
-void setconstants(PyObject *mod, const struct nameval *c)
-{
-  PyObject *x;
-  unsigned long u;
-
-  while (c->name) {
-    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++;
-  }
-}
-
-/*----- Building method tables --------------------------------------------*/
-
-static PyMethodDef *global_methods;
-static size_t nmethods, methodsz;
-
-void addmethods(const PyMethodDef *m)
-{
-  size_t n, want, newsz;
-
-  for (n = 0; m[n].ml_name; n++);
-  want = nmethods + n + 1;
-  if (want > methodsz) {
-    newsz = methodsz ? 2*methodsz : 16;
-    while (want > newsz) newsz *= 2;
-    if (!global_methods)
-      global_methods = PyObject_Malloc(newsz*sizeof(PyMethodDef));
-    else
-      global_methods = PyObject_Realloc(global_methods,
-                                       newsz*sizeof(PyMethodDef));
-    assert(global_methods);
-    methodsz = newsz;
-  }
-  memcpy(global_methods + nmethods, m, n*sizeof(PyMethodDef));
-  nmethods += n;
-  global_methods[nmethods].ml_name = 0;
-}
-
-PyMethodDef *donemethods(void) { return (global_methods); }
-
-/*----- Exceptions --------------------------------------------------------*/
-
-PyObject *mkexc(PyObject *mod, PyObject *base,
-               const char *name, PyMethodDef *mm)
-{
-  PyObject *nameobj = 0;
-  PyObject *dict = 0;
-  PyObject *exc = 0;
-  PyObject *func = 0;
-  PyObject *meth = 0;
-
-  if ((dict = PyDict_New()) == 0) goto fail;
-
-  if (mm) {
-    while (mm->ml_name) {
-      if ((func = PyCFunction_NewEx(mm, 0, mod)) == 0 ||
-         (meth = PyMethod_New(func, 0, exc)) == 0 ||
-         PyDict_SetItemString(dict, mm->ml_name, meth))
-       goto fail;
-      Py_DECREF(func); func = 0;
-      Py_DECREF(meth); meth = 0;
-      mm++;
-    }
-  }
-
-  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);
-  return (exc);
-
-fail:
-  Py_XDECREF(exc);
-  Py_XDECREF(func);
-  Py_XDECREF(meth);
-  exc = 0;
-  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;
-
-typedef struct iter_pyobj {
-  PyObject_HEAD
-  PyObject *map;
-  PyObject *i;
-} iter_pyobj;
-#define ITER_MAP(o) (((iter_pyobj *)(o))->map)
-#define ITER_I(o) (((iter_pyobj *)(o))->i)
-
-static void iter_pydealloc(PyObject *me)
-  { Py_DECREF(ITER_MAP(me)); Py_DECREF(ITER_I(me)); FREEOBJ(me); }
-
-static PyObject *itemiter_pynext(PyObject *me)
-{
-  PyObject *k = 0, *v = 0, *rc = 0;
-
-  if ((k = PyIter_Next(ITER_I(me))) != 0 &&
-      (v = PyObject_GetItem(ITER_MAP(me), k)) != 0)
-    rc = Py_BuildValue("(OO)", k, v);
-  Py_XDECREF(k); Py_XDECREF(v);
-  return (rc);
-}
-
-static PyTypeObject itemiter_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
-  "ItemIter",                          /* @tp_name@ */
-  sizeof(iter_pyobj),                  /* @tp_basicsize@ */
-  0,                                   /* @tp_itemsize@ */
-
-  iter_pydealloc,                      /* @tp_dealloc@ */
-  0,                                   /* @tp_print@ */
-  0,                                   /* @tp_getattr@ */
-  0,                                   /* @tp_setattr@ */
-  0,                                   /* @tp_compare@ */
-  0,                                   /* @tp_repr@ */
-  0,                                   /* @tp_as_number@ */
-  0,                                   /* @tp_as_sequence@ */
-  0,                                   /* @tp_as_mapping@ */
-  0,                                   /* @tp_hash@ */
-  0,                                   /* @tp_call@ */
-  0,                                   /* @tp_str@ */
-  0,                                   /* @tp_getattro@ */
-  0,                                   /* @tp_setattro@ */
-  0,                                   /* @tp_as_buffer@ */
-  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
-    Py_TPFLAGS_BASETYPE,
-
-  /* @tp_doc@ */
-  "Iterates over the items of a mapping.",
-
-  0,                                   /* @tp_traverse@ */
-  0,                                   /* @tp_clear@ */
-  0,                                   /* @tp_richcompare@ */
-  0,                                   /* @tp_weaklistoffset@ */
-  PyObject_SelfIter,                   /* @tp_iter@ */
-  itemiter_pynext,                     /* @tp_iternext@ */
-  0,                                   /* @tp_methods@ */
-  0,                                   /* @tp_members@ */
-  0,                                   /* @tp_getset@ */
-  0,                                   /* @tp_base@ */
-  0,                                   /* @tp_dict@ */
-  0,                                   /* @tp_descr_get@ */
-  0,                                   /* @tp_descr_set@ */
-  0,                                   /* @tp_dictoffset@ */
-  0,                                   /* @tp_init@ */
-  PyType_GenericAlloc,                 /* @tp_alloc@ */
-  abstract_pynew,                      /* @tp_new@ */
-  0,                                   /* @tp_free@ */
-  0                                    /* @tp_is_gc@ */
-};
-
-static PyObject *valiter_pynext(PyObject *me)
-{
-  PyObject *k = 0, *rc = 0;
-
-  if ((k = PyIter_Next(ITER_I(me))) != 0)
-    rc = PyObject_GetItem(ITER_MAP(me), k);
-  Py_XDECREF(k);
-  return (rc);
-}
-
-static PyTypeObject valiter_pytype_skel = {
-  PyObject_HEAD_INIT(0) 0,             /* Header */
-  "ValueIter",                         /* @tp_name@ */
-  sizeof(iter_pyobj),                  /* @tp_basicsize@ */
-  0,                                   /* @tp_itemsize@ */
-
-  iter_pydealloc,                      /* @tp_dealloc@ */
-  0,                                   /* @tp_print@ */
-  0,                                   /* @tp_getattr@ */
-  0,                                   /* @tp_setattr@ */
-  0,                                   /* @tp_compare@ */
-  0,                                   /* @tp_repr@ */
-  0,                                   /* @tp_as_number@ */
-  0,                                   /* @tp_as_sequence@ */
-  0,                                   /* @tp_as_mapping@ */
-  0,                                   /* @tp_hash@ */
-  0,                                   /* @tp_call@ */
-  0,                                   /* @tp_str@ */
-  0,                                   /* @tp_getattro@ */
-  0,                                   /* @tp_setattro@ */
-  0,                                   /* @tp_as_buffer@ */
-  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
-    Py_TPFLAGS_BASETYPE,
-
-  /* @tp_doc@ */
-  "Iterates over the values of a mapping.",
-
-  0,                                   /* @tp_traverse@ */
-  0,                                   /* @tp_clear@ */
-  0,                                   /* @tp_richcompare@ */
-  0,                                   /* @tp_weaklistoffset@ */
-  PyObject_SelfIter,                   /* @tp_iter@ */
-  valiter_pynext,                      /* @tp_iternext@ */
-  0,                                   /* @tp_methods@ */
-  0,                                   /* @tp_members@ */
-  0,                                   /* @tp_getset@ */
-  0,                                   /* @tp_base@ */
-  0,                                   /* @tp_dict@ */
-  0,                                   /* @tp_descr_get@ */
-  0,                                   /* @tp_descr_set@ */
-  0,                                   /* @tp_dictoffset@ */
-  0,                                   /* @tp_init@ */
-  PyType_GenericAlloc,                 /* @tp_alloc@ */
-  abstract_pynew,                      /* @tp_new@ */
-  0,                                   /* @tp_free@ */
-  0                                    /* @tp_is_gc@ */
-};
-
-PySequenceMethods gmap_pysequence = {
-  0,                                   /* @sq_length@ */
-  0,                                   /* @sq_concat@ */
-  0,                                   /* @sq_repeat@ */
-  0,                                   /* @sq_item@ */
-  0,                                   /* @sq_slice@ */
-  0,                                   /* @sq_ass_item@ */
-  0,                                   /* @sq_ass_slice@ */
-  PyMapping_HasKey,                    /* @sq_contains@ */
-  0,                                   /* @sq_inplace_concat@ */
-  0                                    /* @sq_inplace_repeat@ */
-};
-
-Py_ssize_t gmap_pysize(PyObject *me)
-{
-  PyObject *i = 0, *x = 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; }
-  if (PyErr_Occurred()) goto done;
-  rc = n;
-done:
-  Py_XDECREF(i); Py_XDECREF(x);
-  return (rc);
-}
-
-PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg)
-{
-  PyObject *k;
-  if (!PyArg_ParseTuple(arg, "O:has_key", &k)) return (0);
-  return (getbool(PyMapping_HasKey(me, k)));
-}
-
-PyObject *gmapmeth_keys(PyObject *me, PyObject *arg)
-{
-  PyObject *l = 0, *i = 0, *k, *rc = 0;
-  int err;
-
-  if (!PyArg_ParseTuple(arg, ":keys") ||
-      (l = PyList_New(0)) == 0 ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto done;
-  while ((k = PyIter_Next(i)) != 0)
-    { err = PyList_Append(l, k); Py_DECREF(k); if (err) goto done; }
-  if (PyErr_Occurred()) goto done;
-  rc = l; l = 0;
-done:
-  Py_XDECREF(l); Py_XDECREF(i);
-  return (rc);
-}
-
-PyObject *gmapmeth_values(PyObject *me, PyObject *arg)
-{
-  PyObject *l = 0, *i = 0, *k, *v, *rc = 0;
-  int err = 0;
-
-  if (!PyArg_ParseTuple(arg, ":values") ||
-      (l = PyList_New(0)) == 0 ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto done;
-  while ((k = PyIter_Next(i)) != 0) {
-    if ((v = PyObject_GetItem(me, k)) == 0 ||
-       PyList_Append(l, v))
-      err = -1;
-    Py_DECREF(k); Py_XDECREF(v);
-    if (err) goto done;
-  }
-  if (PyErr_Occurred()) goto done;
-  rc = l; l = 0;
-done:
-  Py_XDECREF(l); Py_XDECREF(i);
-  return (rc);
-}
-
-PyObject *gmapmeth_items(PyObject *me, PyObject *arg)
-{
-  PyObject *l = 0, *i = 0, *k, *v, *z, *rc = 0;
-  int err = 0;
-
-  if (!PyArg_ParseTuple(arg, ":items") ||
-      (l = PyList_New(0)) == 0 ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto done;
-  while ((k = PyIter_Next(i)) != 0) {
-    z = 0;
-    if ((v = PyObject_GetItem(me, k)) == 0 ||
-       (z = Py_BuildValue("(OO)", k, v)) == 0 ||
-       PyList_Append(l, z))
-      err = -1;
-    Py_DECREF(k); Py_XDECREF(v); Py_XDECREF(z);
-    if (err) goto done;
-  }
-  if (PyErr_Occurred()) goto done;
-  rc = l; l = 0;
-done:
-  Py_XDECREF(l); Py_XDECREF(i);
-  return (rc);
-}
-
-PyObject *gmapmeth_iterkeys(PyObject *me, PyObject *arg)
-{
-  if (!PyArg_ParseTuple(arg, ":iterkeys")) return (0);
-  return (PyObject_GetIter(me));
-}
-
-PyObject *gmapmeth_itervalues(PyObject *me, PyObject *arg)
-{
-  PyObject *i;
-  iter_pyobj *ii;
-
-  if (!PyArg_ParseTuple(arg, ":itervalues") ||
-      (i = PyObject_GetIter(me)) == 0)
-    return (0);
-  ii = PyObject_NEW(iter_pyobj, valiter_pytype);
-  ii->map = me; Py_INCREF(me);
-  ii->i = i;
-  return ((PyObject *)ii);
-}
-
-PyObject *gmapmeth_iteritems(PyObject *me, PyObject *arg)
-{
-  PyObject *i;
-  iter_pyobj *ii;
-
-  if (!PyArg_ParseTuple(arg, ":iteritems") ||
-      (i = PyObject_GetIter(me)) == 0)
-    return (0);
-  ii = PyObject_NEW(iter_pyobj, itemiter_pytype);
-  ii->map = me; Py_INCREF(me);
-  ii->i = i;
-  return ((PyObject *)ii);
-}
-
-PyObject *gmapmeth_clear(PyObject *me, PyObject *arg)
-{
-  PyObject *i = 0, *k = 0, *rc = 0;
-
-  if (!PyArg_ParseTuple(arg, ":clear") ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto end;
-  while ((k = PyIter_Next(i)) != 0) {
-    PyObject_DelItem(me, k);
-    Py_DECREF(k);
-  }
-  if (PyErr_Occurred()) goto end;
-  rc = me; Py_INCREF(me);
-end:
-  Py_XDECREF(i);
-  return (rc);
-}
-
-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, "O|O:get",
-                                  (/*unconst*/ char **)def_kwlist,
-                                  &k, &def))
-    return (0);
-  if ((v = PyObject_GetItem(me, k)) != 0) return (v);
-  PyErr_Clear();
-  RETURN_OBJ(def);
-}
-
-PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
-{
-  PyObject *k, *def = Py_None, *v;
-
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
-                                  (/*unconst*/ char **)def_kwlist,
-                                  &k, &def))
-    return (0);
-  if ((v = PyObject_GetItem(me, k)) != 0) return (v);
-  PyErr_Clear();
-  if (PyObject_SetItem(me, k, def)) return (0);
-  RETURN_OBJ(def);
-}
-
-PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
-{
-  PyObject *k, *def = 0, *v;
-
-  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);
-  } else if (def) {
-    PyErr_Clear();
-    RETURN_OBJ(def);
-  } else
-    return (0);
-}
-
-PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
-{
-  PyObject *map, *i = 0, *k, *v, *rc = 0;
-  int err = 0;
-
-  if (!PyArg_ParseTuple(arg, "O:update", &map) ||
-      (i = PyObject_GetIter(map)) == 0)
-    goto end;
-  while ((k = PyIter_Next(i)) != 0) {
-    if ((v = PyObject_GetItem(map, k)) == 0 ||
-       PyObject_SetItem(me, k, v))
-      err = -1;
-    Py_DECREF(k); Py_XDECREF(v);
-    if (err) goto end;
-  }
-  if (PyErr_Occurred()) goto end;
-  rc = me; Py_INCREF(me);
-end:
-  Py_XDECREF(i);
-  return (rc);
-}
-
-PyObject *gmapmeth_popitem(PyObject *me, PyObject *arg)
-{
-  PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
-
-  if (!PyArg_ParseTuple(arg, ":popitem") ||
-      (i = PyObject_GetIter(me)) == 0)
-    goto end;
-  if ((k = PyIter_Next(i)) == 0) {
-    if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
-    goto end;
-  }
-  if ((v = PyObject_GetItem(me, k)) == 0 ||
-      PyObject_DelItem(me, k))
-    goto end;
-  rc = Py_BuildValue("(OO)", k, v);
-end:
-  Py_XDECREF(i); Py_XDECREF(k); Py_XDECREF(v);
-  return (rc);
-}
-
-PyMethodDef gmap_pymethods[] = {
-  GMAP_METHODS
-  { 0 }
-};
-
-/*----- 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)
-{
-  INITTYPE(itemiter, root);
-  INITTYPE(valiter, root);
-  addmethods(methods);
-}
-
-void util_pyinsert(PyObject *mod)
-{
-  INSERT("ItemIter", itemiter_pytype);
-  INSERT("ValueIter", valiter_pytype);
-}
-
-/*----- That's all, folks -------------------------------------------------*/