X-Git-Url: https://git.distorted.org.uk/~mdw/pyke/blobdiff_plain/d4a9e7e71495a1180062371cbac5353795d1fe47..41efbcc08120755b34f8157cae1fe855314b5c9f:/util.c diff --git a/util.c b/util.c index 7201401..29f7d12 100644 --- a/util.c +++ b/util.c @@ -1,13 +1,11 @@ /* -*-c-*- * - * $Id$ - * * Miscellaneous utilities (not Catacomb-specific) * * (c) 2005 Straylight/Edgeware */ -/*----- Licensing notice --------------------------------------------------* +/*----- Licensing notice --------------------------------------------------* * * This file is part of the Python interface to Catacomb. * @@ -15,12 +13,12 @@ * 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. @@ -30,6 +28,13 @@ #include "catacomb-python.h" +/* #undef HAVE_LONG_LONG */ + +/*----- External values ---------------------------------------------------*/ + +static PyObject *modname = 0; +PyObject *home_module = 0; + /*----- Conversions -------------------------------------------------------*/ PyObject *getulong(unsigned long w) @@ -40,6 +45,35 @@ PyObject *getulong(unsigned long w) return (PyLong_FromUnsignedLong(w)); } +#ifndef HAVE_LONG_LONG +static PyObject *i32 = 0; +static int init_i32(void) + { if (!i32 && (i32 = PyInt_FromLong(32)) == 0) return (-1); return (0); } +#endif + +PyObject *getk64(kludge64 u) +{ +#ifdef HAVE_LONG_LONG + return (PyLong_FromUnsignedLongLong(GET64(unsigned PY_LONG_LONG, u))); +#else + PyObject *i = 0, *j = 0, *t; + PyObject *rc = 0; + + 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: + if (i) Py_DECREF(i); + if (j) Py_DECREF(j); + return (rc); +#endif +} + PyObject *getbool(int b) { if (b) RETURN_TRUE; @@ -58,6 +92,7 @@ int convulong(PyObject *o, void *pp) 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"); @@ -73,15 +108,32 @@ end: return (0); } +#ifdef HAVE_UINT64 +# define CONVu64(n) do { \ + kludge64 k; \ + uint64 t; \ + if (!convk64(o, &k)) goto end; \ + t = GET64(uint64, k); \ + if (t > MASK##n) VALERR("out of range"); \ + *p = t; \ + } while (0) +#else +# define CONVu64(n) assert(!"shouldn't be possible") +#endif + #define CONVU_(n) \ int convu##n(PyObject *o, void *pp) \ { \ unsigned long u; \ uint##n *p = pp; \ \ - if (!convulong(o, &u)) goto end; \ - if (u > MASK##n) VALERR("out of range"); \ - *p = u; \ + if (MASK##n > ULONG_MAX) \ + CONVu64(n); \ + else { \ + if (!convulong(o, &u)) goto end; \ + if (u > MASK##n) VALERR("out of range"); \ + *p = u; \ + } \ return (1); \ end: \ return (0); \ @@ -101,6 +153,42 @@ 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: + if (i) Py_DECREF(i); + return (rc); +} + int convmpw(PyObject *o, void *pp) { unsigned long u; @@ -129,8 +217,11 @@ end: int convbool(PyObject *o, void *pp) { + if (!o) VALERR("can't delete"); *(int *)pp = PyObject_IsTrue(o); return (1); +end: + return (0); } /*----- Type messing ------------------------------------------------------*/ @@ -165,16 +256,22 @@ void *newtype(PyTypeObject *metaty, 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); - PyObject_INIT(&ty->ht_type, metaty); + DISCARD(PyObject_INIT(&ty->ht_type, metaty)); Py_INCREF(metaty); return (ty); } -PyTypeObject *inittype(PyTypeObject *tyskel) +void typeready(PyTypeObject *ty) { - PyTypeObject *ty = newtype(&PyType_Type, tyskel, 0); - ty->tp_flags |= Py_TPFLAGS_HEAPTYPE; 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); } @@ -183,14 +280,14 @@ PyTypeObject *inittype(PyTypeObject *tyskel) void setconstants(PyObject *mod, const struct nameval *c) { PyObject *x; + unsigned long u; while (c->name) { - if (c->value > LONG_MAX) - x = PyLong_FromUnsignedLong(c->value); - else - x = PyInt_FromLong(c->value); - PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x); - c++; + u = c->value; + if (u <= LONG_MAX) x = PyInt_FromLong(u); + else if (c->f&CF_SIGNED) x = PyInt_FromLong(-1 - (long)(ULONG_MAX - u)); + else x = PyLong_FromUnsignedLong(u); + PyModule_AddObject(mod, (/*unconst*/ char *)c->name, x); c++; } } @@ -218,8 +315,8 @@ PyMethodDef *donemethods(void) /*----- Exceptions --------------------------------------------------------*/ -PyObject * mkexc(PyObject *mod, PyObject *base, - const char *name, PyMethodDef *mm) +PyObject *mkexc(PyObject *mod, PyObject *base, + const char *name, PyMethodDef *mm) { PyObject *nameobj = 0; PyObject *dict = 0; @@ -227,13 +324,7 @@ PyObject * mkexc(PyObject *mod, PyObject *base, PyObject *func = 0; PyObject *meth = 0; - if ((nameobj = PyString_FromFormat("%s.%s", - PyModule_GetName(mod), - name)) == 0 || - (dict = PyDict_New()) == 0 || - (exc = PyErr_NewException(PyString_AS_STRING(nameobj), - base, dict)) == 0) - goto fail; + if ((dict = PyDict_New()) == 0) goto fail; if (mm) { while (mm->ml_name) { @@ -247,6 +338,13 @@ PyObject * mkexc(PyObject *mod, PyObject *base, } } + 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); @@ -260,6 +358,101 @@ fail: goto done; } +void report_lost_exception_v(struct excinfo *exc, + const char *why, va_list ap) +{ + PyObject *hookfn = 0; + PyObject *whyobj = 0; + PyObject *obj = 0; + + /* Make sure we start out without a pending exception, or this will get + * really confusing. + */ + assert(!PyErr_Occurred()); + + /* Format the explanation. */ + if (why) whyobj = PyString_FromFormatV(why, ap); + else { whyobj = Py_None; Py_INCREF(whyobj); } + + /* Find our home module's `lostexchook' function. This won't work if + * there's no module, or the function isn't defined, or it's `None'. + */ + if (!home_module) goto sys; + hookfn = PyObject_GetAttrString(home_module, "lostexchook"); + if (hookfn == Py_None) goto sys; + else if (hookfn) ; + else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto ouch; + else { PyErr_Clear(); goto sys; } + + /* Call the hook function. */ + obj = PyObject_CallFunction(hookfn, "(OOOO)", + whyobj, exc->ty, exc->val, exc->tb); + if (!obj) goto ouch; + goto end; + + /* Something went wrong reporting the problem. */ +ouch: + PySys_WriteStderr("\n!!! FAILURE REPORTING LOST EXCEPTION\n"); + PyErr_Print(); + /* drop through... */ + + /* There was no hook, so try to do something sensible using + * `sys.excepthook'. + */ +sys: + PySys_WriteStderr("\n!!! LOST EXCEPTION: %s\n", + PyString_AS_STRING(whyobj)); + RESTORE_EXCINFO(exc); + PyErr_Print(); + /* drop through... */ + + /* Clean up afterwards. */ +end: + Py_XDECREF(hookfn); + Py_XDECREF(whyobj); + Py_XDECREF(obj); +} + +void report_lost_exception(struct excinfo *exc, const char *why, ...) +{ + va_list ap; + + va_start(ap, why); + report_lost_exception_v(exc, why, ap); + va_end(ap); +} + +void stash_exception(struct excinfo *exc, const char *why, ...) +{ + va_list ap; + struct excinfo stash; + + if (!exc->ty) + STASH_EXCINFO(exc); + else { + va_start(ap, why); + STASH_EXCINFO(&stash); + report_lost_exception_v(&stash, why, ap); + va_end(ap); + } +} + +void restore_exception(struct excinfo *exc, const char *why, ...) +{ + va_list ap; + struct excinfo stash; + + if (!PyErr_Occurred()) + RESTORE_EXCINFO(exc); + else { + va_start(ap, why); + STASH_EXCINFO(&stash); + report_lost_exception_v(exc, why, ap); + RESTORE_EXCINFO(&stash); + va_end(ap); + } +} + /*----- Generic dictionary methods ----------------------------------------*/ static PyTypeObject *itemiter_pytype, *valiter_pytype; @@ -278,7 +471,7 @@ static void iter_pydealloc(PyObject *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); @@ -311,7 +504,7 @@ static PyTypeObject itemiter_pytype_skel = { Py_TPFLAGS_BASETYPE, /* @tp_doc@ */ -"Iterates over the items of a mapping.", +"Iterates over the keys of a mapping.", 0, /* @tp_traverse@ */ 0, /* @tp_clear@ */ @@ -337,7 +530,7 @@ static PyTypeObject itemiter_pytype_skel = { 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); @@ -369,7 +562,7 @@ static PyTypeObject valiter_pytype_skel = { Py_TPFLAGS_BASETYPE, /* @tp_doc@ */ -"Iterates over the items of a mapping.", +"Iterates over the values of a mapping.", 0, /* @tp_traverse@ */ 0, /* @tp_clear@ */ @@ -403,13 +596,12 @@ PySequenceMethods gmap_pysequence = { PyMapping_HasKey, /* @sq_contains@ */ 0, /* @sq_inplace_concat@ */ 0 /* @sq_inplace_repeat@ */ -}; +}; -int gmap_pysize(PyObject *me) +Py_ssize_t gmap_pysize(PyObject *me) { PyObject *i = 0, *x = 0; - int rc = -1; - int n = 0; + Py_ssize_t rc = -1, n = 0; if ((i = PyObject_GetIter(me)) == 0) goto done; while ((x = PyIter_Next(i)) != 0) { n++; Py_DECREF(x); x = 0; } @@ -454,7 +646,7 @@ PyObject *gmapmeth_values(PyObject *me, PyObject *arg) (l = PyList_New(0)) == 0 || (i = PyObject_GetIter(me)) == 0) goto done; - while ((k = PyIter_Next(i)) != 0) { + while ((k = PyIter_Next(i)) != 0) { if ((v = PyObject_GetItem(me, k)) == 0 || PyList_Append(l, v)) err = -1; @@ -551,7 +743,7 @@ 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)) + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", def_kwlist, &k, &def)) return (0); if ((v = PyObject_GetItem(me, k)) != 0) return (v); PyErr_Clear(); @@ -562,7 +754,7 @@ PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw) { PyObject *k, *def = Py_None, *v; - if (!PyArg_ParseTupleAndKeywords(arg, kw, "OO:setdefault", + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault", def_kwlist, &k, &def)) return (0); if ((v = PyObject_GetItem(me, k)) != 0) return (v); @@ -575,14 +767,16 @@ 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)) + if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O: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); + } else if (def) { + PyErr_Clear(); + RETURN_OBJ(def); + } else + return (0); } PyObject *gmapmeth_update(PyObject *me, PyObject *arg) @@ -612,7 +806,7 @@ 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))) + (i = PyObject_GetIter(me)) == 0) goto end; if ((k = PyIter_Next(i)) == 0) { if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty"); @@ -634,13 +828,32 @@ PyMethodDef gmap_pymethods[] = { /*----- Initialization ----------------------------------------------------*/ -void util_init(void) +static PyObject *meth__set_home_module(PyObject *me, PyObject *arg) +{ + PyObject *mod; + + if (!PyArg_ParseTuple(arg, "O!:_set_home_module", &PyModule_Type, &mod)) + return (0); + Py_XDECREF(home_module); home_module = mod; Py_INCREF(home_module); + RETURN_NONE; +} + +static const PyMethodDef methods[] = { +#define METHNAME(func) meth_##func + METH (_set_home_module, "_set_home_module(MOD)") +#undef METHNAME + { 0 } +}; + +void util_pyinit(void) { + modname = PyString_FromString("catacomb"); INITTYPE(itemiter, root); INITTYPE(valiter, root); + addmethods(methods); } -void util_insert(PyObject *mod) +void util_pyinsert(PyObject *mod) { INSERT("ItemIter", itemiter_pytype); INSERT("ValueIter", valiter_pytype);