chiark / gitweb /
Add 'pyke/' from commit '6c2569879c220eeac35957cdba9a043e8a5ea9ed'
authorMark Wooding <mdw@distorted.org.uk>
Sat, 11 Apr 2020 14:22:40 +0000 (15:22 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 11 Apr 2020 14:22:40 +0000 (15:22 +0100)
git-subtree-dir: pyke
git-subtree-mainline: c76685552d4bda3d354a8237749106e4460a4270
git-subtree-split: 6c2569879c220eeac35957cdba9a043e8a5ea9ed

56 files changed:
.gitignore [new file with mode: 0644]
.links [new file with mode: 0644]
.mailmap [new file with mode: 0644]
.skelrc
MANIFEST.in [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
array.c [new file with mode: 0644]
array.h [new file with mode: 0644]
assoc.pyx [new file with mode: 0644]
atom-base.c [new file with mode: 0644]
atom.h [new file with mode: 0644]
atom.pyx [new file with mode: 0644]
bres.pyx [new file with mode: 0644]
codec.pyx [new file with mode: 0644]
codec.pyx.in [new file with mode: 0644]
conn.pyx [new file with mode: 0644]
crc32.pyx [new file with mode: 0644]
debian/.gitignore [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/rules [new file with mode: 0755]
debian/source/format [new file with mode: 0644]
defs.pxi [new file with mode: 0644]
fdutils.pyx [new file with mode: 0644]
fwatch.pyx [new file with mode: 0644]
grim.h [new file with mode: 0644]
ident.pyx [new file with mode: 0644]
lbuf.pyx [new file with mode: 0644]
mLib.pyx [new file with mode: 0644]
mapping.pyx [new file with mode: 0644]
mdup.pyx [new file with mode: 0644]
pkbuf.pyx [new file with mode: 0644]
pyke/.skelrc [new file with mode: 0644]
pyke/mapping-mLib.c [moved from mapping-mLib.c with 100% similarity]
pyke/mapping.c [moved from mapping.c with 100% similarity]
pyke/pyke-mLib.c [moved from pyke-mLib.c with 100% similarity]
pyke/pyke-mLib.h [moved from pyke-mLib.h with 100% similarity]
pyke/pyke.c [moved from pyke.c with 100% similarity]
pyke/pyke.h [moved from pyke.h with 100% similarity]
report.pyx [new file with mode: 0644]
sel-base.pyx [new file with mode: 0644]
sel-file.pyx [new file with mode: 0644]
sel-timer.pyx [new file with mode: 0644]
selbuf.pyx [new file with mode: 0644]
selpk.pyx [new file with mode: 0644]
setup.py [new file with mode: 0755]
sig.pyx [new file with mode: 0644]
str.pyx [new file with mode: 0644]
stuff.pyx [new file with mode: 0644]
sym.pyx [new file with mode: 0644]
unihash.pyx [new file with mode: 0644]
url.pyx [new file with mode: 0644]
utils.pyx [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..fd89164
--- /dev/null
@@ -0,0 +1,12 @@
+base32.pyx
+base64.pyx
+hex.pyx
+build
+MANIFEST
+dist
+mLib.c
+COPYING
+mdwsetup.py
+*.pyc
+auto-version
+pysetup.mk
diff --git a/.links b/.links
new file mode 100644 (file)
index 0000000..4e00440
--- /dev/null
+++ b/.links
@@ -0,0 +1,4 @@
+COPYING
+auto-version
+mdwsetup.py
+pysetup.mk
diff --git a/.mailmap b/.mailmap
new file mode 100644 (file)
index 0000000..c635e4a
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,2 @@
+Mark Wooding <mdw@distorted.org.uk> <mdw>
+Mark Wooding <mdw@distorted.org.uk> <mdw@metalzone.distorted.org.uk>
diff --git a/.skelrc b/.skelrc
index 21912db9242a9099b240a5dbc8f97eae3583815c..aa186c89285b489be07cfba07965b07dbcdffa3e 100644 (file)
--- a/.skelrc
+++ b/.skelrc
@@ -4,6 +4,6 @@
       (append
        '((author . "Straylight/Edgeware")
         (licence-text . "[[gpl]]")
-        (full-title . "Pyke: the Python Kit for Extensions")
-        (program . "Pyke"))
+        (full-title . "the Python interface to mLib")
+        (program . "mLib/Python"))
        skel-alist))
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644 (file)
index 0000000..391955a
--- /dev/null
@@ -0,0 +1,39 @@
+### Manifest template for mLib/Python.
+
+## Generated build machinery.
+include COPYING auto-version mdwsetup.py pysetup.mk
+
+## Basic stuff.
+include MANIFEST.in setup.py Makefile mLib.pyx
+include defs.pxi grim.h utils.pyx
+
+## buf
+include lbuf.pyx pkbuf.pyx
+
+## codec
+include codec.pyx codec.pyx.in url.pyx
+
+## hash
+include crc32.pyx unihash.pyx
+
+## sel
+include bres.pyx conn.pyx ident.pyx
+include sel-base.pyx sel-file.pyx sel-timer.pyx
+include selbuf.pyx selpk.pyx sig.pyx
+
+## struct
+include mapping.pyx
+include array.h array.c assoc.pyx atom.h atom-base.c atom.pyx sym.pyx
+
+## sys
+include stuff.pyx fdutils.pyx fwatch.pyx mdup.pyx
+
+## ui
+include report.pyx
+
+## utils
+include str.pyx
+
+## Debian.
+include debian/rules debian/control debian/changelog
+include debian/copyright debian/compat debian/source/format
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..b9f9347
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+### -*-makefile-*-
+include pysetup.mk
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..8deab3a
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+Nothing much to say
diff --git a/array.c b/array.c
new file mode 100644 (file)
index 0000000..d83f64e
--- /dev/null
+++ b/array.c
@@ -0,0 +1,656 @@
+/* -*-c-*-
+ *
+ * Double-ended arrays
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Python interface to mLib.
+ *
+ * mLib/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.
+ *
+ * mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#define PY_SSIZE_T_CLEAN
+
+#include <Python.h>
+
+#include <string.h>
+
+#include <mLib/darray.h>
+#include <mLib/dstr.h>
+#include <mLib/exc.h>
+
+#include "array.h"
+#include "grim.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+DA_DECL(obj_v, PyObject *);
+
+typedef struct da_pyobj {
+  PyObject_HEAD
+  obj_v v;
+} da_pyobj;
+#define DA_PYCHECK(o) PyObject_TypeCheck((o), &da_pytype)
+#define DA_V(o) (&((da_pyobj *)(o))->v)
+
+typedef struct daiter_pyobj {
+  PyObject_HEAD
+  PyObject *da;
+  size_t i;
+} daiter_pyobj;
+#define DAITER_DA(obj) (((daiter_pyobj *)(obj))->da)
+#define DAITER_V(obj) DA_V(DAITER_DA(obj))
+#define DAITER_I(obj) (((daiter_pyobj *)(obj))->i)
+
+static int getseq(PyObject **pseq, PyObject ***v, size_t *n)
+{
+  PyObject *seq = *pseq;
+
+  if (!seq || seq == Py_None) {
+    *v = 0;
+    *n = 0;
+    *pseq = 0;
+  } else if (DA_PYCHECK(seq)) {
+    *v = DA(DA_V(seq));
+    *n = DA_LEN(DA_V(seq));
+    Py_INCREF(seq);
+  } else if ((seq = PySequence_Fast(seq, "expected iterable")) == 0)
+    return (-1);
+  else {
+    *pseq = seq;
+    *v = PySequence_Fast_ITEMS(seq);
+    *n = PySequence_Fast_GET_SIZE(seq);
+  }
+  return (0);
+}
+
+static void range_inc(PyObject **v, PyObject **vl)
+  { while (v < vl) { Py_INCREF(*v);  v++; } }
+static void range_dec(PyObject **v, PyObject **vl)
+  { if (v) { while (v < vl) { Py_DECREF(*v);  v++; } } }
+static void range_copy(PyObject ***gv, PyObject ***gvl)
+{
+  size_t n = *gvl - *gv;
+  size_t sz = sizeof(PyObject *) * n;
+  PyObject **v;
+  if (!n) { *gv = *gvl = 0; return; }
+  v = xmalloc(sz);
+  memcpy(v, *gv, sz);
+  *gv = v;
+  *gvl = v + n;
+}
+
+static PyObject *abstract_pynew(PyTypeObject *ty,
+                               PyObject *hunoz, PyObject *hukairz)
+{
+  PyErr_SetString(PyExc_TypeError, "can't instantiate this type");
+  return (0);
+}
+
+/*----- Iterator ----------------------------------------------------------*/
+
+static PyObject *daiter_pynext(PyObject *me)
+{
+  PyObject *x;
+
+  if (DAITER_I(me) >= DA_LEN(DAITER_V(me))) return (0);
+  x = DA(DAITER_V(me))[DAITER_I(me)]; DAITER_I(me)++; RETURN_OBJ(x);
+}
+
+static void daiter_pydealloc(PyObject *me)
+  { Py_DECREF(DAITER_DA(me)); PyObject_DEL(me); }
+
+PyTypeObject daiter_pytype = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "mLib.ArrayIter",                    /* @tp_name@ */
+  sizeof(daiter_pyobj),                        /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  daiter_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@ */
+"Array iterator.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  PyObject_SelfIter,                   /* @tp_iter@ */
+  daiter_pynext,                       /* @tp_iternexr@ */
+  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@ */
+};
+
+/*----- Main array code ---------------------------------------------------*/
+
+static void da_doinsert(PyObject *me, PyObject **items, size_t n,
+                       size_t start, size_t end)
+{
+  PyObject **v, **gv, **gvl;
+  obj_v *da = DA_V(me);
+  size_t off, ne;
+
+  ne = end - start;
+  v = DA(da);
+  gv = v + start; gvl = v + end; range_copy(&gv, &gvl);
+  if (start < DA_LEN(da) - end) {
+    if (n > ne) {
+      off = n - ne;
+      DA_SHUNT(da, off);
+      DA_UNSAFE_SLIDE(da, off);
+      memmove(v, v + off, sizeof(PyObject *) * start);
+    } else {
+      off = ne - n;
+      memmove(v + off, v, sizeof(PyObject *) * start);
+      DA_UNSAFE_UNSLIDE(da, off);
+    }
+  } else {
+    if (n > ne) {
+      off = n - ne;
+      DA_ENSURE(da, off);
+      memmove(v + end + off, v + end,
+             sizeof(PyObject *) * (DA_LEN(da) - end));
+    } else {
+      off = ne - n;
+      memmove(v + end - off, v + end,
+             sizeof(PyObject *) * (DA_LEN(da) - end));
+      DA_UNSAFE_SHRINK(da, off);
+    }
+    DA_UNSAFE_EXTEND(da, off);
+  }
+
+  if (n) {
+    v = DA(da) + start;
+    memcpy(v, items, sizeof(PyObject *) * n);
+    range_inc(v, v + n);
+  }
+  if (gv) {
+    range_dec(gv, gvl);
+    xfree(gv);
+  }
+}
+
+static int da_insert(PyObject *me, PyObject *seq, int start, int end)
+{
+  PyObject **items;
+  size_t n;
+
+  if (0 > start || start > end || end > DA_LEN(DA_V(me))) {
+    PyErr_SetString(PyExc_IndexError, "bad slice");
+    return (-1);
+  }
+  if (getseq(&seq, &items, &n)) return (-1);
+  da_doinsert(me, items, n, start, end);
+  Py_XDECREF(seq);
+  return (0);
+}
+
+static PyObject *da_new(PyTypeObject *ty)
+{
+  da_pyobj *me = (da_pyobj *)ty->tp_alloc(ty, 0);
+  DA_CREATE(&me->v);
+  return ((PyObject *)me);
+}
+
+static PyObject *da_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+  { return (da_new(ty)); }
+static int da_pyinit(PyObject *me, PyObject *arg, PyObject *kw)
+{
+  PyObject *init = 0;
+  static char *kwlist[] = { "sequence", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", kwlist, &init) ||
+      (init && da_insert((PyObject *)me, init, 0, 0)))
+    return (-1);
+  return (0);
+}
+
+static void da_pydealloc(PyObject *me)
+{
+  size_t i;
+  PyObject **v = DA(DA_V(me));
+  size_t n = DA_LEN(DA_V(me));
+
+  for (i = 0; i < n; i++) Py_DECREF(v[i]);
+  DA_DESTROY(DA_V(me));
+}
+
+static Py_ssize_t da_pylength(PyObject *me)
+  { return (DA_LEN(DA_V(me))); }
+
+static int da_pytraverse(PyObject *me, visitproc proc, void *arg)
+{
+  size_t i;
+  PyObject **v = DA(DA_V(me));
+  size_t n = DA_LEN(DA_V(me));
+  int err;
+
+  for (i = 0; i < n; i++) {
+    if ((err = proc(v[i], arg)) != 0)
+      return (err);
+  }
+  return (0);
+}
+
+static int da_pyclear(PyObject *me)
+{
+  range_dec(DA(DA_V(me)), DA(DA_V(me)) + DA_LEN(DA_V(me)));
+  DA_RESET(DA_V(me));
+  return (0);
+}
+
+static PyObject *da_pyconcat(PyObject *me, PyObject *other)
+{
+  PyObject *x = da_new(&da_pytype);
+  PyObject **items = DA(DA_V(me));
+  size_t n = DA_LEN(DA_V(me));
+
+  da_doinsert(x, items, n, 0, 0);
+  if (da_insert(x, other, n, n)) {
+    Py_DECREF(x);
+    return (0);
+  }
+  return (x);
+}
+
+static PyObject *da_pyrepeat(PyObject *me, Py_ssize_t times)
+{
+  PyObject *x = da_new(&da_pytype);
+  PyObject **items = DA(DA_V(me)), **dest;
+  size_t n = DA_LEN(DA_V(me));
+  Py_ssize_t i;
+
+  DA_ENSURE(DA_V(x), n * times);
+  DA_UNSAFE_EXTEND(DA_V(x), n * times);
+  for (i = 0, dest = DA(DA_V(x)); i < times; i++, dest += n)
+    memcpy(dest, items, n * sizeof(PyObject *));
+  range_inc(DA(DA_V(x)), DA(DA_V(x)) + n * times);
+  return (x);
+}
+
+static PyObject *da_pygetitem(PyObject *me, Py_ssize_t i)
+{
+  PyObject *o;
+
+  if (i < 0 || i >= DA_LEN(DA_V(me))) {
+    PyErr_SetString(PyExc_IndexError, "index out of range");
+    return (0);
+  }
+  o = DA(DA_V(me))[i];
+  Py_INCREF(o);
+  return (o);
+}
+
+static PyObject *da_pygetslice(PyObject *me, Py_ssize_t i, Py_ssize_t j)
+{
+  PyObject *x;
+
+  if (i < 0 || j < i || DA_LEN(DA_V(me)) < j) {
+    PyErr_SetString(PyExc_IndexError, "bad slice");
+    return (0);
+  }
+  x = da_new(&da_pytype);
+  da_doinsert(x, DA(DA_V(me)) + i, j - i, 0, 0);
+  return (x);
+}
+
+static int da_pyputitem(PyObject *me, Py_ssize_t i, PyObject *x)
+{
+  PyObject **p;
+
+  if (i < 0 || i >= DA_LEN(DA_V(me))) {
+    PyErr_SetString(PyExc_IndexError, "index out of range");
+    return (-1);
+  }
+  p = DA(DA_V(me)) + i;
+  Py_DECREF(*p);
+  *p = x;
+  Py_INCREF(*p);
+  return (0);
+}
+
+static int da_pyputslice(PyObject *me, Py_ssize_t i, Py_ssize_t j,
+                        PyObject *x)
+  { return (da_insert(me, x, i, j)); }
+
+static int da_pycontainsp(PyObject *me, PyObject *x)
+{
+  PyObject **items = DA(DA_V(me));
+  size_t n = DA_LEN(DA_V(me));
+  size_t i;
+  int rc;
+
+  for (i = 0; i < n; i++) {
+    if (PyObject_Cmp(items[i], x, &rc))
+      return (-1);
+    if (rc)
+      return (1);
+  }
+  return (0);
+}
+
+static PyObject *da_pyrepr(PyObject *me)
+{
+  dstr d = DSTR_INIT;
+  PyObject *s, *rc = 0;
+  char *p;
+  Py_ssize_t n;
+  size_t i;
+
+  dstr_puts(&d, "Array([");
+  for (i = 0; i < DA_LEN(DA_V(me)); i++) {
+    if ((s = PyObject_Repr(DA(DA_V(me))[i])) == 0 ||
+       PyString_AsStringAndSize(s, &p, &n)) {
+      Py_XDECREF(s);
+      goto done;
+    }
+    if (i) dstr_puts(&d, ", ");
+    dstr_putm(&d, p, n);
+    Py_DECREF(s);
+  }
+  dstr_puts(&d, "])");
+  rc = PyString_FromStringAndSize(d.buf, d.len);
+done:
+  dstr_destroy(&d);
+  return (rc);
+}
+
+static PyObject *da_pyappend(PyObject *me, PyObject *seq)
+{
+  size_t n = DA_LEN(DA_V(me));
+  if (da_insert(me, seq, n, n)) return (0);
+  RETURN_ME;
+}
+
+static PyObject *da_pyiprepeat(PyObject *me, Py_ssize_t times)
+{
+  PyObject **items, **dest;
+  size_t n = DA_LEN(DA_V(me));
+  Py_ssize_t i;
+
+  if (times < 0) {
+    PyErr_SetString(PyExc_ValueError, "multiplier must be nonnegative");
+    return (0);
+  }
+  if (!times) {
+    items = DA(DA_V(me));
+    range_dec(items, items + n);
+    DA_RESET(DA_V(me));
+    RETURN_ME;
+  }
+  times--;
+  DA_ENSURE(DA_V(me), n * times);
+  items = DA(DA_V(me));
+  for (i = 0, dest = items + n; i < times; i++, dest += n)
+    memcpy(dest, items, n * sizeof(PyObject *));
+  range_inc(items + n, dest);
+  DA_UNSAFE_EXTEND(DA_V(me), n * times);
+  RETURN_ME;
+}
+
+static PyObject *da_pyget(PyObject *me, PyObject *index)
+{
+  if (PySlice_Check(index)) {
+    Py_ssize_t start, stop, step, len;
+    PyObject *v;
+    PyObject **ww;
+    PyObject **vv;
+
+    if (PySlice_GetIndicesEx((PySliceObject *)index, DA_LEN(DA_V(me)),
+                            &start, &stop, &step, &len))
+      return (0);
+    if (step == 1) return (da_pygetslice(me, start, stop));
+    v = da_new(&da_pytype);
+    DA_ENSURE(DA_V(v), len);
+    vv = DA(DA_V(v));
+    ww = DA(DA_V(me)) + start;
+    DA_UNSAFE_EXTEND(DA_V(v), len);
+    while (len) {
+      *vv = *ww;
+      Py_INCREF(*vv);
+      vv++; ww += step; len--;
+    }
+    return ((PyObject *)v);
+  } else {
+    int i;
+
+    if ((i = PyInt_AsLong(index)) == -1 && PyErr_Occurred()) return (0);
+    return (da_pygetitem(me, i));
+  }
+}
+
+static int da_pyput(PyObject *me, PyObject *index, PyObject *x)
+{
+  if (PySlice_Check(index)) {
+    Py_ssize_t start, stop, step, len;
+    size_t n;
+    PyObject **ww;
+    PyObject **vv;
+    PyObject **g, **gg;
+
+    if (PySlice_GetIndicesEx((PySliceObject *)index, DA_LEN(DA_V(me)),
+                            &start, &stop, &step, &len))
+      return (-1);
+    if (step == 1) return (da_insert(me, x, start, stop));
+    if (getseq(&x, &vv, &n)) return (-1);
+    if (n != len) {
+      PyErr_SetString(PyExc_ValueError, "wrong number of items");
+      Py_XDECREF(x);
+      return (-1);
+    }
+    g = gg = xmalloc(len * sizeof(PyObject *));
+    ww = DA(DA_V(me)) + start;
+    while (len) {
+      *gg++ = *ww; *ww = *vv++;
+      Py_INCREF(*ww);
+      ww += step; len--;
+    }
+    range_dec(g, gg);
+    xfree(g);
+    Py_XDECREF(x);
+    return (0);
+  } else {
+    int i;
+
+    if ((i = PyInt_AsLong(index)) == -1 && PyErr_Occurred()) return (-1);
+    return (da_pyputitem(me, i, x));
+  }
+}
+
+static PyObject *da_pyiter(PyObject *me)
+{
+  daiter_pyobj *i = PyObject_NEW(daiter_pyobj, &daiter_pytype);
+  i->da = me; Py_INCREF(me);
+  i->i = 0;
+  return ((PyObject *)i);
+}
+
+static PyObject *dameth_push(PyObject *me, PyObject *arg)
+{
+  PyObject *x;
+
+  if (!PyArg_ParseTuple(arg, "O:push", &x)) return (0);
+  Py_INCREF(x);
+  DA_PUSH(DA_V(me), x);
+  RETURN_ME;
+}
+
+static PyObject *dameth_pop(PyObject *me, PyObject *arg)
+{
+  PyObject *x = Py_None;
+
+  if (!PyArg_ParseTuple(arg, ":pop")) return (0);
+  TRY
+    x = DA_POP(DA_V(me));
+  CATCH switch (exc_type) {
+    case DAEXC_UFLOW:
+      PyErr_SetString(PyExc_ValueError, "stack underflow");
+      return (0);
+    default:
+      RETHROW;
+  } END_TRY;
+  RETURN_OBJ(x);
+}
+
+static PyObject *dameth_unshift(PyObject *me, PyObject *arg)
+{
+  PyObject *x;
+
+  if (!PyArg_ParseTuple(arg, "O:unshift", &x)) return (0);
+  Py_INCREF(x);
+  DA_UNSHIFT(DA_V(me), x);
+  RETURN_ME;
+}
+
+static PyObject *dameth_shift(PyObject *me, PyObject *arg)
+{
+  PyObject *x = Py_None;
+
+  if (!PyArg_ParseTuple(arg, ":shift")) return (0);
+  TRY
+    x = DA_SHIFT(DA_V(me));
+  CATCH switch (exc_type) {
+    case DAEXC_UFLOW:
+      PyErr_SetString(PyExc_ValueError, "stack underflow");
+      return (0);
+    default:
+      RETHROW;
+  } END_TRY;
+  RETURN_OBJ(x);
+}
+
+static PyObject *dameth_tidy(PyObject *me, PyObject *arg)
+{
+  if (!PyArg_ParseTuple(arg, ":tidy")) return (0);
+  DA_TIDY(DA_V(me));
+  RETURN_ME;
+}
+
+static PyMethodDef da_pymethods[] = {
+#define METHNAME(func) dameth_##func
+  METH (push,          "A.push(X): [A, B, ..., W], X -> [A, B, ..., W, X]")
+  METH (pop,           "A.pop() -> X: [A, B, ..., W, X] -> [A, B, ..., W]")
+  METH (unshift,       "A.unshift(X): [A, B, ..., W], X -> [X, A, ..., W]")
+  METH (shift,         "A.shift() -> X: [X, A, ..., W] -> [A, ..., W], X")
+  METH (tidy,          "A.tidy()")
+#undef METHNAME
+  { 0 }
+};
+
+static PySequenceMethods da_pysequence = {
+  da_pylength,                         /* @sq_length@ */
+  da_pyconcat,                         /* @sq_concat@ */
+  da_pyrepeat,                         /* @sq_repeat@ */
+  da_pygetitem,                                /* @sq_item@ */
+  da_pygetslice,                       /* @sq_slice@ */
+  da_pyputitem,                                /* @sq_ass_item@ */
+  da_pyputslice,                       /* @sq_ass_slice@ */
+  da_pycontainsp,                      /* @sq_contains@ */
+  da_pyappend,                         /* @sq_inplace_concat@ */
+  da_pyiprepeat                                /* @sq_inplace_repeat@ */
+};
+
+static PyMappingMethods da_pymapping = {
+  da_pylength,                         /* @mp_length@ */
+  da_pyget,                            /* @mp_subscript@ */
+  da_pyput                             /* @mp_ass_subscript@ */
+};
+
+PyTypeObject da_pytype = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "mLib.Array",                                /* @tp_name@ */
+  sizeof(da_pyobj),                    /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  da_pydealloc,                                /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  da_pyrepr,                           /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  &da_pysequence,                      /* @tp_as_sequence@ */
+  &da_pymapping,                       /* @tp_as_mapping@ */
+  0,                                   /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  &da_pyrepr,                          /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT |                 /* @tp_flags@ */
+    Py_TPFLAGS_HAVE_GC,
+
+  /* @tp_doc@ */
+"Double-ended array type.",
+
+  da_pytraverse,                       /* @tp_traverse@ */
+  da_pyclear,                          /* @tp_clear@ */
+  0,                                   /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  da_pyiter,                           /* @tp_iter@ */
+  0,                                   /* @tp_iternext@ */
+  da_pymethods,                                /* @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@ */
+  da_pyinit,                           /* @tp_init@ */
+  PyType_GenericAlloc,                 /* @tp_alloc@ */
+  da_pynew,                            /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+/*----- Initialization ----------------------------------------------------*/
+
+void da_pysetup(void)
+  { PyType_Ready(&da_pytype); PyType_Ready(&daiter_pytype); }
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/array.h b/array.h
new file mode 100644 (file)
index 0000000..56f0660
--- /dev/null
+++ b/array.h
@@ -0,0 +1,43 @@
+/* -*-c-*-
+ *
+ * Double-ended arrays
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Python interface to mLib.
+ *
+ * mLib/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.
+ *
+ * mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#include <Python.h>
+
+extern void da_pysetup(void);
+extern PyTypeObject da_pytype, daiter_pytype;
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/assoc.pyx b/assoc.pyx
new file mode 100644 (file)
index 0000000..7685dd6
--- /dev/null
+++ b/assoc.pyx
@@ -0,0 +1,82 @@
+### -*-pyrex-*-
+###
+### Association tables
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef struct _assoc_entry:
+  sym_base _b
+  PyObject *v
+
+cdef class AssocTable (Mapping):
+  cdef assoc_table _t
+  cdef int _init(me) except -1:
+    assoc_create(&me._t)
+    return 0
+  cdef void *_find(me, object key, unsigned *f) except NULL:
+    cdef void *p
+    cdef Py_ssize_t n
+    cdef _assoc_entry *e
+    cdef atom *a
+    a = ATOM_A(atom_pyintern(key))
+    PyObject_AsReadBuffer(key, <cvp *>&p, &n)
+    if f:
+      f[0] = 0
+      e = <_assoc_entry *>assoc_find(&me._t, a, PSIZEOF(e), f)
+      if not f[0]:
+        e.v = NULL
+    else:
+      e = <_assoc_entry *>assoc_find(&me._t, a, 0, NULL)
+    return <void *>e
+  cdef object _key(me, void *e):
+    return atom_pywrap(ASSOC_ATOM(e))
+  cdef object _value(me, void *e):
+    cdef _assoc_entry *ee
+    ee = <_assoc_entry *>e
+    Py_INCREF(ee.v)
+    return <object>ee.v
+  cdef void _setval(me, void *e, object val):
+    cdef _assoc_entry *ee
+    ee = <_assoc_entry *>e
+    if ee.v:
+      Py_DECREF(ee.v)
+    ee.v = <PyObject *>v
+    Py_INCREF(ee.v)
+  cdef void _del(me, void *e):
+    cdef _assoc_entry *ee
+    ee = <_assoc_entry *>e
+    if ee.v:
+      Py_DECREF(ee.v)
+    assoc_remove(&me._t, <void *>ee)
+  cdef _MapIterator _iter(me):
+    return _AssocIter(me)
+
+cdef class _AssocIter (_MapIterator):
+  cdef AssocTable t
+  cdef assoc_iter i
+  def __cinit__(me, AssocTable t not None):
+    me.t = t
+    assoc_mkiter(&me.i, &me.t._t)
+  cdef void *_next(me):
+    return assoc_next(&me.i)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/atom-base.c b/atom-base.c
new file mode 100644 (file)
index 0000000..f57eaee
--- /dev/null
@@ -0,0 +1,205 @@
+/* -*-c-*-
+ *
+ * Atom stuff
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Python interface to mLib.
+ *
+ * mLib/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.
+ *
+ * mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <Python.h>
+
+#include <mLib/atom.h>
+#include <mLib/assoc.h>
+#include <mLib/dstr.h>
+
+#include "atom.h"
+#include "grim.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct entry {
+  assoc_base _b;
+  PyObject *a;
+} entry;
+
+/*----- Static variables --------------------------------------------------*/
+
+static assoc_table obarray;
+
+/*----- Main code ---------------------------------------------------------*/
+
+PyObject *atom_pywrap(atom *a)
+{
+  entry *e;
+  unsigned f = 0;
+
+  e = assoc_find(&obarray, a, sizeof(entry), &f);
+  if (!f) {
+    atom_pyobj *ao = PyObject_NEW(atom_pyobj, &atom_pytype);
+    ao->a = a;
+    e->a = (PyObject *)ao;
+  }
+  RETURN_OBJ(e->a);
+}
+
+PyObject *atom_pyintern(PyObject *x)
+{
+  atom *a;
+  const void *p;
+  Py_ssize_t n;
+
+  if (ATOM_PYCHECK(x))
+    RETURN_OBJ(x);
+  if (x == Py_None)
+    a = atom_gensym(ATOM_GLOBAL);
+  else {
+    if (PyObject_AsReadBuffer(x, &p, &n)) return (0);
+    a = atom_nintern(ATOM_GLOBAL, p, n);
+  }
+  return (atom_pywrap(a));
+}
+
+static void atom_pydealloc(PyObject *me)
+  { fprintf(stderr, "ouch!  freeing atom\n"); abort(); }
+
+static PyObject *atom_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
+{
+  PyObject *name;
+  static char *kwlist[] = { "name", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O:new", kwlist, &name))
+    return (0);
+  return (atom_pyintern(name));
+}
+
+static PyObject *aget_name(PyObject *me, void *hunoz)
+{
+  return (PyString_FromStringAndSize(ATOM_NAME(ATOM_A(me)),
+                                    ATOM_LEN(ATOM_A(me))));
+}
+
+static PyObject *aget_internedp(PyObject *me, void *hunoz)
+{
+  PyObject *rc = (ATOM_A(me)->f & ATOMF_GENSYM) ? Py_False : Py_True;
+  RETURN_OBJ(rc);
+}
+
+static PyGetSetDef atom_pygetset[] = {
+#define GETSETNAME(op, name) a##op##_##name
+  GET  (name,                  "A.name -> NAME")
+  GET  (internedp,             "A.internedp -> BOOL")
+#undef GETSETNAME
+  { 0 }
+};
+
+static PyObject *atom_pyrichcompare(PyObject *x, PyObject *y, int op)
+{
+  PyObject *rc = 0;
+
+  switch (op) {
+    case Py_EQ: rc = (x == y) ? Py_True : Py_False; break;
+    case Py_NE: rc = (x != y) ? Py_True : Py_False; break;
+    default:
+      PyErr_SetString(PyExc_TypeError, "atoms are unordered");
+      return (0);
+  }
+  RETURN_OBJ(rc);
+}
+
+static PyObject *atom_pyrepr(PyObject *me)
+{
+  PyObject *s, *sr = 0;
+  PyObject *rc = 0;
+  char *p;
+  Py_ssize_t n;
+  dstr d = DSTR_INIT;
+
+  if ((s = aget_name(me, 0)) == 0 ||
+      (sr = PyObject_Repr(s)) == 0 ||
+      PyString_AsStringAndSize(sr, &p, &n))
+    goto done;
+  dstr_puts(&d, "Atom(");
+  dstr_putm(&d, p, n);
+  dstr_puts(&d, ")");
+  rc = PyString_FromStringAndSize(d.buf, d.len);
+done:
+  Py_XDECREF(s);
+  Py_XDECREF(sr);
+  dstr_destroy(&d);
+  return (rc);
+}
+
+static long atom_pyhash(PyObject *me)
+  { long h = ATOM_HASH(ATOM_A(me)); if (h == -1) h = -2; return (h); }
+
+PyTypeObject atom_pytype = {
+  PyObject_HEAD_INIT(0) 0,             /* Header */
+  "mLib.Atom",                         /* @tp_name@ */
+  sizeof(atom_pyobj),                  /* @tp_basicsize@ */
+  0,                                   /* @tp_itemsize@ */
+
+  atom_pydealloc,                      /* @tp_dealloc@ */
+  0,                                   /* @tp_print@ */
+  0,                                   /* @tp_getattr@ */
+  0,                                   /* @tp_setattr@ */
+  0,                                   /* @tp_compare@ */
+  atom_pyrepr,                         /* @tp_repr@ */
+  0,                                   /* @tp_as_number@ */
+  0,                                   /* @tp_as_sequence@ */
+  0,                                   /* @tp_as_mapping@ */
+  atom_pyhash,                         /* @tp_hash@ */
+  0,                                   /* @tp_call@ */
+  atom_pyrepr,                         /* @tp_str@ */
+  0,                                   /* @tp_getattro@ */
+  0,                                   /* @tp_setattro@ */
+  0,                                   /* @tp_as_buffer@ */
+  Py_TPFLAGS_DEFAULT,                  /* @tp_flags@ */
+
+  /* @tp_doc@ */
+"Atom.",
+
+  0,                                   /* @tp_traverse@ */
+  0,                                   /* @tp_clear@ */
+  atom_pyrichcompare,                  /* @tp_richcompare@ */
+  0,                                   /* @tp_weaklistoffset@ */
+  0,                                   /* @tp_iter@ */
+  0,                                   /* @tp_iternexr@ */
+  0,                                   /* @tp_methods@ */
+  0,                                   /* @tp_members@ */
+  atom_pygetset,                       /* @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@ */
+  atom_pynew,                          /* @tp_new@ */
+  0,                                   /* @tp_free@ */
+  0                                    /* @tp_is_gc@ */
+};
+
+void atom_pysetup(void)
+  { assoc_create(&obarray); PyType_Ready(&atom_pytype); }
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/atom.h b/atom.h
new file mode 100644 (file)
index 0000000..a2ac94e
--- /dev/null
+++ b/atom.h
@@ -0,0 +1,64 @@
+/* -*-c-*-
+ *
+ * Atom stuff
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Python interface to mLib.
+ *
+ * mLib/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.
+ *
+ * mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ATOM_H
+#define ATOM_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <Python.h>
+
+#include <mLib/atom.h>
+#include <mLib/assoc.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct atom_pyobj {
+  PyObject_HEAD
+  atom *a;
+} atom_pyobj;
+#define ATOM_PYCHECK(obj) PyObject_TypeCheck(obj, &atom_pytype)
+#define ATOM_A(obj) (((atom_pyobj *)(obj))->a)
+
+extern PyTypeObject atom_pytype;
+
+/*----- Functions provided ------------------------------------------------*/
+
+extern PyObject *atom_pywrap(atom *);
+extern PyObject *atom_pyintern(PyObject *);
+extern void atom_pysetup(void);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/atom.pyx b/atom.pyx
new file mode 100644 (file)
index 0000000..5c63722
--- /dev/null
+++ b/atom.pyx
@@ -0,0 +1,43 @@
+### -*-pyrex-*-
+###
+### Atom tables
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class AtomIter:
+  cdef atom_iter _i
+  def __cinit__(me):
+    atom_mkiter(&me._i, ATOM_GLOBAL)
+  def __next__(me):
+    cdef atom *a
+    a = atom_next(&me._i)
+    if not a:
+      raise StopIteration
+    return atom_pywrap(a)
+  def __iter__(me):
+    return me
+
+def atoms():
+  """atoms() -> ITER: return iterator over known atoms"""
+  return AtomIter()
+
+###----- That's all, folks --------------------------------------------------
diff --git a/bres.pyx b/bres.pyx
new file mode 100644 (file)
index 0000000..8097855
--- /dev/null
+++ b/bres.pyx
@@ -0,0 +1,140 @@
+### -*-pyrex-*-
+###
+### Background name resolution
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class SelResolve:
+  """Abstract superclass for background name resolution."""
+  cdef bres_client r
+  cdef int _activep
+  cdef _resolved
+  cdef _failed
+  def __init__(me, *hunoz, **hukairz):
+    raise TypeError, 'abstract class'
+  def __dealloc__(me):
+    if me._activep:
+      me._dead()
+      bres_abort(&me.r)
+  property activep:
+    """BR.activep: is lookup still waiting?"""
+    def __get__(me):
+      return _tobool(me._activep)
+  def kill(me):
+    """BR.kill(): cancel in-progress lookup"""
+    if not me._activep:
+      raise ValueError, 'already dead'
+    me._dead()
+    bres_abort(&me.r)
+    return me
+  cdef _dead(me):
+    me._activep = 0
+    me.dead()
+  def dead(me):
+    """BR.dead(): called when lookup completes or is cancelled"""
+    pass
+  property resolvedproc:
+    """BR.resolvedproc -> FUNC: call FUNC(NAME, ALIASES, ADDRS) when ok"""
+    def __get__(me):
+      return me._resolved
+    def __set__(me, proc):
+      me._resolved = _checkcallable(proc, 'resolved proc')
+    def __del__(me):
+      me._resolved = None
+  property failedproc:
+    """BR.failedproc -> FUNC: call FUNC() when lookup fails"""
+    def __get__(me):
+      return me._failed
+    def __set__(me, proc):
+      me._failed = _checkcallable(proc, 'failed proc')
+    def __del__(me):
+      me._failed = None
+  def resolved(me, name, aliases, addrs):
+    """BR.resolved(NAME, ALIASES, ADDRS): called when lookup completes"""
+    return _maybecall(me._resolved, (name, aliases, addrs))
+  def failed(me):
+    """BR.failed(): called when lookup fails"""
+    return _maybecall(me._failed, ())
+
+cdef class SelResolveByName (SelResolve):
+  """
+  Resolve a hostname to an IP address, asynchronously.
+
+  SelResolveByName(NAME, [resolvedproc = None], [failedproc = None])
+
+  Calls RESOLVEDPROC(NAME, ALIASES, ADDRS) on success, or FAILEDPROC() on
+  failure.
+  """
+  def __cinit__(me, char *name, resolvedproc = None, failedproc = None,
+              *hunoz, **hukairz):
+    me._resolved = _checkcallable(resolvedproc, 'resolved proc')
+    me._failed = _checkcallable(failedproc, 'failed proc')
+    me._activep = 1
+    bres_byname(&me.r, name, _resfunc, <void *>me)
+  def __init__(me, name, resolvedproc = None, failedproc = None):
+    pass
+
+cdef class SelResolveByAddr (SelResolve):
+  """
+  Resolve an IPv4 address to a hostname, asynchronously.
+
+  SelResolveByAddr(ADDR, [resolvedproc = None], [failedproc = None])
+
+  Calls RESOLVEDPROC(NAME, ALIASES, ADDRS) on success, or FAILEDPROC() on
+  failure.
+  """
+  def __cinit__(me, char *addr, resolvedproc = None, failedproc = None,
+              *hunoz, **hukairz):
+    cdef in_addr ia
+    if not inet_aton(addr, &ia):
+      raise TypeError, 'bad IP address'
+    me._resolved = _checkcallable(resolvedproc, 'resolved proc')
+    me._failed = _checkcallable(failedproc, 'failed proc')
+    me._activep = 1
+    bres_byaddr(&me.r, ia, _resfunc, <void *>me)
+  def __init__(me, addr, resolvedproc = None, failedproc = None):
+    pass
+
+cdef void _resfunc(hostent *h, void *arg):
+  cdef SelResolve r
+  cdef int i
+  r = <SelResolve>arg
+  r._dead()
+  if h is NULL:
+    r.failed()
+  else:
+    alias = []
+    addr = []
+    i = 0
+    while h.h_aliases[i]:
+      alias.append(h.h_aliases[i])
+      i = i + 1
+    i = 0
+    while h.h_addr_list[i]:
+      addr.append(inet_ntoa((<in_addr *>h.h_addr_list[i])[0]))
+      i = i + 1
+    r.resolved(h.h_name, alias, addr)
+
+bres_exec(NULL)
+bres_init(&_sel)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/codec.pyx b/codec.pyx
new file mode 100644 (file)
index 0000000..1274afd
--- /dev/null
+++ b/codec.pyx
@@ -0,0 +1,286 @@
+### -*-pyrex-*-
+###
+### Generic encoder/decoder
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### Base classes.
+
+cdef extern from 'mLib/codec.h':
+
+  ctypedef struct codec
+
+  ctypedef struct codec_class:
+    char *name
+    codec *(*encoder)(unsigned f, char *ind, unsigned max)
+    codec *(*decoder)(unsigned f)
+
+  ctypedef struct codec_ops:
+    codec_class *c
+    int (*code)(codec *c, void *p, size_t, dstr *d)
+    void (*destroy)(codec *c)
+  ctypedef struct codec:
+    codec_ops *ops
+
+  enum:
+    CDCF_LOWERC
+    CDCF_IGNCASE
+    CDCF_NOEQPAD
+    CDCF_IGNEQPAD
+    CDCF_IGNEQMID
+    CDCF_IGNZPAD
+    CDCF_IGNNEWL
+    CDCF_IGNSPC
+    CDCF_IGNINVCH
+    CDCF_IGNJUNK
+
+  enum:
+    CDCERR_OK
+    CDCERR_INVCH
+    CDCERR_INVEQPAD
+    CDCERR_INVZPAD
+
+  char *_codec_strerror "codec_strerror"(int err)
+
+class CDCF:
+  LOWERC = CDCF_LOWERC
+  IGNCASE = CDCF_IGNCASE
+  NOEQPAD = CDCF_NOEQPAD
+  IGNEQPAD = CDCF_IGNEQPAD
+  IGNEQMID = CDCF_IGNEQMID
+  IGNZPAD = CDCF_IGNZPAD
+  IGNNEWL = CDCF_IGNNEWL
+  IGNSPC = CDCF_IGNSPC
+  IGNINVCH = CDCF_IGNINVCH
+  IGNJUNK = CDCF_IGNJUNK
+
+class CDCERR:
+  OK = CDCERR_OK
+  INVCH = CDCERR_INVCH
+  INVEQPAD = CDCERR_INVEQPAD
+  INVZPAD = CDCERR_INVZPAD
+
+class CodecError (Exception):
+  """
+  Exception from decoding operation.
+
+  Attributes: err = CDCERR.* code, msg = message string
+  """
+  def __init__(me, err):
+    me.err = err
+    me.msg = _codec_strerror(err)
+  def __str__(me):
+    return me.msg
+
+def codec_strerror(err):
+  """codec_strerror(ERR) -> STR: message for CDCERR.* code"""
+  return _codec_strerror(err)
+
+cdef int code(codec *c, void *p, size_t len, dstr *d) except -1:
+  cdef int err
+  err = c.ops.code(c, p, len, d)
+  if err:
+    raise CodecError(err)
+  return 0
+
+cdef class _BaseCodec:
+  """Abstract superclass for codecs."""
+  cdef codec *c
+  def __cinit__(me, *hunoz, **hukairz):
+    me.c = NULL
+  def __init__(me, *hunoz, **hukairz):
+    raise TypeError, 'abstract class'
+  def __dealloc__(me):
+    if me.c is not NULL:
+      me.c.ops.destroy(me.c)
+  cdef code(me, text, int finishp):
+    cdef void *p
+    cdef Py_ssize_t len
+    cdef dstr d
+    cdef int err
+    if me.c is NULL:
+      raise ValueError, 'Encoding finished'
+    DCREATE(&d)
+    try:
+      PyObject_AsReadBuffer(text, <cvp *>&p, &len)
+      code(me.c, p, len, &d)
+      if finishp:
+        code(me.c, NULL, 0, &d)
+        me.c.ops.destroy(me.c)
+        me.c = NULL
+      return PyString_FromStringAndSize(d.buf, d.len)
+    finally:
+      dstr_destroy(&d)
+  def done(me):
+    """C.done() -> OUT: final output"""
+    me.code('', True)
+
+cdef class _BaseEncoder (_BaseCodec):
+  def encode(me, text, finishp = False):
+    """C.encode(IN, [finishp = False]) -> OUT: continue/finish encoding"""
+    return me.code(text, finishp)
+
+cdef class _BaseDecoder (_BaseCodec):
+  def decode(me, text, finishp = False):
+    """C.decode(IN, [finishp = False]) -> OUT: continue/finish decoding"""
+    return me.code(text, finishp)
+
+###--------------------------------------------------------------------------
+### Base64.
+
+cdef extern from 'mLib/base64.h':
+  codec_class base64_class
+  codec_class file64_class
+  codec_class base64url_class
+
+cdef class Base64Encoder (_BaseEncoder):
+  """
+  Base64Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base64 encoder.
+  """
+  def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK):
+    me.c = base64_class.encoder(flags, indent, maxline)
+
+cdef class Base64Decoder (_BaseDecoder):
+  """
+  Base64Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base64 decoder.
+  """
+  def __init__(me, flags = CDCF_IGNJUNK):
+    me.c = base64_class.decoder(flags)
+
+cdef class File64Encoder (_BaseEncoder):
+  """
+  File64Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base64 encoder, using `%' instead of `/', so encoded strings are safe as
+  filenames.
+  """
+  def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK):
+    me.c = file64_class.encoder(flags, indent, maxline)
+
+cdef class File64Decoder (_BaseDecoder):
+  """
+  File64Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base64 encoder, using `%' instead of `/', so encoded strings are safe as
+  filenames.
+  """
+  def __init__(me, flags = CDCF_IGNJUNK):
+    me.c = file64_class.decoder(flags)
+
+cdef class Base64URLEncoder (_BaseEncoder):
+  """
+  Base64URLEncoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base64 encoder, using `-' and `_' instead of `+' and `/', so encoded
+  strings are safe as URL components.
+  """
+  def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK):
+    me.c = base64url_class.encoder(flags, indent, maxline)
+
+cdef class Base64URLDecoder (_BaseDecoder):
+  """
+  Base64URLDecoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base64 decoder, using `-' and `_' instead of `+' and `/', so encoded
+  strings are safe as URL components.
+  """
+  def __init__(me, flags = CDCF_IGNJUNK):
+    me.c = base64url_class.decoder(flags)
+
+###--------------------------------------------------------------------------
+### Base32.
+
+cdef extern from 'mLib/base32.h':
+  codec_class base32_class
+  codec_class base32hex_class
+
+cdef class Base32Encoder (_BaseEncoder):
+  """
+  Base32Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base32 encoder.
+  """
+  def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK):
+    me.c = base32_class.encoder(flags, indent, maxline)
+
+cdef class Base32Decoder (_BaseDecoder):
+  """
+  Base32Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base32 decoder.
+  """
+  def __init__(me, flags = CDCF_IGNJUNK):
+    me.c = base32_class.decoder(flags)
+
+cdef class Base32HexEncoder (_BaseEncoder):
+  """
+  Base32Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base32 encoder, using digits and letters in ascending order, rather than
+  avoiding digits which visually resemble letters.
+  """
+  def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK):
+    me.c = base32hex_class.encoder(flags, indent, maxline)
+
+cdef class Base32HexDecoder (_BaseDecoder):
+  """
+  Base32Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK])
+
+  Base32 decoder, using digits and letters in ascending order, rather than
+  avoiding digits which visually resemble letters.
+  """
+  def __init__(me, flags = CDCF_IGNJUNK):
+    me.c = base32hex_class.decoder(flags)
+
+###--------------------------------------------------------------------------
+### Hex.
+
+cdef extern from 'mLib/hex.h':
+  codec_class hex_class
+
+cdef class HexEncoder (_BaseEncoder):
+  """
+  HexEncoder([indent = '\\n'], [maxline = 72],
+             [flags = CDCF.IGNJUNK | CDCF.LOWERC])
+
+  Hexadecimal encoder.
+  """
+  def __init__(me, indent = '\n', maxline = 72,
+               flags = CDCF_IGNJUNK | CDCF_LOWERC):
+    me.c = hex_class.encoder(flags, indent, maxline)
+
+cdef class HexDecoder (_BaseDecoder):
+  """
+  HexDecoder([indent = '\\n'], [maxline = 72],
+             [flags = CDCF.IGNJUNK | CDCF.LOWERC])
+
+  Hexadecimal decoder.
+  """
+  def __init__(me, flags = CDCF_IGNJUNK | CDCF_LOWERC):
+    me.c = hex_class.decoder(flags)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/codec.pyx.in b/codec.pyx.in
new file mode 100644 (file)
index 0000000..3da1b83
--- /dev/null
@@ -0,0 +1,136 @@
+### -*-pyrex-*-
+###
+### Generic encoder/decoder
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef extern from 'mLib/%PREFIX%.h':
+  ctypedef struct %PREFIX%_ctx:
+    char *indent
+    int maxline
+  void _%PREFIX%_init "%PREFIX%_init"(%PREFIX%_ctx *b)
+  void _%PREFIX%_encode "%PREFIX%_encode"(%PREFIX%_ctx *b,
+                                          void *p, size_t sz, dstr *d)
+  void _%PREFIX%_decode"%PREFIX%_decode"(%PREFIX%_ctx *b,
+                                         void *p, size_t sz, dstr *d)
+
+cdef class %CLASS%Encode:
+  """
+  %CLASS%([indent = '\\n'], [maxline = 72])
+
+  Obsolete %CLASS% encoder.
+  """
+  cdef %PREFIX%_ctx ctx
+  def __cinit__(me, *hunoz, **hukairz):
+    _%PREFIX%_init(&me.ctx)
+    me.ctx.indent = NULL
+  def __init__(me, indent = '\n', maxline = 72):
+    if me.ctx.indent:
+      xfree(<void *>me.ctx.indent)
+    me.ctx.indent = xstrdup(indent)
+    me.ctx.maxline = maxline
+  def __dealloc__(me):
+    if me.ctx.indent:
+      xfree(<void *>me.ctx.indent)
+  property indent:
+    """E.indent -> INT: indent level for new lines"""
+    def __get__(me):
+      return me.ctx.indent
+    def __set__(me, indent):
+      if me.ctx.indent:
+        xfree(<void *>me.ctx.indent)
+      me.ctx.indent = xstrdup(indent)
+  property maxline:
+    """E.maxline -> INT: maximum length of line, or 0 to prevent splitting"""
+    def __get__(me):
+      return me.ctx.maxline
+    def __set__(me, maxline):
+      me.ctx.maxline = maxline
+  def encode(me, text):
+    """E.encode(IN) -> OUT: continue encoding"""
+    cdef void *p
+    cdef Py_ssize_t len
+    cdef dstr d
+    DCREATE(&d)
+    try:
+      PyObject_AsReadBuffer(text, <cvp *>&p, &len)
+      _%PREFIX%_encode(&me.ctx, p, len, &d)
+      rc = PyString_FromStringAndSize(d.buf, d.len)
+    finally:
+      dstr_destroy(&d)
+    return rc
+  def done(me):
+    """E.done() -> OUT: finish encoding, returning final output"""
+    cdef dstr d
+    DCREATE(&d)
+    try:
+      _%PREFIX%_encode(&me.ctx, NULL, 0, &d)
+      rc = PyString_FromStringAndSize(d.buf, d.len)
+    finally:
+      dstr_destroy(&d)
+    return rc
+
+def %PREFIX%_encode(text, *arg, **kw):
+  """%PREFIX%_encode(IN, [ARGS...]) -> OUT: %CLASS%-encode the string IN"""
+  e = %CLASS%Encode(*arg, **kw)
+  return e.encode(text) + e.done()
+
+cdef class %CLASS%Decode:
+  """
+  %CLASS%()
+
+  Obsolete %CLASS% decoder.
+  """
+  cdef %PREFIX%_ctx ctx
+  def __cinit__(me, *hunoz, **hukairz):
+    _%PREFIX%_init(&me.ctx)
+    me.ctx.indent = NULL
+  def decode(me, text):
+    """D.encode(IN) -> OUT: continue decoding"""
+    cdef void *p
+    cdef Py_ssize_t len
+    cdef dstr d
+    DCREATE(&d)
+    try:
+      PyObject_AsReadBuffer(text, <cvp *>&p, &len)
+      _%PREFIX%_decode(&me.ctx, p, len, &d)
+      rc = PyString_FromStringAndSize(d.buf, d.len)
+    finally:
+      dstr_destroy(&d)
+    return rc
+  def done(me):
+    """D.done() -> OUT: finish decoding, returning final output"""
+    cdef dstr d
+    DCREATE(&d)
+    try:
+      _%PREFIX%_decode(&me.ctx, NULL, 0, &d)
+      rc = PyString_FromStringAndSize(d.buf, d.len)
+    finally:
+      dstr_destroy(&d)
+    return rc
+
+def %PREFIX%_decode(text, *arg, **kw):
+  """%PREFIX%_decode(IN) -> OUT: %CLASS%-decode the string IN"""
+  d = %CLASS%Decode(*arg, **kw)
+  return d.decode(text) + d.done()
+
+###----- That's all, folks --------------------------------------------------
diff --git a/conn.pyx b/conn.pyx
new file mode 100644 (file)
index 0000000..2f5d493
--- /dev/null
+++ b/conn.pyx
@@ -0,0 +1,102 @@
+### -*-pyrex-*-
+###
+### Non-blocking connections
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice -----------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class SelConnect:
+  """
+  SelConnect(SK, [connectedproc = FUNC], [errorproc = FUNC])
+
+  When socket SK connects, call CONNECTEDPROC(); if connection fails, call
+  ERRORPROC(ERRNO, MESSAGE).
+
+  Attributes: C.socket, C.activep, C.connectedproc, C.errorproc.
+  """
+  cdef conn c
+  cdef int _activep
+  cdef readonly object socket
+  cdef _connected
+  cdef _error
+  def __cinit__(me, sk, connectedproc = None, errorproc = None,
+              *hunoz, **hukairz):
+    conn_fd(&me.c, &_sel, sk.fileno(), _connfunc, <void *>me)
+    me._activep = 1
+    me.socket = sk
+    me._connected = _checkcallable(connectedproc, 'connected proc')
+    me._error = _checkcallable(errorproc, 'error proc')
+  def __dealloc__(me):
+    if me._activep:
+      conn_kill(&me.c)
+  property activep:
+    """C.activep -> BOOL: is connection still in progress?"""
+    def __get__(me):
+      return _tobool(me._activep)
+  property connectedproc:
+    """C.connectedproc -> FUNC: call FUNC() when connection completes"""
+    def __get__(me):
+      return me._connected
+    def __set__(me, proc):
+      me._connected = _checkcallable(proc, 'connected proc')
+    def __del__(me):
+      me._connected = None
+  property errorproc:
+    """
+    C.errorproc -> FUNC: call FUNC(ERRNO, MSG) if connection fails
+    """
+    def __get__(me):
+      return me._error
+    def __set__(me, proc):
+      me._error = _checkcallable(proc, 'error proc')
+    def __del__(me):
+      me._error = None
+  def kill(me):
+    """C.kill(): give up on connection"""
+    if not me._activep:
+      raise ValueError, 'already dead'
+    conn_kill(&me.c);
+    me._dead()
+    return me
+  cdef _dead(me):
+    me._activep = 0
+    me.dead()
+  def dead(me):
+    """C.dead(): called when connection completes or fails"""
+    pass
+  def connected(me):
+    """C.connected(): called when connection completes successfully"""
+    return _maybecall(me._connected, ())
+  def error(me, errno, strerror):
+    """C.error(ERRNO, MSG): called when connection fails"""
+    return _maybecall(me._error, ())
+
+cdef void _connfunc(int fd, void *arg):
+  cdef SelConnect c
+  c = <SelConnect>arg
+  c._dead()
+  if fd == -1:
+    c.socket = None
+    c.error(errno, strerror(errno))
+  else:
+    c.connected()
+
+###----- That's all, folks --------------------------------------------------
diff --git a/crc32.pyx b/crc32.pyx
new file mode 100644 (file)
index 0000000..f4072a0
--- /dev/null
+++ b/crc32.pyx
@@ -0,0 +1,53 @@
+### -*-pyrex-*-
+###
+### Cyclic redundancy checker
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class CRC32:
+  """CRC32(): calculate CRC32 of a stream"""
+  cdef uint32 _a
+  def __cinit__(me, *hunoz, **hukairz):
+    me._a = 0
+  def __init__(me):
+    pass
+  def chunk(me, data):
+    """C.chunk(STR): process another chunk of input"""
+    cdef void *p
+    cdef Py_ssize_t n
+    PyObject_AsReadBuffer(data, <cvp *>&p, &n)
+    me._a = c_crc32(me._a, p, n)
+    return me
+  def done(me):
+    """C.done() -> INT: return CRC of data"""
+    return _u32(me._a)
+
+def crc32(data):
+  """crc32(STR) -> INT"""
+  cdef void *p
+  cdef Py_ssize_t n
+  cdef uint32 c
+  PyObject_AsReadBuffer(data, <cvp *>&p, &n)
+  c = c_crc32(0, p, n)
+  return _u32(c)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/debian/.gitignore b/debian/.gitignore
new file mode 100644 (file)
index 0000000..19ca0da
--- /dev/null
@@ -0,0 +1,7 @@
+files
+tmp
+substvars
+*.substvars
+*.debhelper
+*.log
+python-mlib
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..be7bd23
--- /dev/null
@@ -0,0 +1,60 @@
+mlib-python (1.1.1) experimental; urgency=medium
+
+  * Work around Pyrex bugs in exception handling (notably in _getfd).
+  * Fix variable-name typos in `fdflags', which has obviously been wrong
+    forever.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sun, 10 Sep 2017 11:31:09 +0100
+
+mlib-python (1.1.0) experimental; urgency=medium
+
+  * Lots of fixing for 64-bit targets.
+  * Add (rather terse) docstrings.
+  * Switch to `dh_python2' for packaging, to eliminate dependency on
+    `python-central'.
+  * Various bug fixes.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Wed, 16 Aug 2017 14:26:18 +0100
+
+mlib-python (1.0.5) experimental; urgency=low
+
+  * Fix Debian packaging.
+  * Support CDCF_IGNSPC flag in binary codecs.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Fri, 19 Feb 2016 08:52:39 +0000
+
+mlib-python (1.0.4) experimental; urgency=low
+
+  * Fix distribution arrangements.  Apparently the release tarballs have
+    been hopelessly broken for ages and nobody noticed.
+  * Interface to newer binary encoding machinery.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Tue, 18 Jun 2013 09:53:24 +0100
+
+mlib-python (1.0.3) experimental; urgency=low
+
+  * Update for Python 2.7.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Wed, 09 Jan 2013 02:18:03 +0000
+
+mlib-python (1.0.2) experimental; urgency=low
+
+  * Fix some stupid bugs the compiler had been warning about for ages (but
+    got buried in the dross about Pyrex's code generator.
+  * Update __new__ methods to __cinit__ as prompted by Pyrex compiler.
+  * Overhaul Python build system.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sun, 04 Oct 2009 14:15:04 +0100
+
+mlib-python (1.0.1) experimental; urgency=low
+
+  * Switch to CDBS and support Python 2.6
+  * Support (glorious!) mdup function.
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sun, 04 Oct 2009 00:32:29 +0100
+
+mlib-python (1.0.0) experimental; urgency=low
+
+  * Debianization!
+
+ -- Mark Wooding <mdw@distorted.org.uk>  Sun, 04 Oct 2009 00:31:21 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..ec63514
--- /dev/null
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..335e071
--- /dev/null
@@ -0,0 +1,29 @@
+Source: mlib-python
+Section: python
+Priority: extra
+XS-Python-Version: >= 2.6, << 2.8
+Build-Depends: debhelper (>= 9), dh-python, pkg-config,
+       python (>= 2.6.6-3~), python-all-dev, python-pyrex,
+       mlib-dev (>= 2.2.2.1)
+Maintainer: Mark Wooding <mdw@distorted.org.uk>
+Standards-Version: 3.8.0
+
+Package: python-mlib
+Architecture: any
+XB-Python-Version: ${python:Versions}
+Depends: ${shlibs:Depends}, ${python:Depends}
+Provides: ${python:Provides}
+Description: A library of miscellaneous stuff
+ The mLib library provides various handy utilities, including
+   * yet another options parser, like GNU getopt but more so;
+   * a simple but efficient universal hashing family;
+   * a suite for writing event-driven select-based servers;
+   * a simple exception-handling system, based on longjmp;
+   * dynamically resizing strings and arrays;
+   * a resizing hashtable;
+   * base64 and hex encoding and decoding; and
+   * a simple background DNS resolver.
+ .
+ Not all of the features of mLib are available (or, indeed, very useful) in
+ Python.  For example, Python has its own exception system, and different
+ ideas about how strings work.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..775e274
--- /dev/null
@@ -0,0 +1,29 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Copyright: 2005--2010, 2013, 2017 Straylight/Edgeware
+Upstream-Name: mLib-python
+Upstream-Contact: Mark Wooding <mdw@distorted.org.uk>
+Source: https://ftp.distorted.org.uk/pub/mdw/
+License: GPL-2.0+
+
+Files: *
+Copyright: 2005--2010, 2013, 2017 Straylight/Edgeware
+License: GPL-2.0+
+
+License: GPL-2.0+
+ mLib-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.
+ .
+ mLib-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 a copy of the GNU General Public License in
+ /usr/share/common-licenses/GPL; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+ .
+ On Debian systems, the full text of the GNU General Public License
+ version 2 can be found in the file `/usr/share/common-licenses/LGPL-2'.
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..f2ab49c
--- /dev/null
@@ -0,0 +1,2 @@
+#! /usr/bin/make -f
+%:; dh $@ --parallel -Bdebian/build --with=python2
diff --git a/debian/source/format b/debian/source/format
new file mode 100644 (file)
index 0000000..d3827e7
--- /dev/null
@@ -0,0 +1 @@
+1.0
diff --git a/defs.pxi b/defs.pxi
new file mode 100644 (file)
index 0000000..b14b855
--- /dev/null
+++ b/defs.pxi
@@ -0,0 +1,444 @@
+### -*-pyrex-*-
+###
+### Basic definitions, used all over
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### C library.
+
+cdef extern from 'errno.h':
+  int errno
+  enum:
+    EINTR
+    EAGAIN
+cdef extern from 'limits.h':
+  enum:
+    LONG_MAX
+cdef extern from 'math.h':
+  double modf(double x, double *i)
+cdef extern from 'stddef.h':
+  ctypedef int size_t
+cdef extern from 'string.h':
+  void memcpy(void *p, void *q, size_t n)
+  char *strerror(int err)
+  size_t strlen(char *p)
+
+###--------------------------------------------------------------------------
+### Unix interface.
+
+cdef extern from 'sys/types.h':
+  pass
+cdef extern from 'sys/time.h':
+  struct timeval:
+    int tv_sec
+    int tv_usec
+
+cdef extern from 'sys/socket.h':
+  ctypedef int socklen_t
+  struct sockaddr:
+    int sa_family
+  enum:
+    AF_INET
+  int getsockname(int fd, sockaddr *pa, size_t *psz)
+  int getpeername(int fd, sockaddr *pa, size_t *psz)
+cdef extern from 'arpa/inet.h':
+  struct in_addr:
+    int s_addr
+  int inet_aton(char *rp, in_addr *ia)
+  char *inet_ntoa(in_addr ia)
+cdef extern from 'netinet/in.h':
+  struct sockaddr_in:
+    int sin_family
+    in_addr sin_addr
+    int sin_port
+  int htons(int x)
+  int htonl(int x)
+  int ntohs(int x)
+  int ntohl(int x)
+cdef extern from 'netdb.h':
+  struct hostent:
+    char *h_name
+    char **h_aliases
+    int h_addrtype
+    int h_length
+    char **h_addr_list
+    char *h_addr
+  int h_errno
+
+###--------------------------------------------------------------------------
+### Python.
+
+cdef extern from 'Python.h':
+
+  ctypedef struct PyObject:
+    pass
+  ctypedef struct PyTypeObject:
+    pass
+
+  object PyString_FromStringAndSize(char *p, Py_ssize_t len)
+  int PyString_AsStringAndSize(obj, char **p, Py_ssize_t *len) except -1
+  int PyObject_AsReadBuffer(obj, void **buf, Py_ssize_t *len) except -1
+  object PyInt_FromLong(long i)
+  object PyLong_FromUnsignedLong(unsigned long i)
+  char *PyString_AS_STRING(string)
+  int _PyString_Resize(PyObject **string, int size) except -1
+  void PyErr_Clear()
+
+  void Py_INCREF(PyObject *obj)
+  void Py_DECREF(PyObject *obj)
+
+###--------------------------------------------------------------------------
+### mLib basic stuff.
+
+cdef extern from 'mLib/alloc.h':
+  char *xstrdup(char *p)
+  void *xmalloc(size_t sz)
+  void xfree(void *p)
+
+cdef extern from 'mLib/bits.h':
+  ctypedef int uint32
+
+cdef extern from 'mLib/dstr.h':
+  ctypedef struct dstr:
+    char *buf
+    int len
+  void DCREATE(dstr *d)
+  void dstr_destroy(dstr *d)
+
+###--------------------------------------------------------------------------
+### CRC32.
+
+cdef extern from 'mLib/crc32.h':
+  uint32 c_crc32 "crc32" (uint32 a, void *p, int sz)
+
+###--------------------------------------------------------------------------
+### Universal hashing.
+
+cdef extern from 'mLib/unihash.h':
+  ctypedef struct unihash_info:
+    pass
+  void unihash_setkey(unihash_info *i, uint32 k)
+  uint32 UNIHASH_INIT(unihash_info *i)
+  uint32 unihash_hash(unihash_info *i, uint32 a, void *p, int sz)
+  unihash_info unihash_global
+
+###--------------------------------------------------------------------------
+### Symbol tables.
+
+cdef extern from 'mLib/sym.h':
+  ctypedef struct sym_table:
+    pass
+  ctypedef struct sym_base:
+    pass
+  ctypedef struct sym_iter:
+    pass
+  void sym_create(sym_table *t)
+  void sym_destroy(sym_table *t)
+  void *sym_find(sym_table *t, char *n, long l, int sz, unsigned *f)
+  void sym_remove(sym_table *t, void *b)
+  char *SYM_NAME(void *b)
+  int SYM_LEN(void *b)
+  void sym_mkiter(sym_iter *i, sym_table *t)
+  void *sym_next(sym_iter *i)
+
+###--------------------------------------------------------------------------
+### String utilities.
+
+cdef extern from 'mLib/str.h':
+  enum:
+    STRF_QUOTE
+    STRF_PREFIX
+  char *str_qword(char **pp, unsigned f)
+  size_t str_qsplit(char *p, char **v, size_t c, char **rest, unsigned f)
+  int str_matchx(char *p, char *s, unsigned f)
+  void str_sanitize(char *d, char *p, size_t sz)
+
+cdef extern from 'mLib/versioncmp.h':
+  int _versioncmp "versioncmp"(char *va, char *vb)
+
+###--------------------------------------------------------------------------
+### Form-urlencoding functions.
+
+cdef extern from 'mLib/url.h':
+  struct url_ectx:
+    unsigned f
+  struct url_dctx:
+    char *p
+    unsigned f
+  enum:
+    URLF_STRICT
+    URLF_LAX
+    URLF_SEMI
+  void url_initenc(url_ectx *ctx)
+  void url_enc(url_ectx *ctx, dstr *d, char *name, char *value)
+  void url_initdec(url_dctx *ctx, char *p)
+  int url_dec(url_dctx *ctx, dstr *n, dstr *v)
+
+###--------------------------------------------------------------------------
+### Atom stuff.
+
+## Atoms.
+##
+## Partly written in `real' C.
+cdef extern from 'atom.h':
+  ctypedef struct atom:
+    pass
+  ctypedef struct atom_iter:
+    pass
+  ctypedef struct atom_table:
+    pass
+  atom_table *ATOM_GLOBAL
+  void atom_mkiter(atom_iter *i, atom_table *t)
+  atom *atom_next(atom_iter *)
+  void atom_pysetup()
+  atom_pywrap(atom *a)
+  atom_pyintern(obj)
+  atom *ATOM_A(obj)
+  PyTypeObject atom_pytype
+
+## Association tables.
+cdef extern from 'mLib/assoc.h':
+  ctypedef struct assoc_table:
+    pass
+  ctypedef struct assoc_base:
+    pass
+  ctypedef struct assoc_iter:
+    pass
+  void assoc_create(assoc_table *t)
+  void assoc_destroy(assoc_table *t)
+  void *assoc_find(assoc_table *t, atom *a, int sz, unsigned *f)
+  void assoc_remove(assoc_table *t, void *b)
+  atom *ASSOC_ATOM(void *b)
+  void assoc_mkiter(assoc_iter *i, assoc_table *t)
+  void *assoc_next(assoc_iter *i)
+
+###--------------------------------------------------------------------------
+### Double-ended arrays.
+
+cdef extern from 'array.h':
+  void da_pysetup()
+  PyTypeObject da_pytype
+  PyTypeObject daiter_pytype
+
+###--------------------------------------------------------------------------
+### Line buffer.
+
+cdef extern from 'mLib/lbuf.h':
+  cdef struct lbuf:
+    int f
+    int delim
+    size_t sz
+  enum:
+    LBUF_ENABLE
+    _LBUF_CRLF "LBUF_CRLF"
+    _LBUF_STRICTCRLF "LBUF_STRICTCRLF"
+  void lbuf_flush(lbuf *b, char *p, size_t len)
+  void lbuf_close(lbuf *b)
+  size_t lbuf_free(lbuf *b, char **p)
+  void lbuf_setsize(lbuf *b, size_t sz)
+  void lbuf_enable(lbuf *b)
+  void lbuf_disable(lbuf *b)
+  void lbuf_init(lbuf *b,
+                 void (*func)(char *s, size_t len, void *arg), void *arg)
+  void lbuf_destroy(lbuf *b)
+
+###--------------------------------------------------------------------------
+### Packet buffer.
+
+cdef extern from 'mLib/pkbuf.h':
+  ctypedef struct pkbuf:
+    int f
+    int want
+  enum:
+    PKBUF_ENABLE
+  void pkbuf_flush(pkbuf *pk, unsigned char *p, size_t len)
+  void pkbuf_close(pkbuf *pk)
+  size_t pkbuf_free(pkbuf *pk, unsigned char **p)
+  void pkbuf_want(pkbuf *pk, size_t sz)
+  void pkbuf_init(pkbuf *b,
+                  void (*func)(unsigned char *s, size_t len,
+                               pkbuf *pk, size_t *keep, void *arg),
+                  void *arg)
+  void pkbuf_destroy(pkbuf *b)
+
+###--------------------------------------------------------------------------
+### Select stuff.
+
+## Basics.
+cdef extern from 'mLib/sel.h':
+  ctypedef struct sel_state:
+    pass
+  ctypedef struct sel_file:
+    int fd
+  ctypedef struct sel_timer:
+    pass
+  enum:
+    _SEL_READ "SEL_READ"
+    _SEL_WRITE "SEL_WRITE"
+    _SEL_EXC "SEL_EXC"
+  void sel_init(sel_state *s)
+  void sel_initfile(sel_state *s, sel_file *f, int fd, unsigned mode,
+                    void (*func)(int fd, unsigned mode, void *arg),
+                    void *arg)
+  void sel_force(sel_file *f)
+  void sel_addfile(sel_file *f)
+  void sel_rmfile(sel_file *f)
+  void sel_addtimer(sel_state *s, sel_timer *t, timeval *tv,
+                    void (*func)(timeval *tv, void *arg),
+                    void *arg)
+  void sel_rmtimer(sel_timer *t)
+  int sel_select(sel_state *s) except *
+
+### Non-blocking connection.
+cdef extern from 'mLib/conn.h':
+  ctypedef struct conn:
+    pass
+  int conn_fd(conn *c, sel_state *s, int fd,
+              void (*func)(int fd, void *arg), void *arg)
+  void conn_kill(conn *c)
+
+## Background name resolver.
+cdef extern from 'mLib/bres.h':
+  ctypedef struct bres_client:
+    pass
+  void bres_byname(bres_client *r, char *name,
+                   void (*func)(hostent *h, void *arg), void *arg)
+  void bres_byaddr(bres_client *r, in_addr addr,
+                   void (*func)(hostent *h, void *arg), void *arg)
+  void bres_abort(bres_client *r)
+  void bres_exec(char *null)
+  void bres_init(sel_state *s)
+
+## In-band signal handling.
+cdef extern from 'mLib/sig.h':
+  ctypedef struct sig:
+    pass
+  void sig_add(sig *s, int n, void (*func)(int n, void *arg), void *arg)
+  void sig_remove(sig *s)
+  void sig_init(sel_state *s)
+
+## Line buffer.
+cdef extern from 'mLib/selbuf.h':
+  ctypedef struct selbuf:
+    sel_file reader
+    lbuf b
+  void selbuf_enable(selbuf *b)
+  void selbuf_disable(selbuf *b)
+  void selbuf_setsize(selbuf *b, size_t sz)
+  void selbuf_init(selbuf *b, sel_state *s, int fd,
+                   void (*func)(char *s, size_t len, void *arg), void *arg)
+  void selbuf_destroy(selbuf *b)
+
+## Packet buffer.
+cdef extern from 'mLib/selpk.h':
+  ctypedef struct selpk:
+    sel_file reader
+    pkbuf pk
+  void selpk_enable(selpk *b)
+  void selpk_disable(selpk *b)
+  void selpk_want(selpk *b, size_t sz)
+  void selpk_init(selpk *b, sel_state *s, int fd,
+                  void (*func)(unsigned char *p, size_t n,
+                               pkbuf *pk, size_t *keep, void *arg),
+                  void *arg)
+  void selpk_destroy(selpk *b)
+
+## Ident client.
+cdef extern from 'mLib/ident.h':
+  ctypedef struct ident_request:
+    pass
+  enum:
+    IDENT_USERID
+    IDENT_ERROR
+    IDENT_BAD
+  struct ident_userid:
+    char *os
+    char *user
+  union ident_u:
+    ident_userid userid
+    char *error
+  ctypedef struct ident_reply:
+    int sport
+    int dport
+    int type
+    ident_u u
+  void ident(ident_request *rq, sel_state *s,
+             sockaddr_in *local, sockaddr_in *remote,
+             void (*func)(ident_reply *r, void *arg),
+             void *arg)
+  void ident_abort(ident_request *rq)
+
+###--------------------------------------------------------------------------
+### Error reporting.
+
+cdef extern from 'mLib/quis.h':
+  void _ego "ego"(char *prog)
+  char *_quis "quis"()
+
+cdef extern from 'mLib/report.h':
+  void _moan "moan"(char *f, char *msg)
+
+###--------------------------------------------------------------------------
+### File comparison.
+
+cdef extern from 'mLib/fwatch.h':
+  ctypedef struct fwatch:
+    pass
+  void fwatch_init(fwatch *f, char *name)
+  void fwatch_initfd(fwatch *f, int fd)
+  int fwatch_update(fwatch *f, char *name)
+  int fwatch_updatefd(fwatch *f, int fd)
+
+###--------------------------------------------------------------------------
+### File descriptor hacking.
+
+cdef extern from 'mLib/fdflags.h':
+  int _fdflags "fdflags"(int fd,
+                         unsigned fbic, unsigned fxor,
+                         unsigned fdbic, unsigned fdxor)
+
+cdef extern from 'mLib/fdpass.h':
+  int fdpass_send(int sock, int fd, void *p, size_t sz)
+  int fdpass_recv(int sock, int *fd, void *p, size_t sz)
+
+cdef extern from 'mLib/mdup.h':
+  ctypedef struct mdup_fd:
+    int cur
+    int want
+  int _mdup "mdup"(mdup_fd *v, size_t n)
+
+###--------------------------------------------------------------------------
+### Daemon utilities.
+
+cdef extern from 'mLib/daemonize.h':
+  int _daemonize "daemonize"()
+  void _detachtty "detachtty"()
+
+###--------------------------------------------------------------------------
+### Internal utilities.
+
+cdef extern from 'grim.h':
+  int PSIZEOF(void *x)
+  ctypedef void *cvp
+
+###----- That's all, folks --------------------------------------------------
diff --git a/fdutils.pyx b/fdutils.pyx
new file mode 100644 (file)
index 0000000..71135e6
--- /dev/null
@@ -0,0 +1,79 @@
+### -*-pyrex-*-
+###
+### Messing with file descriptors
+###
+### (c) 2007 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+def fdflags(file,
+            unsigned fbic = 0, unsigned fxor = 0,
+            unsigned fdbic = 0, unsigned fdxor = 0):
+  """
+  fdflags(FILE, [fbic = 0], [fxor = 0], [fdbic = 0], [fdxor = 0])
+
+  Set fcntl(2) file and descriptor flags.  If these are FL and FD, then
+  update:
+
+    * FL = (FL & ~FBIC) ^ FXOR
+    * FD = (FD & ~FDBIC) ^ FDXOR
+
+  FILE may be integer file descriptor or an object with `fileno' method.
+  """
+  cdef int rc
+  rc = _fdflags(_getfd(file), fbic, fxor, fdbic, fdxor)
+  if rc < 0:
+    _oserror()
+  return rc
+
+def fdsend(sock, file, buffer):
+  """
+  fdsend(SOCK, FILE, BUFFER) -> RC:
+    send FILE over Unix-domain socket SOCK, along with BUFFER
+  """
+  cdef void *p
+  cdef Py_ssize_t len
+  cdef int rc
+  PyObject_AsReadBuffer(buffer, <cvp *>&p, &len)
+  rc = fdpass_send(_getfd(sock), _getfd(file), p, len)
+  if rc < 0:
+    _oserror()
+  return rc
+
+def fdrecv(sock, unsigned size):
+  """
+  fdrecv(SOCK, SIZE) -> FD, BUFFER
+    receive file FD and BUFFER of length up to SIZE from Unix-domain SOCK
+  """
+  cdef void *p
+  cdef buf
+  cdef Py_ssize_t len
+  cdef PyObject *obj
+  cdef int fd
+  buf = PyString_FromStringAndSize(NULL, size)
+  p = PyString_AS_STRING(buf)
+  len = fdpass_recv(_getfd(sock), &fd, p, size)
+  if len < 0:
+    _oserror()
+  obj = <PyObject *>buf
+  _PyString_Resize(&obj, len)
+  return fd, <object>obj
+
+###----- That's all, folks --------------------------------------------------
diff --git a/fwatch.pyx b/fwatch.pyx
new file mode 100644 (file)
index 0000000..eedb872
--- /dev/null
@@ -0,0 +1,53 @@
+### -*-pyrex-*-
+###
+### Watching files for changes
+###
+### (c) 2007 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class FWatch:
+  """
+  FWatch(FILE): watch FILE for changes
+
+  FILE may be a string, file descriptor, or an object with a `fileno' method.
+  """
+  cdef fwatch fw
+  cdef public file
+  def __cinit__(me, file):
+    me._init(file)
+  def __init__(me, file):
+    me._init(file)
+  cdef _init(me, file):
+    if isinstance(file, str):
+      fwatch_init(&me.fw, file)
+    else:
+      fwatch_initfd(&me.fw, _getfd(file))
+    me.file = file
+  def update(me):
+    """FW.update() -> RC: nonzero if the file has changed state"""
+    cdef int rc
+    if isinstance(me.file, str):
+      rc = fwatch_update(&me.fw, me.file)
+    else:
+      rc = fwatch_updatefd(&me.fw, _getfd(me.file))
+    return rc
+
+###----- That's all, folks --------------------------------------------------
diff --git a/grim.h b/grim.h
new file mode 100644 (file)
index 0000000..85ea681
--- /dev/null
+++ b/grim.h
@@ -0,0 +1,73 @@
+/* -*-c-*-
+ *
+ * Grim hacks to support the Pyrex code
+ *
+ * (c) 2005 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Python interface to mLib.
+ *
+ * mLib/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.
+ *
+ * mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GRIM_H
+#define GRIM_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <Python.h>
+
+/*----- Utilities ---------------------------------------------------------*/
+
+#define PSIZEOF(x) sizeof(*x)
+typedef const void *cvp;
+
+#define RETURN_OBJ(obj) do { Py_INCREF(obj); return (obj); } while (0)
+#define RETURN_ME RETURN_OBJ(me)
+#define RETURN_NONE RETURN_OBJ(Py_None)
+#define RETURN_NOTIMPL RETURN_OBJ(Py_NotImplemented)
+
+#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 },
+
+#ifndef PySequence_Fast_ITEMS
+#  define PySequence_Fast_ITEMS(o)                                     \
+  (PyList_Check(o) ? &PyList_GET_ITEM(o, 0) : &PyTuple_GET_ITEM(o, 0))
+#endif
+
+#define FREEOBJ(obj)                                                   \
+  (((PyObject *)(obj))->ob_type->tp_free((PyObject *)(obj)))
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/ident.pyx b/ident.pyx
new file mode 100644 (file)
index 0000000..9a522bd
--- /dev/null
+++ b/ident.pyx
@@ -0,0 +1,164 @@
+### -*-pyrex-*-
+###
+### Ident client
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import socket
+
+cdef _inaddr_frompy(sockaddr_in *sin, addr):
+  cdef int port
+  if len(addr) != 2:
+    raise TypeError, 'want address/port pair'
+  a = addr[0]
+  if not inet_aton(a, &sin.sin_addr):
+    raise TypeError, 'bad IP address'
+  port = addr[1]
+  if not (0 <= port < 65536):
+    raise TypeError, 'port number out of range'
+  sin.sin_port = htons(port)
+
+cdef _inaddr_topy(sockaddr_in *sin):
+  return inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)
+
+cdef class SelIdentify:
+  """
+  SelIdentify(SK, [userproc = None], [bogusproc = None],
+                  [badproc = None], [errorproc = None],)
+
+  Asynchronously enquire about remote user of socket SK.
+  """
+  cdef ident_request irq
+  cdef int _activep
+  cdef readonly localaddr
+  cdef readonly remoteaddr
+  cdef _user
+  cdef _bad
+  cdef _error
+  cdef _bogus
+  def __cinit__(me, sk, userproc = None, bogusproc = None,
+                badproc = None, errorproc = None, *hunoz, **hukairz):
+    cdef sockaddr_in s_in, s_out
+    cdef socklen_t sz_in, sz_out
+    cdef int fd
+    if typecheck(sk, socket.SocketType):
+      fd = sk.fileno()
+      sz_in = PSIZEOF(&s_in)
+      sz_out = PSIZEOF(&s_out)
+      if getsockname(fd, <sockaddr *>&s_in, &sz_in) or \
+         getpeername(fd, <sockaddr *>&s_out, &sz_out):
+        _oserror()
+      if s_in.sin_family != AF_INET or s_out.sin_family != AF_INET:
+        raise TypeError, 'must be internet socket'
+    elif len(sk) != 2:
+      raise TypeError, 'want pair of addresses'
+    else:
+      _inaddr_frompy(&s_in, sk[0])
+      _inaddr_frompy(&s_out, sk[1])
+    ident(&me.irq, &_sel, &s_in, &s_out, _identfunc, <void *>me)
+    me.localaddr = _inaddr_topy(&s_in)
+    me.remoteaddr = _inaddr_topy(&s_out)
+    me._activep = 1
+    me._user = _checkcallable(userproc, 'user proc')
+    me._bad = _checkcallable(badproc, 'bad proc')
+    me._error = _checkcallable(errorproc, 'error proc')
+    me._bogus = _checkcallable(bogusproc, 'bogus proc')
+  def __dealloc__(me):
+    if me._activep:
+      ident_abort(&me.irq)
+  property activep:
+    """I.activep -> BOOL: query still in progress?"""
+    def __get__(me):
+      return _tobool(me._activep)
+  property userproc:
+    """I.userproc -> FUNC: call FUNC(OS, USER) if server replied"""
+    def __get__(me):
+      return me._user
+    def __set__(me, proc):
+      me._user = _checkcallable(proc, 'user proc')
+    def __del__(me):
+      me._user = None
+  property badproc:
+    """I.badproc -> FUNC: call FUNC() if server's reply was broken"""
+    def __get__(me):
+      return me._bad
+    def __set__(me, proc):
+      me._bad = _checkcallable(proc, 'bad proc')
+    def __del__(me):
+      me._bad = None
+  property errorproc:
+    """I.errorproc -> FUNC: call FUNC(ERR) if server reported error"""
+    def __get__(me):
+      return me._error
+    def __set__(me, proc):
+      me._error = _checkcallable(proc, 'error proc')
+    def __del__(me):
+      me._error = None
+  property bogusproc:
+    """I.bogusproc -> FUNC: call FUNC() on failure if no specific handler"""
+    def __get__(me):
+      return me._bogus
+    def __set__(me, proc):
+      me._bogus = _checkcallable(proc, 'bogus proc')
+    def __del__(me):
+      me._bogus = None
+  def kill(me):
+    """I.kill(): cancel ident query"""
+    if not me._activep:
+      raise ValueError, 'already disabled'
+    ident_abort(&me.irq)
+    me._dead()
+  def _dead(me):
+    me._activep = 0
+    me.dead()
+  def dead(me):
+    """I.dead(): called when operation completes or fails"""
+    pass
+  def user(me, os, user):
+    """I.user(OS, USER): called if server returns user name"""
+    return _maybecall(me._user, (os, user))
+  def bad(me):
+    """I.bad(): called if server's reply is invalid"""
+    if me._bad is not None:
+      return me._bad()
+    return me.bogus()
+  def error(me, error):
+    """I.error(ERR): called if server returns an error"""
+    if me._error is not None:
+      return me._error(error)
+    return me.bogus()
+  def bogus(me):
+    """I.bogus(): called on failure if there's no more specific handler"""
+    return _maybecall(me._bogus, ())
+
+cdef void _identfunc(ident_reply *i, void *arg):
+  cdef SelIdentify id
+  id = <SelIdentify>arg
+  id._dead()
+  if i.type == IDENT_BAD:
+    id.bad()
+  elif i.type == IDENT_ERROR:
+    id.error(i.u.error)
+  elif i.type == IDENT_USERID:
+    id.user(i.u.userid.os, i.u.userid.user)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/lbuf.pyx b/lbuf.pyx
new file mode 100644 (file)
index 0000000..c5f2688
--- /dev/null
+++ b/lbuf.pyx
@@ -0,0 +1,148 @@
+### -*-pyrex-*-
+###
+### Line buffering
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+LBUF_CRLF = _LBUF_CRLF
+LBUF_STRICTCRLF = _LBUF_STRICTCRLF
+
+cdef class LineBuffer:
+  """
+  LineBuffer([lineproc = None], [eofproc = None])
+
+  Split an incoming stream into lines.
+  """
+  cdef lbuf b
+  cdef _line
+  cdef _eof
+  def __cinit__(me, lineproc = None, eofproc = None, *hunoz, **hukairz):
+    lbuf_init(&me.b, _lbfunc, <void *>me)
+    me._line = _checkcallable(lineproc, 'line proc')
+    me._eof = _checkcallable(eofproc, 'eof proc')
+  def __dealloc__(me):
+    lbuf_destroy(&me.b)
+  property activep:
+    """LB.activep -> BOOL: is the buffer still active?"""
+    def __get__(me):
+      return _tobool(me.b.f & LBUF_ENABLE)
+  property delim:
+    """LB.delim -> CHAR | LBUF_...: line-end delimiter"""
+    def __get__(me):
+      if me.b.delim == _LBUF_CRLF or me.b.delim == _LBUF_STRICTCRLF:
+        return me.b.delim
+      else:
+        return chr(me.b.delim)
+    def __set__(me, d):
+      if d == _LBUF_CRLF or d == _LBUF_STRICTCRLF:
+        me.b.delim = d
+      else:
+        me.b.delim = ord(d)
+  property size:
+    """LB.size -> INT: buffer size limit"""
+    def __get__(me):
+      return me.b.sz
+    def __set__(me, sz):
+      if sz <= 0:
+        raise TypeError, 'size must be positive'
+      lbuf_setsize(&me.b, sz)
+  property lineproc:
+    """LB.lineproc -> FUNC: call FUNC(LINE) on each line"""
+    def __get__(me):
+      return me._line
+    def __set__(me, proc):
+      me._line = _checkcallable(proc, 'line proc')
+    def __del__(me):
+      me._line = None
+  property eofproc:
+    """LB.eofproc -> FUNC: call FUNC() at end-of-file"""
+    def __get__(me):
+      return me._eof
+    def __set__(me, proc):
+      me._eof = _checkcallable(proc, 'eof proc')
+    def __del__(me):
+      me._eof = None
+  def enable(me):
+    """LB.enable(): enable the buffer, allowing lines to be emitted"""
+    if me.b.f & LBUF_ENABLE:
+      raise ValueError, 'already enabled'
+    me.b.f = me.b.f | LBUF_ENABLE
+    me.enabled()
+    return me
+  def disable(me):
+    """LB.disable(): disable the buffer, suspending line emission"""
+    if not (me.b.f & LBUF_ENABLE):
+      raise ValueError, 'already disabled'
+    me.b.f = me.b.f & ~LBUF_ENABLE
+    me.disabled()
+    return me
+  def close(me):
+    """LB.close(): report the end of the input stream"""
+    if not (me.b.f & LBUF_ENABLE):
+      raise ValueError, 'buffer disabled'
+    lbuf_close(&me.b)
+    return me
+  property free:
+    """LB.free -> INT: amount of space remaining in buffer"""
+    def __get__(me):
+      cdef char *p
+      return lbuf_free(&me.b, &p)
+  def flush(me, str):
+    """LB.flush(STR) -> insert STR into the buffer and emit lines"""
+    cdef Py_ssize_t len
+    cdef char *p
+    cdef char *q
+    cdef size_t n
+    PyString_AsStringAndSize(str, &p, &len)
+    while len > 0:
+      n = lbuf_free(&me.b, &q)
+      if n > len:
+        n = len
+      memcpy(q, p, n)
+      p = p + n
+      len = len - n
+      if not (me.b.f & LBUF_ENABLE):
+        break
+      lbuf_flush(&me.b, q, n)
+    return PyString_FromStringAndSize(p, len)
+  def enabled(me):
+    """LB.enabled(): called when buffer is enabled"""
+    pass
+  def disabled(me):
+    """LB.disabled(): called when buffer is disabled"""
+    pass
+  def line(me, line):
+    """LB.line(LINE): called for each completed line"""
+    return _maybecall(me._line, (line,))
+  def eof(me):
+    """LB.eof(): called at end-of-file"""
+    return _maybecall(me._eof, ())
+
+cdef void _lbfunc(char *s, size_t n, void *arg):
+  cdef LineBuffer sb
+  sb = <LineBuffer>arg
+  if s is NULL:
+    sb.eof()
+  else:
+    sb.line(PyString_FromStringAndSize(s, n))
+
+###----- That's all, folks --------------------------------------------------
diff --git a/mLib.pyx b/mLib.pyx
new file mode 100644 (file)
index 0000000..daa6cd9
--- /dev/null
+++ b/mLib.pyx
@@ -0,0 +1,99 @@
+### -*-pyrex-*-
+###
+### Main driver for mLib module
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### External stuff.
+
+include 'defs.pxi'
+
+###--------------------------------------------------------------------------
+### Various facilities.
+
+## Internal utilities.
+include 'utils.pyx'
+
+## Hashing.
+include 'crc32.pyx'
+include 'unihash.pyx'
+
+## Data structures.
+include 'mapping.pyx'
+include 'sym.pyx'
+include 'atom.pyx'
+include 'assoc.pyx'
+
+## String utilities.
+include 'str.pyx'
+
+## Encodings.
+include 'codec.pyx'
+include 'base64.pyx'
+include 'base32.pyx'
+include 'hex.pyx'
+include 'url.pyx'
+
+## Error reporting.
+include 'report.pyx'
+
+## File utilities.
+include 'fwatch.pyx'
+include 'fdutils.pyx'
+include 'mdup.pyx'
+
+## Other useful stuff.
+include 'stuff.pyx'
+
+## Buffering.
+include 'lbuf.pyx'
+include 'pkbuf.pyx'
+
+## Select stuff.
+include 'sel-base.pyx'
+include 'sel-file.pyx'
+include 'sel-timer.pyx'
+include 'conn.pyx'
+include 'bres.pyx'
+include 'sig.pyx'
+include 'selbuf.pyx'
+include 'selpk.pyx'
+include 'ident.pyx'
+
+###--------------------------------------------------------------------------
+### Set-up stuff.
+
+cdef object _tyobj(PyTypeObject *ty):
+  cdef PyObject *obj
+  obj = <PyObject *>ty
+  Py_INCREF(obj)
+  return <object>obj
+
+da_pysetup()
+Array = _tyobj(&da_pytype)
+ArrayIter = _tyobj(&daiter_pytype)
+
+atom_pysetup()
+Atom = _tyobj(&atom_pytype)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/mapping.pyx b/mapping.pyx
new file mode 100644 (file)
index 0000000..b044ed6
--- /dev/null
@@ -0,0 +1,217 @@
+### -*-pyrex-*-
+###
+### Common mapping stuff
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class Mapping
+
+cdef class _MapIterator:
+  cdef void *_next(me):
+    return NULL
+
+cdef class Mapping:
+
+  ## Subclasses must implement these
+  cdef int _init(me) except -1:
+    raise TypeError, 'abstract class'
+  cdef void *_find(me, object key, unsigned *f) except NULL:
+    raise SystemError, 'unimplemented _find'
+  cdef object _key(me, void *e):
+    return None
+  cdef object _value(me, void *e):
+    return None
+  cdef void _setval(me, void *e, object val):
+    pass
+  cdef void _del(me, void *e):
+    pass
+  cdef _MapIterator _iter(me):
+    raise SystemError, 'unimplemented _iter'
+
+  ## Initialization
+  def __cinit__(me, *hunoz, **hukairz):
+    me._init()
+  def __init__(me, stuff = None, **kw):
+    me.update(stuff, kw)
+
+  ## Bulk update
+  def update(me, stuff = None, **kw):
+    """D.update([MAP], **KW): insert mappings from MAP and KW"""
+    cdef unsigned f
+    if stuff is None:
+      pass
+    elif hasattr(stuff, 'itemiter'):
+      for k, v in stuff.itemiter:
+        me._setval(me._find(k, &f), v)
+    elif hasattr(stuff, 'keys'):
+      for k in stuff.keys():
+        me._setval(me._find( k, &f), stuff[k])
+    else:
+      for k, v in stuff:
+        me._setval(me._find(k, &f), v)
+    for k, v in kw.iteritems():
+      me._setval(me._find(k, &f), v)
+    return me
+
+  ## Item access
+  def __getitem__(me, key):
+    cdef void *e
+    e = me._find(key, NULL)
+    if not e:
+      raise KeyError, key
+    return me._value(e)
+  def __setitem__(me, key, value):
+    cdef unsigned f
+    me._setval(me._find(key, &f), value)
+  def __delitem__(me, key):
+    cdef void *e
+    cdef unsigned f
+    e = me._find(key, &f)
+    if not e:
+      raise KeyError, key
+    me._del(e)
+  def get(me, key, default = None):
+    """D.get(KEY, [default = None]) -> VALUE: value at KEY, or DEFAULT"""
+    cdef void *e
+    e = me._find(key, NULL)
+    if not e:
+      return default
+    return me._value(e)
+  def setdefault(me, key, default = None):
+    """
+    D.setdefault(KEY, [default = None]) -> VALUE:
+      return value at key, or store DEFAULT at key and return that"""
+    cdef void *e
+    cdef unsigned f
+    e = me._find(key, &f)
+    if f:
+      return me._value(e)
+    else:
+      me._setval(e, default)
+      return default
+  def pop(me, key, default = None):
+    """
+    D.pop(KEY, [default = None]) -> VALUE:
+      return value at key or DEFAULT, and remove KEY"""
+    cdef void *e
+    e = me._find(key, NULL)
+    if not e:
+      return default
+    rc = me._value(e)
+    me._del(e)
+    return rc
+  def popitem(me):
+    """D.popitem() -> KEY, VALUE: return and remove an association pair"""
+    cdef _MapIterator i
+    cdef void *e
+    i = me._iter()
+    e = i._next()
+    if not e:
+      raise ValueError, 'popitem(): table is empty'
+    return me._key(e), me._value(e)
+
+  ## Lists of items
+  cdef object _list(me, object (*func)(Mapping m, void *e)):
+    cdef _MapIterator i
+    cdef void *e
+    i = me._iter()
+    l = []
+    while 1:
+      e = i._next()
+      if not e:
+        break
+      l.append(func(me, e))
+    return l
+
+  def keys(me):
+    """D.keys() -> LIST: return a list of known keys"""
+    return me._list(_map_key)
+  def values(me):
+    """D.values() -> LIST: return a list of known values"""
+    return me._list(_map_value)
+  def items(me):
+    """D.values() -> LIST: return a list of known (KEY, VALUE) pairs"""
+    return me._list(_map_item)
+
+  def clear(me):
+    """D.clear(): remove all mappings"""
+    cdef _MapIterator i
+    cdef void *e
+    i = me._iter()
+    while 1:
+      e = i._next()
+      if not e:
+        break
+      me._del(e)
+    return me
+
+  ## Iteration
+  def __iter__(me):
+    return MapKeyIter(me)
+  def iterkeys(me):
+    """D.iterkeys() -> ITER: return iterator over keys"""
+    return MapKeyIter(me)
+  def itervalues(me):
+    """D.itervalues() -> ITER: return iterator over values"""
+    return MapValueIter(me)
+  def iteritems(me):
+    """D.iteritems() -> ITER: return iterator over (KEY, VALUE) pairs"""
+    return MapItemIter(me)
+
+cdef class MapIterBase:
+  cdef Mapping m
+  cdef object (*func)(Mapping m, void *e)
+  cdef _MapIterator i
+  cdef int _init(me) except -1:
+    raise TypeError, 'abstract class'
+  def __cinit__(me):
+    me.i = m._iter()
+    me._init()
+  def __iter__(me):
+    return me
+  def __next__(me):
+    cdef void *e
+    e = me.i._next()
+    if not e:
+      raise StopIteration
+    return me.func(me.m, e)
+cdef class MapKeyIter (MapIterBase):
+  cdef int _init(me) except -1:
+    me.func = _map_key
+    return 0
+cdef class MapValueIter (MapIterBase):
+  cdef int _init(me) except -1:
+    me.func = _map_value
+    return 0
+cdef class MapItemIter (MapIterBase):
+  cdef int _init(me) except -1:
+    me.func = _map_item
+    return 0
+
+cdef object _map_key(Mapping m, void *e):
+  return m._key(e)
+cdef object _map_value(Mapping m, void *e):
+  return m._value(e)
+cdef object _map_item(Mapping m, void *e):
+  return m._key(e), m._value(e)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/mdup.pyx b/mdup.pyx
new file mode 100644 (file)
index 0000000..7256211
--- /dev/null
+++ b/mdup.pyx
@@ -0,0 +1,51 @@
+### -*-pyrex-*-
+###
+### File descriptor juggling
+###
+### (c) 2009 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+def mdup(v):
+  """
+  mdup(LIST) -> LIST:
+    LIST is a list (mutable sequence) of pairs (CUR, WANT).  Duplicate each
+    CUR file descriptor as WANT (may be -1 to mean `don't care'), closing
+    original CUR.  Works even if there are cycles.  LIST is updated in place
+    with CUR reflecting the new file descriptors even on error.  Returns the
+    same LIST on success.
+  """
+  cdef mdup_fd *vv
+  cdef size_t n
+  cdef int i
+  cdef int rc
+
+  n = len(v)
+  vv = <mdup_fd *>xmalloc(n * PSIZEOF(vv))
+  for 0 <= i < n:
+    vv[i].cur, vv[i].want = v[i]
+  rc = _mdup(vv, n)
+  for 0 <= i < n:
+    v[i] = vv[i].cur, vv[i].want
+  if rc < 0:
+    _oserror()
+  return v
+
+###----- That's all, folks --------------------------------------------------
diff --git a/pkbuf.pyx b/pkbuf.pyx
new file mode 100644 (file)
index 0000000..fe296f3
--- /dev/null
+++ b/pkbuf.pyx
@@ -0,0 +1,143 @@
+### -*-pyrex-*-
+###
+### Packet buffering
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class PacketBuffer:
+  """
+  PacketBuffer([packetproc = None], [eofproc = None])
+
+  Split an incoming stream into packets.
+  """
+  cdef pkbuf pk
+  cdef _packet
+  cdef _eof
+  def __cinit__(me, packetproc = None, eofproc = None, *hunoz, **hukairz):
+    pkbuf_init(&me.pk, _pkfunc, <void *>me)
+    me._packet = _checkcallable(packetproc, 'packet proc')
+    me._eof = _checkcallable(eofproc, 'eof proc')
+  def __dealloc__(me):
+    pkbuf_destroy(&me.pk)
+  property activep:
+    """PK.activep -> BOOL: is the buffer still active?"""
+    def __get__(me):
+      return _tobool(me.pk.f & PKBUF_ENABLE)
+  property want:
+    """PK.want -> INT: size of next packet to return"""
+    def __get__(me):
+      return me.pk.want
+    def __set__(me, want):
+      if want <= 0:
+        raise TypeError, 'want must be positive'
+      pkbuf_want(&me.pk, pk)
+  property packetproc:
+    """PK.packetproc -> FUNC: call FUNC(PACKET) on each packet"""
+    def __get__(me):
+      return me._packet
+    def __set__(me, proc):
+      me._line = _checkcallable(proc, 'packet proc')
+    def __del__(me):
+      me._line = None
+  property eofproc:
+    """PK.eofproc -> FUNC: call FUNC() at end-of-file"""
+    def __get__(me):
+      return me._eof
+    def __set__(me, proc):
+      me._eof = _checkcallable(proc, 'eof proc')
+    def __del__(me):
+      me._eof = None
+  def enable(me):
+    """PK.enable(): enable the buffer, allowing packets to be emitted"""
+    if me.pk.f & PKBUF_ENABLE:
+      raise ValueError, 'already enabled'
+    me.pk.f = me.pk.f | PKBUF_ENABLE
+    me.enabled()
+    return me
+  def disable(me):
+    """PK.disable(): disable the buffer, suspending packet emission"""
+    if not (me.pk.f & PKBUF_ENABLE):
+      raise ValueError, 'already disabled'
+    me.pk.f = me.pk.f & ~PKBUF_ENABLE
+    me.disabled()
+    return me
+  def close(me):
+    """PK.close(): report the end of the input stream"""
+    if not (me.pk.f & PKBUF_ENABLE):
+      raise ValueError, 'buffer disabled'
+    pkbuf_close(&me.pk)
+    return me
+  property free:
+    """PK.free -> INT: amount of space remaining in buffer"""
+    def __get__(me):
+      cdef unsigned char *p
+      return pkbuf_free(&me.pk, &p)
+  def flush(me, str):
+    """PK.flush(STR) -> insert STR into the buffer and emit packets"""
+    cdef Py_ssize_t len
+    cdef unsigned char *p
+    cdef unsigned char *q
+    cdef size_t n
+    PyObject_AsReadBuffer(str, <cvp *>&p, &len)
+    while len > 0:
+      n = pkbuf_free(&me.pk, &q)
+      if n > len:
+        n = len
+      memcpy(q, p, n)
+      p = p + n
+      len = len - n
+      if not (me.pk.f & PKBUF_ENABLE):
+        break
+      pkbuf_flush(&me.pk, q, n)
+    return PyString_FromStringAndSize(<char *>p, len)
+  def enabled(me):
+    """PK.enabled(): called when buffer is enabled"""
+    pass
+  def disabled(me):
+    """PK.disabled(): called when buffer is disabled"""
+    pass
+  def packet(me, pk):
+    """PK.packet(PACKET): called for each completed packet"""
+    return _maybecall(me._packet, (pk,))
+  def eof(me):
+    """PK.eof(): called at end-of-file"""
+    return _maybecall(me._eof, ())
+
+cdef void _pkfunc(unsigned char *p, size_t n, pkbuf *pk,
+                  size_t *keep, void *arg):
+  cdef PacketBuffer pb
+  cdef void *rp
+  cdef Py_ssize_t rn
+  pb = <PacketBuffer>arg
+  if p is NULL:
+    pb.eof()
+  else:
+    r = pb.packet(PyString_FromStringAndSize(<char *>p, n))
+    if r is not None:
+      PyObject_AsReadBuffer(r, <cvp *>&rp, &rn)
+      if rn > n:
+        raise ValueError, 'remaining buffer too large'
+      if rn:
+        memcpy(p + n - rn, rp, rn)
+        keep[0] = rn
+
+###----- That's all, folks --------------------------------------------------
diff --git a/pyke/.skelrc b/pyke/.skelrc
new file mode 100644 (file)
index 0000000..21912db
--- /dev/null
@@ -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))
similarity index 100%
rename from mapping-mLib.c
rename to pyke/mapping-mLib.c
similarity index 100%
rename from mapping.c
rename to pyke/mapping.c
similarity index 100%
rename from pyke-mLib.c
rename to pyke/pyke-mLib.c
similarity index 100%
rename from pyke-mLib.h
rename to pyke/pyke-mLib.h
similarity index 100%
rename from pyke.c
rename to pyke/pyke.c
similarity index 100%
rename from pyke.h
rename to pyke/pyke.h
diff --git a/report.pyx b/report.pyx
new file mode 100644 (file)
index 0000000..9d19ed2
--- /dev/null
@@ -0,0 +1,47 @@
+### -*-pyrex-*-
+###
+### Error reporting and stuff
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+quis = '<UNNAMED>'
+cdef char *_progstring
+_progstring = NULL
+
+def ego(char *prog):
+  """ego(PROG): set program name"""
+  global quis, _progstring
+  if _progstring:
+    xfree(_progstring)
+  _progstring = xstrdup(prog)
+  _ego(_progstring)
+  quis = _quis()
+
+def moan(char *msg):
+  """moan(MSG): report a warning"""
+  _moan('%s', msg)
+def die(char *msg, rc = 126):
+  """die(MSG, [rc = 126]): report a fatal error and exit"""
+  _moan('%s', msg)
+  raise SystemExit, rc
+
+###----- That's all, folks --------------------------------------------------
diff --git a/sel-base.pyx b/sel-base.pyx
new file mode 100644 (file)
index 0000000..64e8d08
--- /dev/null
@@ -0,0 +1,35 @@
+### -*-pyrex-*-
+###
+### Select basic stuff
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef sel_state _sel
+
+def select():
+  """select(): wait for I/O and/or timeouts"""
+  if sel_select(&_sel) and errno != EINTR and errno != EAGAIN:
+    _oserror()
+
+sel_init(&_sel)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/sel-file.pyx b/sel-file.pyx
new file mode 100644 (file)
index 0000000..9ff1029
--- /dev/null
@@ -0,0 +1,108 @@
+### -*-pyrex-*-
+###
+### File selectors
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+SEL_READ = _SEL_READ
+SEL_WRITE = _SEL_WRITE
+SEL_EXCEPT = _SEL_EXC
+
+cdef class SelFile:
+  """
+  SelFile(FILE, [mode = SEL_READ], [readyproc = None])
+
+  Register a file (or socket, or, ...) with the select loop.
+  """
+  cdef sel_file f
+  cdef int _activep
+  cdef readonly unsigned mode
+  cdef _readyfunc
+  def __cinit__(me, fd, int mode = SEL_READ, readyproc = None,
+              *hunoz, **hukairz):
+    if (mode != _SEL_READ and
+        mode != _SEL_WRITE and
+        mode != _SEL_EXC):
+      raise ValueError, 'bad select mode'
+    sel_initfile(&_sel, &me.f, _getfd(fd), mode, _filefunc, <void *>me)
+    me._activep = 0
+    me.mode = mode
+    me._readyfunc = _checkcallable(readyproc, 'ready proc')
+  def __dealloc__(me):
+    if me._activep:
+      sel_rmfile(&me.f)
+  property fd:
+    """SF.fd -> INT: the file descriptor"""
+    def __get__(me):
+      return me.f.fd
+  property activep:
+    """SF.activep -> BOOL: is the descriptor active?"""
+    def __get__(me):
+      return _tobool(me._activep)
+  property readyproc:
+    """SF.readyproc -> FUNC: call FUNC() when file is ready for I/O"""
+    def __get__(me):
+      return me._readyfunc
+    def __set__(me, proc):
+      me._readyfunc = _checkcallable(proc, 'ready proc')
+    def __del__(me):
+      me._readyfunc = None
+  def enable(me):
+    """SF.enable(): enable waiting on file"""
+    if me._activep:
+      raise ValueError, 'already enabled'
+    sel_addfile(&me.f)
+    me._enabled()
+    return me
+  def disable(me):
+    """SF.disable(): disable waiting on file"""
+    if not me._activep:
+      raise ValueError, 'already disabled'
+    sel_rmfile(&me.f)
+    me._disabled()
+    return me
+  def force(me):
+    """SF.force(): artificially mark file as ready"""
+    sel_force(&me.f)
+    return me
+  cdef _enabled(me):
+    me._activep = 1
+    me.enabled()
+  cdef _disabled(me):
+    me._activep = 0
+    me.disabled()
+  def enabled(me):
+    """SF.enabled(): called when file is enabled"""
+    pass
+  def disabled(me):
+    """SF.disabled(): called when file is disabled"""
+    pass
+  def ready(me):
+    """SF.ready(): called when file is ready for I/O"""
+    return _maybecall(me._readyfunc, ())
+
+cdef void _filefunc(int fd, unsigned mode, void *arg):
+  cdef SelFile sf
+  sf = <SelFile>arg
+  sf.ready()
+
+###----- That's all, folks --------------------------------------------------
diff --git a/sel-timer.pyx b/sel-timer.pyx
new file mode 100644 (file)
index 0000000..ebdd433
--- /dev/null
@@ -0,0 +1,89 @@
+### -*-pyrex-*-
+###
+### Timer selectors
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef double _tvtofloat(timeval *tv):
+  return tv.tv_sec + (tv.tv_usec / 1000000)
+cdef void _floattotv(timeval *tv, double t):
+  cdef double s, us
+  us = modf(t, &s)
+  tv.tv_sec = <int>s
+  tv.tv_usec = <int>(us * 1000000)
+
+cdef class SelTimer:
+  """
+  SelTimer(WHEN, [timerproc = None])
+
+  Arrange to perform some action at time WHEN.
+  """
+  cdef sel_timer t
+  cdef int _activep
+  cdef readonly double time
+  cdef _timer
+  def __cinit__(me, double when, timerproc = None, *hunoz, **hukairz):
+    cdef timeval tv
+    _floattotv(&tv, when)
+    sel_addtimer(&_sel, &me.t, &tv, _timerfunc, <void *>me)
+    me._activep = 1
+    me.time = when
+    me._timer = _checkcallable(timerproc, 'timer proc')
+  def __dealloc__(me):
+    if me._activep:
+      sel_rmtimer(&me.t)
+  property activep:
+    """ST.activep -> BOOL: is the timer active?"""
+    def __get__(me):
+      return _tobool(me._activep)
+  property timerproc:
+    """ST.timerproc -> FUNC: call FUNC() when the timer pops"""
+    def __get__(me):
+      return me._timer
+    def __set__(me, proc):
+      me._timer = _checkcallable(proc, 'timer proc')
+    def __del__(me):
+      me._timer = None
+  def kill(me):
+    """ST.kill(): deactivate timer permanently"""
+    if not me._activep:
+      raise ValueError, 'already dead'
+    sel_rmtimer(&me.t)
+    me._dead()
+    return me
+  cdef _dead(me):
+    me._activep = 0
+    me.dead()
+  def dead(me):
+    """ST.dead(): called when timer is deactivated"""
+    pass
+  def timer(me, now):
+    """ST.timer(NOW): called when the timer pops"""
+    return _maybecall(me._timer, ())
+
+cdef void _timerfunc(timeval *now, void *arg):
+  cdef SelTimer st
+  st = <SelTimer>arg
+  st._dead()
+  st.timer(_tvtofloat(now))
+
+###----- That's all, folks --------------------------------------------------
diff --git a/selbuf.pyx b/selbuf.pyx
new file mode 100644 (file)
index 0000000..53964c7
--- /dev/null
@@ -0,0 +1,121 @@
+### -*-pyrex-*-
+###
+### Selecting line-buffers
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class SelLineBuffer:
+  """
+  SelLineBuffer(FILE, [lineproc = None], [eofproc = None])
+
+  Split an asynchronous stream into lines.
+  """
+  cdef selbuf b
+  cdef _line
+  cdef _eof
+  def __cinit__(me, fd, lineproc = None, eofproc = None, *hunoz, **hukairz):
+    selbuf_init(&me.b, &_sel, _getfd(fd), _selbfunc, <void *>me)
+    selbuf_disable(&me.b)
+    me._line = _checkcallable(lineproc, 'line proc')
+    me._eof = _checkcallable(eofproc, 'eof proc')
+  def __dealloc__(me):
+    selbuf_destroy(&me.b)
+  property activep:
+    """SLB.activep -> BOOL: is the buffer still active?"""
+    def __get__(me):
+      return _tobool(me.b.b.f & LBUF_ENABLE)
+  property fd:
+    """SLB.fd -> INT: the file descriptor"""
+    def __get__(me):
+      return me.b.reader.fd
+  property delim:
+    """SLB.delim -> CHAR | LBUF_...: line-end delimiter"""
+    def __get__(me):
+      if me.b.b.delim == _LBUF_CRLF or me.b.b.delim == _LBUF_STRICTCRLF:
+        return me.b.b.delim
+      else:
+        return chr(me.b.b.delim)
+    def __set__(me, d):
+      if d == _LBUF_CRLF or d == _LBUF_STRICTCRLF:
+        me.b.b.delim = d
+      else:
+        me.b.b.delim = ord(d)
+  property size:
+    """SLB.size -> INT: buffer size limit"""
+    def __get__(me):
+      return me.b.b.sz
+    def __set__(me, sz):
+      if sz <= 0:
+        raise TypeError, 'size must be positive'
+      selbuf_setsize(&me.b, sz)
+  property lineproc:
+    """SLB.lineproc -> FUNC: call FUNC(LINE) on each line"""
+    def __get__(me):
+      return me._line
+    def __set__(me, proc):
+      me._line = _checkcallable(proc, 'line proc')
+    def __del__(me):
+      me._line = None
+  property eofproc:
+    """SLB.eofproc -> FUNC: call FUNC() at end-of-file"""
+    def __get__(me):
+      return me._eof
+    def __set__(me, proc):
+      me._eof = _checkcallable(proc, 'eof proc')
+    def __del__(me):
+      me._eof = None
+  def enable(me):
+    """SLB.enable(): enable the buffer, allowing lines to be emitted"""
+    if me.b.b.f & LBUF_ENABLE:
+      raise ValueError, 'already enabled'
+    selbuf_enable(&me.b)
+    me.enabled()
+    return me
+  def disable(me):
+    """SLB.disable(): disable the buffer, suspending line emission"""
+    if not (me.b.b.f & LBUF_ENABLE):
+      raise ValueError, 'already disabled'
+    selbuf_disable(&me.b)
+    me.disabled()
+    return me
+  def enabled(me):
+    """SLB.enabled(): called when buffer is enabled"""
+    pass
+  def disabled(me):
+    """SLB.disabled(): called when buffer is disabled"""
+    pass
+  def line(me, line):
+    """SLB.line(LINE): called for each completed line"""
+    return _maybecall(me._line, (line,))
+  def eof(me):
+    """SLB.eof(): called at end-of-file"""
+    return _maybecall(me._eof, ())
+
+cdef void _selbfunc(char *s, size_t n, void *arg):
+  cdef SelLineBuffer sb
+  sb = <SelLineBuffer>arg
+  if s is NULL:
+    sb.eof()
+  else:
+    sb.line(PyString_FromStringAndSize(s, n))
+
+###----- That's all, folks --------------------------------------------------
diff --git a/selpk.pyx b/selpk.pyx
new file mode 100644 (file)
index 0000000..a54ce95
--- /dev/null
+++ b/selpk.pyx
@@ -0,0 +1,119 @@
+### -*-pyrex-*-
+###
+### Selecting packet-buffer
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class SelPacketBuffer:
+  """
+  SelPacketBuffer(FILE, [packetproc = None], [eofproc = None])
+
+  Split an incoming stream into packets.
+  """
+  cdef selpk p
+  cdef _packet
+  cdef _eof
+  def __cinit__(me, fd, packetproc = None, eofproc = None, *hunoz, **hukairz):
+    selpk_init(&me.p, &_sel, _getfd(fd), _selpkfunc, <void *>me)
+    selpk_disable(&me.p)
+    me._packet = _checkcallable(packetproc, 'packet proc')
+    me._eof = _checkcallable(eofproc, 'eof proc')
+  def __dealloc__(me):
+    selpk_destroy(&me.p)
+  property activep:
+    """SPK.activep -> BOOL: is the buffer still active?"""
+    def __get__(me):
+      return _to_bool(me.p.pk.f & PKBUF_ENABLE)
+  property fd:
+    """SPK.fd -> INT: the file descriptor"""
+    def __get__(me):
+      return me.p.reader.fd
+  property want:
+    """SPK.want -> INT: size of next packet to return"""
+    def __get__(me):
+      return me.p.pk.want
+    def __set__(me, n):
+      if n <= 0:
+        raise TypeError, 'size must be positive'
+      selpk_want(&me.p, n)
+  property packetproc:
+    """SPK.packetproc -> FUNC: call FUNC(PACKET) on each packet"""
+    def __get__(me):
+      return me._packet
+    def __set__(me, proc):
+      me._packet = _checkcallable(proc, 'packet proc')
+    def __del__(me):
+      me._packet = None
+  property eofproc:
+    """SPK.eofproc -> FUNC: call FUNC() at end-of-file"""
+    def __get__(me):
+      return me._eof
+    def __set__(me, proc):
+      me._eof = _checkcallable(proc, 'eof proc')
+    def __del__(me):
+      me._eof = None
+  def enable(me):
+    """SPK.enable(): enable the buffer, allowing packets to be emitted"""
+    if me.p.pk.f & PKBUF_ENABLE:
+      raise ValueError, 'already enabled'
+    selpk_enable(&me.p)
+    me.enabled()
+    return me
+  def disable(me):
+    """SPK.disable(): disable the buffer, suspending packet emission"""
+    if not (me.p.pk.f & PKBUF_ENABLE):
+      raise ValueError, 'already disabled'
+    selpk_disable(&me.p)
+    me.disabled()
+    return me
+  def enabled(me):
+    """SPK.enabled(): called when buffer is enabled"""
+    pass
+  def disabled(me):
+    """SPK.disabled(): called when buffer is disabled"""
+    pass
+  def packet(me, pk):
+    """SPK.packet(PACKET): called for each completed packet"""
+    return _maybecall(me._packet, (pk,))
+  def eof(me):
+    """SPK.eof(): called at end-of-file"""
+    return _maybecall(me._eof, ())
+
+cdef void _selpkfunc(unsigned char *p, size_t n, pkbuf *pk,
+                     size_t *keep, void *arg):
+  cdef SelPacketBuffer pb
+  cdef void *rp
+  cdef Py_ssize_t rn
+  pb = <SelPacketBuffer>arg
+  if p is NULL:
+    pb.eof()
+  else:
+    r = pb.packet(PyString_FromStringAndSize(<char *>p, n))
+    if r is not None:
+      PyObject_AsReadBuffer(r, <cvp *>&rp, &rn)
+      if rn > n:
+        raise ValueError, 'remaining buffer too large'
+      if rn:
+        memcpy(p + n - rn, rp, rn)
+        keep[0] = rn
+
+###----- That's all, folks --------------------------------------------------
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..66aafc5
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,28 @@
+#! /usr/bin/python
+
+import distutils.core as DC
+import Pyrex.Distutils as PXD
+import mdwsetup as MS
+
+MS.pkg_config('mLib', '2.1.0')
+
+mLib = DC.Extension('mLib', ['mLib.pyx', 'atom-base.c', 'array.c'],
+                    ##extra_compile_args = ['-O0'],
+                    include_dirs = MS.uniquify(MS.INCLUDEDIRS),
+                    library_dirs = MS.uniquify(MS.LIBDIRS),
+                    libraries = MS.uniquify(MS.LIBS))
+
+MS.setup(name = 'mLib-python',
+         description = 'Python interface to mLib utilities library',
+         author = 'Straylight/Edgeware',
+         author_email = 'mdw@distorted.org.uk',
+         license = 'GNU General Public License',
+         ext_modules = [mLib],
+         genfiles = [MS.Derive('base64.pyx', 'codec.pyx.in',
+                               {'CLASS': 'Base64', 'PREFIX': 'base64'}),
+                     MS.Derive('base32.pyx', 'codec.pyx.in',
+                               {'CLASS': 'Base32', 'PREFIX': 'base32'}),
+                     MS.Derive('hex.pyx', 'codec.pyx.in',
+                               {'CLASS': 'Hex', 'PREFIX': 'hex'})],
+         cleanfiles = ['mLib.c'],
+         cmdclass = { 'build_ext': PXD.build_ext })
diff --git a/sig.pyx b/sig.pyx
new file mode 100644 (file)
index 0000000..aafa050
--- /dev/null
+++ b/sig.pyx
@@ -0,0 +1,96 @@
+### -*-pyrex-*-
+###
+### In-band signals
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import signal
+
+cdef class SelSignal:
+  """
+  SelSignal(SIG, [signalledproc = None])
+
+  Collect signals from the event loop.
+  """
+  cdef sig s
+  cdef int _activep
+  cdef readonly int signal
+  cdef _signalled
+  def __cinit__(me, int sig, signalledproc = None, *hunoz, **hukairz):
+    if sig < 0 or sig >= signal.NSIG:
+      raise ValueError, 'signal number out of range'
+    me.signal = sig
+    me._signalled = _checkcallable(signalledproc, 'signalled proc')
+    me._activep = 0
+  def __dealloc__(me):
+    if me._activep:
+      sig_remove(&me.s)
+  property activep:
+    """SS.activep -> BOOL: is the handler still active?"""
+    def __get__(me):
+      return _tobool(me._activep)
+  property signalledproc:
+    """SS.signalledproc -> FUNC: call FUNC() when the signal is received"""
+    def __get__(me):
+      return me._signalled
+    def __set__(me, proc):
+      me._signalled = _checkcallable(proc, 'signalled proc')
+    def __del__(me):
+      me._signalled = None
+  def enable(me):
+    """SS.enable(): enable the handler"""
+    if me._activep:
+      raise ValueError, 'already enabled'
+    sig_add(&me.s, me.signal, _sigfunc, <void *>me)
+    me._enabled()
+    return me
+  def disable(me):
+    """SS.disable(): disable the handler"""
+    if not me._activep:
+      raise ValueError, 'already disabled'
+    sig_remove(&me.s)
+    me._disabled()
+    return me
+  cdef _enabled(me):
+    me._activep = 1
+    me.enabled()
+  cdef _disabled(me):
+    me._activep = 0
+    me.disabled()
+  def enabled(me):
+    """SS.enabled(): called when handler is enabled"""
+    pass
+  def disabled(me):
+    """SS.disabled(): called when handler is disabled"""
+    pass
+  def signalled(me):
+    """SS.signalled(): called when the signal is received"""
+    return _maybecall(me._signalled, ())
+
+cdef void _sigfunc(int sig, void *arg):
+  cdef SelSignal s
+  s = <SelSignal>arg
+  s.signalled()
+
+sig_init(&_sel)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/str.pyx b/str.pyx
new file mode 100644 (file)
index 0000000..f1a2ee5
--- /dev/null
+++ b/str.pyx
@@ -0,0 +1,105 @@
+### -*-pyrex-*-
+###
+### String utilities
+###
+### (c) 2006 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+def word(char *p, quotep = False):
+  """word(STR, [quotep = False]) -> WORD, REST"""
+  cdef unsigned f
+  cdef char *op
+  cdef char *pp
+  cdef char *q
+  cdef object w
+  cdef object r
+
+  f = 0
+  if quotep:
+    f = f | STRF_QUOTE
+  pp = op = xstrdup(p)
+  q = str_qword(&pp, f)
+  if q is NULL:
+    w = None
+  else:
+    w = q
+  if pp is NULL:
+    r = ''
+  else:
+    r = pp
+  xfree(op)
+  return w, r
+
+def split(char *p, int n = -1, quotep = False):
+  """split(STR, [n = -1], [quotep = False]) -> WORDS, REST"""
+  cdef unsigned f
+  cdef char *op
+  cdef char *pp
+  cdef char *q
+  cdef object l
+  cdef object r
+
+  f = 0
+  if quotep:
+    f = f | STRF_QUOTE
+  l = []
+  op = pp = xstrdup(p)
+  while n != 0:
+    q = str_qword(&pp, f)
+    if q is NULL:
+      break
+    l.append(q)
+    if n > 0:
+      n = n - 1
+  if pp is NULL:
+    r = ''
+  else:
+    r = pp
+  xfree(op)
+  return l, r
+
+def match(char *p, char *s, prefixp = False):
+  """match(PAT, STR, [prefixp = False]) -> BOOL"""
+  cdef unsigned f
+
+  f = 0
+  if prefixp:
+    f = f | STRF_PREFIX
+  return _tobool(str_matchx(p, s, f))
+
+def sanitize(char *p, int n = -1):
+  """sanitize(STR, [n = -1]) -> STR"""
+  cdef char *buf
+  cdef object d
+
+  if n < 0:
+    n = strlen(p)
+  buf = <char *>xmalloc(n + 1)
+  str_sanitize(buf, p, n + 1)
+  d = buf
+  xfree(buf)
+  return d
+
+def versioncmp(char *va, char *vb):
+  """versioncmp(V0, V1) -> -1 | 0 | +1"""
+  return _versioncmp(va, vb)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/stuff.pyx b/stuff.pyx
new file mode 100644 (file)
index 0000000..dcb8a9c
--- /dev/null
+++ b/stuff.pyx
@@ -0,0 +1,35 @@
+### -*-pyrex-*-
+###
+### Various small things
+###
+### (c) 2007 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+def detachtty():
+  """detachtty(): detach from terminal and fork"""
+  _detachtty()
+
+def daemonize():
+  """daemonize(): become a daemon"""
+  if _daemonize():
+    _oserror()
+
+###----- That's all, folks --------------------------------------------------
diff --git a/sym.pyx b/sym.pyx
new file mode 100644 (file)
index 0000000..f456db8
--- /dev/null
+++ b/sym.pyx
@@ -0,0 +1,85 @@
+### -*-pyrex-*-
+###
+### Symbol table, using universal hashing
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef struct _sym_entry:
+  sym_base _b
+  PyObject *v
+
+cdef class SymTable (Mapping):
+  """
+  SymTable([DICT], **KW)
+
+  A mapping keyed by strings.
+  """
+  cdef sym_table _t
+  cdef int _init(me) except -1:
+    sym_create(&me._t)
+    return 0
+  cdef void *_find(me, object key, unsigned *f) except NULL:
+    cdef void *p
+    cdef Py_ssize_t n
+    cdef _sym_entry *e
+    PyObject_AsReadBuffer(key, <cvp *>&p, &n)
+    if f:
+      f[0] = 0
+      e = <_sym_entry *>sym_find(&me._t, <char *>p, n, PSIZEOF(e), f)
+      if not f[0]:
+        e.v = NULL
+    else:
+      e = <_sym_entry *>sym_find(&me._t, <char *>p, n, 0, NULL)
+    return <void *>e
+  cdef object _key(me, void *e):
+    return PyString_FromStringAndSize(SYM_NAME(e), SYM_LEN(e))
+  cdef object _value(me, void *e):
+    cdef _sym_entry *ee
+    ee = <_sym_entry *>e
+    Py_INCREF(ee.v)
+    return <object>ee.v
+  cdef void _setval(me, void *e, object val):
+    cdef _sym_entry *ee
+    ee = <_sym_entry *>e
+    if ee.v:
+      Py_DECREF(ee.v)
+    ee.v = <PyObject *>v
+    Py_INCREF(ee.v)
+  cdef void _del(me, void *e):
+    cdef _sym_entry *ee
+    ee = <_sym_entry *>e
+    if ee.v:
+      Py_DECREF(ee.v)
+    sym_remove(&me._t, <void *>ee)
+  cdef _MapIterator _iter(me):
+    return _SymIter(me)
+
+cdef class _SymIter (_MapIterator):
+  cdef SymTable t
+  cdef sym_iter i
+  def __cinit__(me, SymTable t not None):
+    me.t = t
+    sym_mkiter(&me.i, &me.t._t)
+  cdef void *_next(me):
+    return sym_next(&me.i)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/unihash.pyx b/unihash.pyx
new file mode 100644 (file)
index 0000000..8ededb0
--- /dev/null
@@ -0,0 +1,66 @@
+### -*-pyrex-*-
+###
+### Universal hashing interface
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+def setglobalkey(uint32 k):
+  """setglobalkey(K): set global hash key"""
+  unihash_setkey(&unihash_global, k)
+
+cdef class Key:
+  """Key(K): universal hashing key"""
+  cdef unihash_info _i
+  cdef uint32 _k
+  def __cinit__(me, uint32 k):
+    unihash_setkey(&me._i, k)
+    me._k = k
+  property k:
+    """K.k -> INT: the key value"""
+    def __get__(me):
+      return _u32(me._k)
+
+cdef class Unihash:
+  """Unihash([key = None]): universal hashing context"""
+  cdef uint32 _a
+  cdef readonly Key key
+  cdef unihash_info *_i
+  def __cinit__(me, key = None):
+    cdef Key k
+    me.key = key
+    if key is None:
+      me._i = &unihash_global
+    else:
+      k = key
+      me._i = &k._i
+    me._a = UNIHASH_INIT(me._i)
+  def chunk(me, data):
+    """U.chunk(STR): hash the STR"""
+    cdef void *p
+    cdef Py_ssize_t n
+    PyObject_AsReadBuffer(data, <cvp *>&p, &n)
+    me._a = unihash_hash(me._i, me._a, p, n)
+  def done(me):
+    """U.done() -> INT: the hash of the data"""
+    return _u32(me._a)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/url.pyx b/url.pyx
new file mode 100644 (file)
index 0000000..72467f1
--- /dev/null
+++ b/url.pyx
@@ -0,0 +1,130 @@
+### -*-pyrex-*-
+###
+### Form-urlencoding functions
+###
+### (c) 2006 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef class URLEncode:
+  """URLEncode([strictp = False], [laxp = False], [semip = False])"""
+  cdef url_ectx ctx
+  cdef dstr d
+
+  def __cinit__(me, *hunoz, **hukairz):
+    url_initenc(&me.ctx)
+    DCREATE(&me.d)
+  def __init__(me, strictp = False, laxp = False, semip = False):
+    cdef unsigned f
+    f = 0
+    if strictp:
+      f = f | URLF_STRICT
+    if laxp:
+      f = f | URLF_LAX
+    if semip:
+      f = f | URLF_SEMI
+    me.ctx.f = f
+  def encode(me, char *name, char *value):
+    """UE.encode(NAME, VALUE): encode a key/value pair"""
+    url_enc(&me.ctx, &me.d, name, value)
+    return me
+  property result:
+    """UE.result -> STR: the encoded string"""
+    def __get__(me):
+      return PyString_FromStringAndSize(me.d.buf, me.d.len)
+  property strictp:
+    """UE.strictp -> BOOL: strictly escape non-alphanumerics?"""
+    def __get__(me):
+      return _tobool(me.ctx.f & URLF_STRICT)
+    def __set__(me, val):
+      if val:
+        me.ctx.f = me.ctx.f | URLF_STRICT
+      else:
+        me.ctx.f = me.ctx.f & ~URLF_STRICT
+  property laxp:
+    """UE.laxp -> BOOL: only escape obviously unsafe characters?"""
+    def __get__(me):
+      return _tobool(me.ctx.f & URLF_LAX)
+    def __set__(me, val):
+      if val:
+        me.ctx.f = me.ctx.f | URLF_LAX
+      else:
+        me.ctx.f = me.ctx.f & ~URLF_LAX
+  property semip:
+    """UE.semip -> BOOL: separate key/value pairs with semicolons?"""
+    def __get__(me):
+      return _tobool(me.ctx.f & URLF_SEMI)
+    def __set__(me, val):
+      if val:
+        me.ctx.f = me.ctx.f | URLF_SEMI
+      else:
+        me.ctx.f = me.ctx.f & ~URLF_SEMI
+  def __del__(me):
+    dstr_destroy(&me.d)
+
+cdef class URLDecode:
+  """URLDecode(STR, [semip = False]): iterator over (KEY, VALUE) pairs"""
+  cdef url_dctx ctx
+  cdef char *p
+
+  def __cinit__(me, *hunoz, **hukairz):
+    me.p = xstrdup('')
+    url_initdec(&me.ctx, me.p)
+  def __init__(me, char *string, semip = False):
+    cdef unsigned f
+    f = 0
+    if semip:
+      f = f | URLF_SEMI
+    xfree(me.p)
+    me.p = xstrdup(string)
+    me.ctx.p = me.p
+    me.ctx.f = f
+  def __iter__(me):
+    return me
+  def __next__(me):
+    cdef dstr n
+    cdef dstr v
+    cdef object nn
+    cdef object vv
+    cdef int anyp
+    DCREATE(&n)
+    DCREATE(&v)
+    anyp = url_dec(&me.ctx, &n, &v)
+    if anyp:
+      nn = PyString_FromStringAndSize(n.buf, n.len)
+      vv = PyString_FromStringAndSize(v.buf, v.len)
+    dstr_destroy(&n)
+    dstr_destroy(&v)
+    if not anyp:
+      raise StopIteration
+    return nn, vv
+  property semip:
+    """UD.semip -> BOOL: key/value pairs separated with semicolons?"""
+    def __get__(me):
+      return _tobool(me.ctx.f & URLF_SEMI)
+    def __set__(me, val):
+      if val:
+        me.ctx.f = me.ctx.f | URLF_SEMI
+      else:
+        me.ctx.f = me.ctx.f & ~URLF_SEMI
+  def __del__(me):
+    xfree(me.p)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/utils.pyx b/utils.pyx
new file mode 100644 (file)
index 0000000..8d4ffad
--- /dev/null
+++ b/utils.pyx
@@ -0,0 +1,59 @@
+### -*-pyrex-*-
+###
+### Miscellaneous support gubbins
+###
+### (c) 2005 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the Python interface to mLib.
+###
+### mLib/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.
+###
+### mLib/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 mLib/Python; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+cdef object _u32(uint32 x):
+  if x <= LONG_MAX:
+    return PyInt_FromLong(x)
+  else:
+    return PyLong_FromUnsignedLong(x)
+
+cdef object _oserror():
+  raise OSError, (errno, strerror(errno))
+
+cdef object _tobool(int i):
+  if i:
+    return True
+  else:
+    return False
+
+cdef int _getfd(object fdobj):
+  try:
+    fd = int(fdobj)
+  except TypeError:
+    PyErr_Clear()
+    fd = fdobj.fileno()
+  return fd
+
+cdef object _checkcallable(f, what):
+  if f is not None and not callable(f):
+    raise TypeError, '%s must be callable' % what
+  return f
+
+cdef object _maybecall(f, args):
+  if f is None:
+    return None
+  return f(*args)
+
+###----- That's all, folks --------------------------------------------------