+/* -*-c-*-
+ *
+ * $Id$
+ *
+ * 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"
+
+/*----- Conversions -------------------------------------------------------*/
+
+#define GETU_(n) \
+ PyObject *getu##n(uint##n w) \
+ { \
+ if (w <= MASK##n) \
+ return (PyInt_FromLong(w)); \
+ else \
+ return (PyLong_FromUnsignedLong(w)); \
+ }
+DOUINTSZ(GETU_)
+
+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 (PyInt_Check(o)) {
+ i = PyInt_AS_LONG(o);
+ if (i < 0) TYERR("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);
+}
+
+#define CONVU_(n) \
+ int convu##n(PyObject *o, void *pp) \
+ { \
+ unsigned long u; \
+ uint##n *p = pp; \
+ \
+ if (!convulong(o, &u)) goto end; \
+ if (u > MASK##n) TYERR("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) TYERR("out of range");
+ *p = u;
+ return (1);
+end:
+ return (0);
+}
+
+int convmpw(PyObject *o, void *pp)
+{
+ unsigned long u;
+ unsigned *p = pp;
+
+ if (!convulong(o, &u)) goto end;
+ if (u > MPW_MAX) TYERR("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) TYERR("out of range");
+ *p = u;
+ return (1);
+end:
+ return (0);
+}
+
+int convbool(PyObject *o, void *pp)
+{
+ *(int *)pp = PyObject_IsTrue(o);
+ return (1);
+}
+
+/*----- 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->type.tp_base) Py_INCREF(ty->type.tp_base);
+#define COPY(blah) do { \
+ if (ty->type.tp_as_##blah) { \
+ memcpy(&ty->as_##blah, \
+ ty->type.tp_as_##blah, \
+ sizeof(ty->as_##blah)); \
+ ty->type.tp_as_##blah = &ty->as_##blah; \
+ } \
+ } while (0)
+ COPY(number);
+ COPY(sequence);
+ COPY(mapping);
+ COPY(buffer);
+#undef COPY
+ if (name)
+ ty->name = PyString_FromString(name);
+ else if (ty->type.tp_name)
+ ty->name = PyString_FromString(ty->type.tp_name);
+ if (ty->name)
+ ty->type.tp_name = PyString_AS_STRING(ty->name);
+ PyObject_INIT(&ty->type, metaty);
+ Py_INCREF(metaty);
+ return (ty);
+}
+
+PyTypeObject *inittype(PyTypeObject *tyskel)
+{
+ PyTypeObject *ty = newtype(&PyType_Type, tyskel, 0);
+ ty->tp_flags |= Py_TPFLAGS_HEAPTYPE;
+ PyType_Ready(ty);
+ return (ty);
+}
+
+/*----- Constants ---------------------------------------------------------*/
+
+void setconstants(PyObject *mod, const struct nameval *c)
+{
+ PyObject *x;
+
+ while (c->name) {
+ if (c->value > LONG_MAX)
+ x = PyLong_FromUnsignedLong(c->value);
+ else
+ x = PyInt_FromLong(c->value);
+ PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x);
+ c++;
+ }
+}
+
+/*----- Building method tables --------------------------------------------*/
+
+DA_DECL(method_v, PyMethodDef);
+static method_v global_pymethods = DA_INIT;
+void addmethods(const PyMethodDef *m)
+{
+ size_t n;
+
+ for (n = 0; m[n].ml_name; n++);
+ DA_ENSURE(&global_pymethods, n);
+ memcpy(DA(&global_pymethods) + DA_LEN(&global_pymethods),
+ m, n * sizeof(*m));
+ DA_EXTEND(&global_pymethods, n);
+}
+
+PyMethodDef *donemethods(void)
+{
+ static const PyMethodDef mzero = { 0 };
+ DA_PUSH(&global_pymethods, mzero);
+ return (DA(&global_pymethods));
+}
+
+/*----- 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 ((nameobj = PyString_FromFormat("%s.%s",
+ PyModule_GetName(mod),
+ name)) == 0 ||
+ (dict = PyDict_New()) == 0 ||
+ (exc = PyErr_NewException(PyString_AS_STRING(nameobj),
+ base, dict)) == 0)
+ goto fail;
+
+ if (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++;
+ }
+ }
+
+done:
+ Py_XDECREF(nameobj);
+ Py_XDECREF(dict);
+ return (exc);
+
+fail:
+ Py_XDECREF(exc);
+ Py_XDECREF(func);
+ Py_XDECREF(meth);
+ exc = 0;
+ goto done;
+}
+
+/*----- 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 items 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@ */
+};
+
+int gmap_pysize(PyObject *me)
+{
+ PyObject *i = 0, *x = 0;
+ int rc = -1;
+ int 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 char *def_kwlist[] = { "key", "default", 0 };
+
+PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
+{
+ PyObject *k, *def = Py_None, *v;
+
+ if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:get", 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, "OO:setdefault",
+ 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, "OO:pop", def_kwlist, &k, &def))
+ return (0);
+ if ((v = PyObject_GetItem(me, k)) != 0) {
+ PyObject_DelItem(me, k);
+ return (v);
+ }
+ PyErr_Clear();
+ RETURN_OBJ(def);
+}
+
+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)))
+ 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 ----------------------------------------------------*/
+
+void util_init(void)
+{
+ INITTYPE(itemiter, root);
+ INITTYPE(valiter, root);
+}
+
+void util_insert(PyObject *mod)
+{
+ INSERT("ItemIter", itemiter_pytype);
+ INSERT("ValueIter", valiter_pytype);
+}
+
+/*----- That's all, folks -------------------------------------------------*/