diff --git a/swh/loader/cvs/rcsparse/py-rcsparse.c b/swh/loader/cvs/rcsparse/py-rcsparse.c index bddde7c..2bc6309 100644 --- a/swh/loader/cvs/rcsparse/py-rcsparse.c +++ b/swh/loader/cvs/rcsparse/py-rcsparse.c @@ -1,786 +1,842 @@ /* * This file is part of rcsparse. * * rcsparse is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * rcsparse 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with rcsparse. If not, see . */ #include #include #include #include "queue.h" #include "rcsparse.h" +#if PY_MAJOR_VERSION >= 3 +#define PyString_AsStringAndSize _PyUnicode_AsUTF8AndSize +#define PyString_CheckExact PyUnicode_CheckExact +#define PyString_FromString PyUnicode_FromString +#define PyString_FromStringAndSize PyUnicode_FromStringAndSize +#endif + +static void +_PyUnicode_AsUTF8AndSize(PyObject *obj, char **strp, Py_ssize_t *sizep) +{ + *strp = PyUnicode_AsUTF8AndSize(obj, sizep); +} + static PyObject * rcstoken2pystr(struct rcstoken *tok) { if (tok == NULL) Py_RETURN_NONE; return PyString_FromStringAndSize(tok->str, tok->len); } static PyObject * rcstoklist2py(struct rcstoklist *head) { PyObject *list; struct rcstoken *tok; list = PyList_New(0); if (list == NULL) return NULL; for (tok = SLIST_FIRST(head); tok != NULL; tok = SLIST_NEXT(tok, link)) { PyObject *o; o = rcstoken2pystr(tok); if (PyList_Append(list, o) < 0) { Py_XDECREF(o); Py_XDECREF(list); return NULL; } Py_XDECREF(o); } return list; } struct pyrcsrevtree { PyObject_HEAD struct pyrcsfile *pyrcs; struct rcsrevtree *tree; }; static int pyrcsrevtree_find_internal(struct pyrcsrevtree *self, PyObject *key, struct rcsrev **frev) { struct rcsrev rev; struct rcstoken tok; Py_ssize_t l; if (!PyString_CheckExact(key)) return -1; PyString_AsStringAndSize(key, &tok.str, &l); if (l < 0) return -1; tok.len = (unsigned)l; rev.rev = &tok; *frev = RB_FIND(rcsrevtree, self->tree, &rev); return *frev != NULL; } static PyObject * rcsrev2py(struct rcsrev *rev) { struct tm tm; const char *month; bzero(&tm, sizeof(struct tm)); #define readdate(str, dest, len) do { \ const char *pos; \ int scale; \ for (pos = str + len - 1, scale = 1; pos >= str; pos--, scale *= 10) \ if (*pos < '0' || *pos > '9') \ return PyErr_Format(PyExc_RuntimeError, "Invalid date format"); \ else \ dest += scale * (*pos - '0'); \ } while (0) if (rev->date->len == 17) { /* 2-digit year */ readdate(rev->date->str, tm.tm_year, 2); month = rev->date->str + 3; } else { /* 4-digit year */ readdate(rev->date->str, tm.tm_year, 4); tm.tm_year -= 1900; month = rev->date->str + 5; } readdate(month, tm.tm_mon, 2); tm.tm_mon--; readdate(month + 3, tm.tm_mday, 2); readdate(month + 6, tm.tm_hour, 2); readdate(month + 9, tm.tm_min, 2); readdate(month + 12, tm.tm_sec, 2); #undef readdate return Py_BuildValue("NNNNNNN", rcstoken2pystr(rev->rev), +#if PY_MAJOR_VERSION >= 3 + PyLong_FromLong(timegm(&tm)), +#else PyInt_FromLong(timegm(&tm)), +#endif rcstoken2pystr(rev->author), rcstoken2pystr(rev->state), rcstoklist2py(&rev->branches), rcstoken2pystr(rev->next), rcstoken2pystr(rev->commitid)); } static PyObject * pyrcsrevtree_find(struct pyrcsrevtree *self, PyObject *key) { struct rcsrev *frev; switch (pyrcsrevtree_find_internal(self, key, &frev)) { case 1: return rcsrev2py(frev); case 0: PyErr_SetObject(PyExc_KeyError, key); return NULL; case -1: default: return NULL; } } static PyObject * pyrcsrevtree_get(struct pyrcsrevtree *self, PyObject *args) { PyObject *key, *def = Py_None; struct rcsrev *frev; if (!PyArg_ParseTuple(args, "O|O", &key, &def)) return NULL; switch (pyrcsrevtree_find_internal(self, key, &frev)) { case 1: return rcsrev2py(frev); case 0: return Py_INCREF(def), def; case -1: default: return NULL; } } static int pyrcsrevtree_contains(struct pyrcsrevtree *self, PyObject *key) { struct rcsrev *rev; return pyrcsrevtree_find_internal(self, key, &rev); } static PyObject * pyrcsrevtree_has_key(struct pyrcsrevtree *self, PyObject *key) { switch (pyrcsrevtree_contains(self, key)) { case 1: Py_RETURN_TRUE; case 0: Py_RETURN_FALSE; case -1: default: return NULL; } } static PyObject * pyrcsrevtree_items(struct pyrcsrevtree *self) { PyObject *list; struct rcsrev *rev; list = PyList_New(0); if (list == NULL) return NULL; for (rev = RB_MIN(rcsrevtree, self->tree); rev != NULL; rev = RB_NEXT(rcsrevtree, self->tree, rev)) { PyObject *f, *s, *p; f = rcstoken2pystr(rev->rev); s = rcsrev2py(rev); p = PyTuple_Pack(2, f, s); Py_XDECREF(f); Py_XDECREF(s); if (PyList_Append(list, p) < 0) { Py_XDECREF(p); Py_DECREF(list); return NULL; } Py_XDECREF(p); } return list; } static PyObject * pyrcsrevtree_keys(struct pyrcsrevtree *self) { PyObject *list; struct rcsrev *rev; list = PyList_New(0); if (list == NULL) return NULL; for (rev = RB_MIN(rcsrevtree, self->tree); rev != NULL; rev = RB_NEXT(rcsrevtree, self->tree, rev)) { PyObject *i; i = rcstoken2pystr(rev->rev); if (PyList_Append(list, i) < 0) { Py_XDECREF(i); Py_DECREF(list); return NULL; } Py_XDECREF(i); } return list; } static PyObject * pyrcsrevtree_values(struct pyrcsrevtree *self) { PyObject *list; struct rcsrev *rev; list = PyList_New(0); if (list == NULL) return NULL; for (rev = RB_MIN(rcsrevtree, self->tree); rev != NULL; rev = RB_NEXT(rcsrevtree, self->tree, rev)) { PyObject *i; i = rcsrev2py(rev); if (PyList_Append(list, i) < 0) { Py_XDECREF(i); Py_DECREF(list); return NULL; } Py_XDECREF(i); } return list; } static void pyrcsrevtree_dealloc(struct pyrcsrevtree *self) { Py_DECREF((PyObject *)self->pyrcs); - self->ob_type->tp_free(self); + Py_TYPE(self)->tp_free(self); } static PyMappingMethods pyrcsrevtree_mapmethods = { NULL, (binaryfunc)pyrcsrevtree_find, NULL }; static PySequenceMethods pyrcsrevtree_seqmethods = { .sq_contains= (objobjproc)pyrcsrevtree_contains }; static PyMethodDef pyrcsrevtree_methods[] = { {"__contains__",(PyCFunction)pyrcsrevtree_has_key, METH_O | METH_COEXIST, NULL}, {"__getitem__", (PyCFunction)pyrcsrevtree_find, METH_O | METH_COEXIST, NULL}, {"has_key", (PyCFunction)pyrcsrevtree_has_key, METH_O, NULL}, {"get", (PyCFunction)pyrcsrevtree_get, METH_VARARGS, NULL}, {"keys", (PyCFunction)pyrcsrevtree_keys, METH_NOARGS, NULL}, {"items", (PyCFunction)pyrcsrevtree_items, METH_NOARGS, NULL}, {"values", (PyCFunction)pyrcsrevtree_values, METH_NOARGS, NULL}, {NULL} }; static PyTypeObject pyrcsrevtree_type = { - PyObject_HEAD_INIT(&PyType_Type) + PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name= "rcsparse.rcsrevtree", .tp_basicsize= sizeof(struct pyrcsrevtree), .tp_dealloc= (destructor)pyrcsrevtree_dealloc, .tp_as_mapping= &pyrcsrevtree_mapmethods, .tp_as_sequence= &pyrcsrevtree_seqmethods, .tp_flags= Py_TPFLAGS_DEFAULT, .tp_doc= "RCS Revision Tree Map", .tp_new= PyType_GenericNew, .tp_methods= pyrcsrevtree_methods }; static PyObject * rcsrevtree2py(struct pyrcsfile *pyrcs, struct rcsrevtree *tree) { struct pyrcsrevtree *pytree; if (tree == NULL) Py_RETURN_NONE; pytree = PyObject_New(struct pyrcsrevtree, &pyrcsrevtree_type); pytree->pyrcs = pyrcs; Py_INCREF((PyObject *)pyrcs); pytree->tree = tree; return (PyObject *)pytree; } struct pyrcstokmap { PyObject_HEAD struct pyrcsfile *pyrcs; struct rcstokmap *map; }; static int pyrcstokmap_find_internal(struct pyrcstokmap *self, PyObject *key, struct rcstokpair **fpair) { struct rcstokpair pair; struct rcstoken tok; Py_ssize_t l; if (!PyString_CheckExact(key)) return -1; PyString_AsStringAndSize(key, &tok.str, &l); if (l < 0) return -1; tok.len = (unsigned)l; pair.first = &tok; *fpair = RB_FIND(rcstokmap, self->map, &pair); return *fpair != NULL; } static PyObject * pyrcstokmap_find(struct pyrcstokmap *self, PyObject *key) { struct rcstokpair *fpair; switch (pyrcstokmap_find_internal(self, key, &fpair)) { case 1: return rcstoken2pystr(fpair->second); case 0: PyErr_SetObject(PyExc_KeyError, key); return NULL; case -1: default: return NULL; } } static PyObject * pyrcstokmap_get(struct pyrcstokmap *self, PyObject *args) { PyObject *key, *def = Py_None; struct rcstokpair *fpair; if (!PyArg_ParseTuple(args, "O|O", &key, &def)) return NULL; switch (pyrcstokmap_find_internal(self, key, &fpair)) { case 1: return rcstoken2pystr(fpair->second); case 0: return Py_INCREF(def), def; case -1: default: return NULL; } } static int pyrcstokmap_contains(struct pyrcstokmap *self, PyObject *key) { struct rcstokpair *pair; return pyrcstokmap_find_internal(self, key, &pair); } static PyObject * pyrcstokmap_has_key(struct pyrcstokmap *self, PyObject *key) { switch (pyrcstokmap_contains(self, key)) { case 1: Py_RETURN_TRUE; case 0: Py_RETURN_FALSE; case -1: default: return NULL; } } static PyObject * pyrcstokmap_items(struct pyrcstokmap *self) { PyObject *list; struct rcstokpair *pair; list = PyList_New(0); if (list == NULL) return NULL; for (pair = RB_MIN(rcstokmap, self->map); pair != NULL; pair = RB_NEXT(rcstokmap, self->map, pair)) { PyObject *f, *s, *p; f = rcstoken2pystr(pair->first); s = rcstoken2pystr(pair->second); p = PyTuple_Pack(2, f, s); Py_XDECREF(f); Py_XDECREF(s); if (PyList_Append(list, p) < 0) { Py_XDECREF(p); Py_DECREF(list); return NULL; } Py_XDECREF(p); } return list; } static PyObject * pyrcstokmap_keys(struct pyrcstokmap *self) { PyObject *list; struct rcstokpair *pair; list = PyList_New(0); if (list == NULL) return NULL; for (pair = RB_MIN(rcstokmap, self->map); pair != NULL; pair = RB_NEXT(rcstokmap, self->map, pair)) { PyObject *i; i = rcstoken2pystr(pair->first); if (PyList_Append(list, i) < 0) { Py_XDECREF(i); Py_DECREF(list); return NULL; } Py_XDECREF(i); } return list; } static PyObject * pyrcstokmap_values(struct pyrcstokmap *self) { PyObject *list; struct rcstokpair *pair; list = PyList_New(0); if (list == NULL) return NULL; for (pair = RB_MIN(rcstokmap, self->map); pair != NULL; pair = RB_NEXT(rcstokmap, self->map, pair)) { PyObject *i; i = rcstoken2pystr(pair->second); if (PyList_Append(list, i) < 0) { Py_XDECREF(i); Py_DECREF(list); return NULL; } Py_XDECREF(i); } return list; } static void pyrcstokmap_dealloc(struct pyrcstokmap *self) { Py_DECREF((PyObject *)self->pyrcs); - self->ob_type->tp_free(self); + Py_TYPE(self)->tp_free(self); } static PyMappingMethods pyrcstokmap_mapmethods = { NULL, (binaryfunc)pyrcstokmap_find, NULL }; static PySequenceMethods pyrcstokmap_seqmethods = { .sq_contains= (objobjproc)pyrcstokmap_contains }; static PyMethodDef pyrcstokmap_methods[] = { {"__contains__",(PyCFunction)pyrcstokmap_has_key, METH_O | METH_COEXIST, NULL}, {"__getitem__", (PyCFunction)pyrcstokmap_find, METH_O | METH_COEXIST, NULL}, {"has_key", (PyCFunction)pyrcstokmap_has_key, METH_O, NULL}, {"get", (PyCFunction)pyrcstokmap_get, METH_VARARGS, NULL}, {"keys", (PyCFunction)pyrcstokmap_keys, METH_NOARGS, NULL}, {"items", (PyCFunction)pyrcstokmap_items, METH_NOARGS, NULL}, {"values", (PyCFunction)pyrcstokmap_values, METH_NOARGS, NULL}, {NULL} }; static PyTypeObject pyrcstokmap_type = { - PyObject_HEAD_INIT(&PyType_Type) + PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name= "rcsparse.rcstokmap", .tp_basicsize= sizeof(struct pyrcstokmap), .tp_dealloc= (destructor)pyrcstokmap_dealloc, .tp_as_mapping= &pyrcstokmap_mapmethods, .tp_as_sequence= &pyrcstokmap_seqmethods, .tp_flags= Py_TPFLAGS_DEFAULT, .tp_doc= "RCS Token Map", .tp_new= PyType_GenericNew, .tp_methods= pyrcstokmap_methods }; static PyObject * rcstokmap2py(struct pyrcsfile *pyrcs, struct rcstokmap *map) { struct pyrcstokmap *pymap; if (map == NULL) Py_RETURN_NONE; pymap = PyObject_New(struct pyrcstokmap, &pyrcstokmap_type); pymap->pyrcs = pyrcs; Py_INCREF((PyObject *)pyrcs); pymap->map = map; return (PyObject *)pymap; } struct pyrcsfile { PyObject_HEAD struct rcsfile *rcs; }; enum { PYRCSADM_HEAD, PYRCSADM_BRANCH, PYRCSADM_SYMBOLS, PYRCSADM_LOCKS, PYRCSADM_COMMENT, PYRCSADM_EXPAND, PYRCSADM_DESC, }; static PyObject * pyrcsfile_getstr(struct pyrcsfile *self, void *closure) { struct rcstoken *tok; struct rcsadmin *adm; if (rcsparseadmin(self->rcs) < 0) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); adm = &self->rcs->admin; switch ((int)(uintptr_t)closure) { case PYRCSADM_HEAD: tok = adm->head; break; case PYRCSADM_BRANCH: tok = adm->branch; break; case PYRCSADM_COMMENT: tok = adm->comment; break; case PYRCSADM_EXPAND: tok = adm->expand; break; case PYRCSADM_DESC: tok = adm->desc; break; default: return PyErr_Format(PyExc_RuntimeError, "Wrong closure"); } return rcstoken2pystr(tok); } static PyObject * pyrcsfile_gettokmap(struct pyrcsfile *self, void *closure) { struct rcstokmap *map; struct rcsadmin *adm; if (rcsparseadmin(self->rcs) < 0) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); adm = &self->rcs->admin; switch ((int)(uintptr_t)closure) { case PYRCSADM_SYMBOLS: map = &adm->symbols; break; case PYRCSADM_LOCKS: map = &adm->locks; break; default: return PyErr_Format(PyExc_RuntimeError, "Wrong closure"); } return rcstokmap2py(self, map); } static PyObject * pyrcsfile_getaccess(struct pyrcsfile *self, void *closure) { if (rcsparseadmin(self->rcs) < 0) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); return rcstoklist2py(&self->rcs->admin.access); } static PyObject * pyrcsfile_getstrict(struct pyrcsfile *self, void *closure) { if (rcsparseadmin(self->rcs) < 0) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); if (self->rcs->admin.strict) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject * pyrcsfile_checkout(struct pyrcsfile *self, PyObject *args) { PyObject *o; const char *rev = "HEAD"; char *buf; size_t len; if (!PyArg_ParseTuple(args, "|s", &rev)) return NULL; buf = rcscheckout(self->rcs, rev, &len); if (buf == NULL) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); +#if PY_MAJOR_VERSION >= 3 + o = PyBytes_FromStringAndSize(buf, len); +#else o = PyString_FromStringAndSize(buf, len); +#endif free(buf); return o; } static PyObject * pyrcsfile_getlog(struct pyrcsfile *self, PyObject *args) { PyObject *o; const char *rev; char *buf; if (!PyArg_ParseTuple(args, "s", &rev)) return NULL; buf = rcsgetlog(self->rcs, rev); if (buf == NULL) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); +#if PY_MAJOR_VERSION >= 3 + o = PyBytes_FromString(buf); +#else o = PyString_FromString(buf); +#endif free(buf); return o; } static PyObject * pyrcsfile_sym2rev(struct pyrcsfile *self, PyObject *args) { PyObject *o; const char *rev = "HEAD"; char *buf; if (!PyArg_ParseTuple(args, "|s", &rev)) return NULL; buf = rcsrevfromsym(self->rcs, rev); if (buf == NULL) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); o = PyString_FromString(buf); free(buf); return o; } static PyObject * pyrcsfile_getrevs(struct pyrcsfile *self, void *closure) { if (rcsparsetree(self->rcs) < 0) return PyErr_Format(PyExc_RuntimeError, "Error parsing"); return rcsrevtree2py(self, &self->rcs->admin.revs); } static int pyrcsfile_init(struct pyrcsfile *pyrcs, PyObject *args) { const char *filename; if (!PyArg_ParseTuple(args, "s", &filename)) return -1; pyrcs->rcs = rcsopen(filename); if (pyrcs->rcs == NULL) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, (char *)(long)filename); return -1; } return 0; } static void pyrcsfile_dealloc(struct pyrcsfile *self) { if (self->rcs != NULL) rcsclose(self->rcs); - self->ob_type->tp_free(self); + Py_TYPE(self)->tp_free(self); } static PyGetSetDef pyrcsfile_getseters[] = { {"head", (getter)pyrcsfile_getstr, NULL, "rcsfile head data", (void *)PYRCSADM_HEAD}, {"branch", (getter)pyrcsfile_getstr, NULL, "rcsfile branch data", (void *)PYRCSADM_BRANCH}, {"access", (getter)pyrcsfile_getaccess, NULL, "rcsfile access data", NULL}, {"symbols", (getter)pyrcsfile_gettokmap, NULL, "rcsfile symbols data", (void *)PYRCSADM_SYMBOLS}, {"locks", (getter)pyrcsfile_gettokmap, NULL, "rcsfile locks data", (void *)PYRCSADM_LOCKS}, {"strict", (getter)pyrcsfile_getstrict, NULL, "rcsfile strict data", NULL}, {"comment", (getter)pyrcsfile_getstr, NULL, "rcsfile comment data", (void *)PYRCSADM_COMMENT}, {"expand", (getter)pyrcsfile_getstr, NULL, "rcsfile expand data", (void *)PYRCSADM_EXPAND}, {"revs", (getter)pyrcsfile_getrevs, NULL, "rcsfile revs data", NULL}, {"desc", (getter)pyrcsfile_getstr, NULL, "rcsfile desc data", (void *)PYRCSADM_DESC}, {NULL} }; static PyMethodDef pyrcsfile_methods[] = { {"checkout", (PyCFunction)pyrcsfile_checkout, METH_VARARGS, NULL}, {"getlog", (PyCFunction)pyrcsfile_getlog, METH_VARARGS, NULL}, {"sym2rev", (PyCFunction)pyrcsfile_sym2rev, METH_VARARGS, NULL}, {NULL} }; static PyTypeObject pyrcsfile_type = { PyObject_HEAD_INIT(&PyType_Type) .tp_name= "rcsparse.rcsfile", .tp_basicsize= sizeof(struct pyrcsfile), .tp_dealloc= (destructor)pyrcsfile_dealloc, .tp_flags= Py_TPFLAGS_DEFAULT, .tp_doc= "RCS File", .tp_getset= pyrcsfile_getseters, .tp_init= (initproc)pyrcsfile_init, .tp_new= PyType_GenericNew, .tp_methods= pyrcsfile_methods, }; static PyMethodDef pyrcsparse_methods[] = { {NULL} }; +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "rcsparse", /* m_name */ + "RCS file parser", /* m_doc */ + -1, /* m_size */ + pyrcsparse_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + +#if PY_MAJOR_VERSION >= 3 +#define retnull return NULL + +PyMODINIT_FUNC +PyInit_rcsparse(void) +#else +#define retnull return + PyMODINIT_FUNC initrcsparse(void) +#endif { PyObject *m; if (PyType_Ready(&pyrcsfile_type) < 0) - return; + retnull; if (PyType_Ready(&pyrcstokmap_type) < 0) - return; + retnull; if (PyType_Ready(&pyrcsrevtree_type) < 0) - return; + retnull; +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&moduledef); +#else m = Py_InitModule3("rcsparse", pyrcsparse_methods, "RCS file parser"); +#endif if (m == NULL) - return; + retnull; Py_INCREF(&pyrcsfile_type); PyModule_AddObject(m, "rcsfile", (PyObject *)&pyrcsfile_type); Py_INCREF(&pyrcstokmap_type); PyModule_AddObject(m, "rcstokmap", (PyObject *)&pyrcstokmap_type); Py_INCREF(&pyrcsrevtree_type); PyModule_AddObject(m, "rcsrevtree", (PyObject *)&pyrcsrevtree_type); + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif } diff --git a/swh/loader/cvs/rcsparse/testmodule.py b/swh/loader/cvs/rcsparse/testmodule.py index 2f0d388..ed9d0a8 100644 --- a/swh/loader/cvs/rcsparse/testmodule.py +++ b/swh/loader/cvs/rcsparse/testmodule.py @@ -1,13 +1,13 @@ import rcsparse -import md5 f=rcsparse.rcsfile('test,v') print f.head print f.branch s=f.symbols print s['RELENG_4'] print s.items() r=f.revs i=r.items() print i -print f.getlog(f.sym2rev('RELENG_4')) +print f.getlog(f.sym2rev('RELENG_4')).decode('ascii') +print '1.1' in r