/** \ingroup py_c * \file python/rpmfts-py.c */ #include "system.h" #include "structmember.h" #ifdef __LCLINT__ #undef PyObject_HEAD #define PyObject_HEAD int _PyObjectHead; #endif #include #include "rpmfts-py.h" #include /* XXX _free */ #include "debug.h" /*@unchecked@*/ static int _rpmfts_debug = 1; #define infoBit(_ix) (1 << (((unsigned)(_ix)) & 0x1f)) static const char * ftsInfoStrings[] = { "UNKNOWN", "D", "DC", "DEFAULT", "DNR", "DOT", "DP", "ERR", "F", "INIT", "NS", "NSOK", "SL", "SLNONE", "W", }; /*@observer@*/ static const char * ftsInfoStr(int fts_info) /*@*/ { if (!(fts_info >= 1 && fts_info <= 14)) fts_info = 0; return ftsInfoStrings[ fts_info ]; } #define RPMFTS_CLOSE 0 #define RPMFTS_OPEN 1 #define RPMFTS_OPEN_LAZY 2 static void rpmfts_debug (const char * msg, rpmftsObject * s) { if (_rpmfts_debug == 0) return; if (msg) fprintf(stderr, "*** %s(%p)", msg, s); if (s) fprintf(stderr, " %d %d ftsp %p fts %p\n", s->ob_refcnt, s->active, s->ftsp, s->fts); } static int rpmfts_initialize(rpmftsObject * s, const char * root, int options, int ignore) /*@modifies s @*/ { int ac = 1; size_t nb; /*@-branchstate@*/ if (root == NULL) root = "/"; /*@=branchstate@*/ if (options == -1) options = (FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOSTAT); if (ignore == -1) ignore = infoBit(FTS_DP); s->roots = _free(s->roots); nb = (ac + 1) * sizeof(*s->roots); nb += strlen(root) + 1; s->roots = malloc(nb); if (s->roots != NULL) { char *t = (char *) &s->roots[ac + 1]; s->roots[0] = t; s->roots[ac] = NULL; (void) stpcpy(t, root); } s->options = options; s->ignore = ignore; s->compare = NULL; s->ftsp = NULL; s->fts = NULL; s->active = RPMFTS_CLOSE; return 0; } static int rpmfts_state(rpmftsObject * s, int nactive) /*@modifies s @*/ { int rc = 0; rpmfts_debug(__FUNCTION__, s); switch (nactive) { case RPMFTS_CLOSE: if (s->ftsp != NULL) { Py_BEGIN_ALLOW_THREADS rc = Fts_close(s->ftsp); Py_END_ALLOW_THREADS s->ftsp = NULL; } break; case RPMFTS_OPEN_LAZY: case RPMFTS_OPEN: if (s->ftsp == NULL) { Py_BEGIN_ALLOW_THREADS s->ftsp = Fts_open((char *const *)s->roots, s->options, (int (*)(const FTSENT **, const FTSENT **))s->compare); Py_END_ALLOW_THREADS } break; } s->fts = NULL; s->active = nactive; return rc; } /*@null@*/ static PyObject * rpmfts_step(rpmftsObject * s) /*@modifies s @*/ { PyObject * result = NULL; int xx; rpmfts_debug(__FUNCTION__, s); if (s->ftsp == NULL) return NULL; do { Py_BEGIN_ALLOW_THREADS s->fts = Fts_read(s->ftsp); Py_END_ALLOW_THREADS } while (s->fts && (infoBit(s->fts->fts_info) & s->ignore)); if (s->fts != NULL) { Py_INCREF(s); result = (PyObject *)s; } else { if (s->active == RPMFTS_OPEN_LAZY) xx = rpmfts_state(s, RPMFTS_CLOSE); s->active = RPMFTS_CLOSE; } return result; } /* ---------- */ /** \ingroup python * \name Class: Rpmfts * \class Rpmfts * \brief A python rpm.fts object represents an rpm fts(3) handle. */ /*@null@*/ static PyObject * rpmfts_Debug(/*@unused@*/ rpmftsObject * s, PyObject * args, PyObject * kwds) /*@globals _Py_NoneStruct @*/ /*@modifies _Py_NoneStruct @*/ { char * kwlist[] = {"debugLevel", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Debug", kwlist, &_rpmfts_debug)) return NULL; Py_INCREF(Py_None); return Py_None; } /*@null@*/ static PyObject * rpmfts_Open(rpmftsObject * s, PyObject * args, PyObject * kwds) /*@modifies s @*/ { char * root = NULL; int options = -1; int ignore = -1; int xx; /* XXX: there's bound to be a better name than "ignore" */ char * kwlist[] = {"root", "options", "ignore", NULL}; rpmfts_debug(__FUNCTION__, s); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sii:Open", kwlist, &root, &options, &ignore)) return NULL; xx = rpmfts_initialize(s, root, options, ignore); xx = rpmfts_state(s, RPMFTS_OPEN); return (PyObject *)s; } /*@null@*/ static PyObject * rpmfts_Read(rpmftsObject * s) /*@globals _Py_NoneStruct @*/ /*@modifies s, _Py_NoneStruct @*/ { PyObject * result; rpmfts_debug(__FUNCTION__, s); result = rpmfts_step(s); if (result == NULL) { Py_INCREF(Py_None); return Py_None; } return result; } /*@null@*/ static PyObject * rpmfts_Children(rpmftsObject * s, PyObject * args, PyObject * kwds) /*@globals _Py_NoneStruct @*/ /*@modifies s, _Py_NoneStruct @*/ { int instr; char * kwlist[] = {"instructions", NULL}; rpmfts_debug(__FUNCTION__, s); if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Children", kwlist, &instr)) return NULL; if (!(s && s->ftsp)) return NULL; Py_BEGIN_ALLOW_THREADS s->fts = Fts_children(s->ftsp, instr); Py_END_ALLOW_THREADS Py_INCREF(Py_None); return Py_None; } /*@null@*/ static PyObject * rpmfts_Close(rpmftsObject * s) /*@modifies s @*/ { rpmfts_debug(__FUNCTION__, s); return Py_BuildValue("i", rpmfts_state(s, RPMFTS_CLOSE)); } /*@null@*/ static PyObject * rpmfts_Set(rpmftsObject * s, PyObject * args, PyObject * kwds) /*@modifies s @*/ { int instr = 0; int rc = 0; char * kwlist[] = {"instructions", NULL}; rpmfts_debug(__FUNCTION__, s); if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Set", kwlist, &instr)) return NULL; if (s->ftsp && s->fts) rc = Fts_set(s->ftsp, s->fts, instr); return Py_BuildValue("i", rc); } /** \ingroup py_c */ /*@-fullinitblock@*/ /*@unchecked@*/ /*@observer@*/ static struct PyMethodDef rpmfts_methods[] = { {"Debug", (PyCFunction)rpmfts_Debug, METH_VARARGS|METH_KEYWORDS, NULL}, {"open", (PyCFunction)rpmfts_Open, METH_VARARGS|METH_KEYWORDS, NULL}, {"read", (PyCFunction)rpmfts_Read, METH_NOARGS, NULL}, {"children",(PyCFunction)rpmfts_Children, METH_VARARGS|METH_KEYWORDS, NULL}, {"close", (PyCFunction)rpmfts_Close, METH_NOARGS, NULL}, {"set", (PyCFunction)rpmfts_Set, METH_VARARGS|METH_KEYWORDS, NULL}, {NULL, NULL} /* sentinel */ }; /*@=fullinitblock@*/ /* ---------- */ static PyMemberDef rpmfts_members[] = { {"__dict__",T_OBJECT,offsetof(rpmftsObject, md_dict), READONLY, NULL}, {"callbacks",T_OBJECT,offsetof(rpmftsObject, callbacks), 0, "Callback dictionary per fts_info state: FTS_{D|DC|DEFAULT|DNR|DOT|DP|ERR|F|INIT|NS|NSOK|SL|SLNONE|W}"}, {"options", T_INT, offsetof(rpmftsObject, options), 0, "Option bit(s): FTS_{COMFOLLOW|LOGICAL|NOCHDIR|NOSTAT|PHYSICAL|SEEDOT|XDEV}"}, {"ignore", T_INT, offsetof(rpmftsObject, ignore), 0, "Ignore bit(s): (1 << info) with info one of FTS_{D|DC|DEFAULT|DNR|DOT|DP|ERR|F|INIT|NS|NSOK|SL|SLNONE|W}"}, {NULL, 0, 0, 0, NULL} }; static PyObject * rpmfts_getattro(PyObject * o, PyObject * n) /*@*/ { rpmfts_debug(__FUNCTION__, (rpmftsObject *)o); return PyObject_GenericGetAttr(o, n); } static int rpmfts_setattro(PyObject * o, PyObject * n, PyObject * v) /*@*/ { rpmfts_debug(__FUNCTION__, (rpmftsObject *)o); return PyObject_GenericSetAttr(o, n, v); } /* ---------- */ static PyObject * rpmfts_iter(rpmftsObject * s) /*@*/ { Py_INCREF(s); return (PyObject *)s; } /*@null@*/ static PyObject * rpmfts_iternext(rpmftsObject * s) /*@modifies s @*/ { int xx; /* Reset loop indices on 1st entry. */ if (s->active == RPMFTS_CLOSE) xx = rpmfts_state(s, RPMFTS_OPEN_LAZY); return rpmfts_step(s); } /* ---------- */ static void rpmfts_free(/*@only@*/ PyObject * s) /*@*/ { _PyObject_GC_Del(s); } static PyObject * rpmfts_alloc(PyTypeObject * type, int nitems) /*@*/ { return PyType_GenericAlloc(type, nitems); } static void rpmfts_dealloc(/*@only@*/ rpmftsObject * s) /*@modifies s @*/ { int xx; rpmfts_debug(__FUNCTION__, s); xx = rpmfts_state(s, RPMFTS_CLOSE); s->roots = _free(s->roots); PyObject_GC_UnTrack((PyObject *)s); if (s->md_dict != NULL) { _PyModule_Clear((PyObject *)s); Py_DECREF(s->md_dict); } if (s->callbacks != NULL) { _PyModule_Clear((PyObject *)s); Py_DECREF(s->callbacks); } _PyObject_GC_Del((PyObject *)s); } static int rpmfts_init(rpmftsObject * s, PyObject *args, PyObject *kwds) /*@modifies s @*/ { char * root = NULL; int options = -1; int ignore = -1; char * kwlist[] = {"root", "options", "ignore", NULL}; rpmfts_debug(__FUNCTION__, s); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sii:rpmfts_init", kwlist, &root, &options, &ignore)) return -1; return rpmfts_initialize(s, root, options, ignore); } /*@null@*/ static PyObject * rpmfts_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /*@*/ { rpmftsObject *s; PyObject *o; PyObject *n = NULL; char * kwlist[] = {0}; /* All the other _new() functions claim to be _init in their errors...*/ if (!PyArg_ParseTupleAndKeywords(args, kwds, ":rpmfts_new", kwlist)) return NULL; if ((s = PyObject_GC_New(rpmftsObject, type)) == NULL) return NULL; rpmfts_debug(__FUNCTION__, s); s->md_dict = PyDict_New(); if (s->md_dict == NULL) goto fail; s->callbacks = PyDict_New(); if (s->md_dict == NULL) goto fail; if (type->tp_name) { char * name; if ((name = strrchr(type->tp_name, '.')) != NULL) name++; else name = type->tp_name; n = PyString_FromString(name); } if (n != NULL && PyDict_SetItemString(s->md_dict, "__name__", n) != 0) goto fail; if (PyDict_SetItemString(s->md_dict, "__doc__", Py_None) != 0) goto fail; #define CONSTANT(_v) \ PyDict_SetItemString(s->md_dict, #_v, o=PyInt_FromLong(_v)); Py_DECREF(o) CONSTANT(FTS_ROOTPARENTLEVEL); CONSTANT(FTS_ROOTLEVEL); CONSTANT(FTS_COMFOLLOW); CONSTANT(FTS_LOGICAL); CONSTANT(FTS_NOCHDIR); CONSTANT(FTS_NOSTAT); CONSTANT(FTS_PHYSICAL); CONSTANT(FTS_SEEDOT); CONSTANT(FTS_XDEV); CONSTANT(FTS_WHITEOUT); CONSTANT(FTS_OPTIONMASK); CONSTANT(FTS_NAMEONLY); CONSTANT(FTS_STOP); CONSTANT(FTS_D); CONSTANT(FTS_DC); CONSTANT(FTS_DEFAULT); CONSTANT(FTS_DNR); CONSTANT(FTS_DOT); CONSTANT(FTS_DP); CONSTANT(FTS_ERR); CONSTANT(FTS_F); CONSTANT(FTS_NS); CONSTANT(FTS_NSOK); CONSTANT(FTS_SL); CONSTANT(FTS_SLNONE); CONSTANT(FTS_W); CONSTANT(FTS_DONTCHDIR); CONSTANT(FTS_SYMFOLLOW); CONSTANT(FTS_AGAIN); CONSTANT(FTS_FOLLOW); CONSTANT(FTS_NOINSTR); CONSTANT(FTS_SKIP); s->roots = NULL; s->compare = NULL; s->ftsp = NULL; s->fts = NULL; Py_XDECREF(n); PyObject_GC_Track((PyObject *)s); return (PyObject *)s; fail: Py_XDECREF(n); Py_DECREF(s); return NULL; } static int rpmfts_traverse(rpmftsObject * s, visitproc visit, void * arg) /*@*/ { if (s->md_dict != NULL) return visit(s->md_dict, arg); if (s->callbacks != NULL) return visit(s->callbacks, arg); return 0; } static int rpmfts_print(rpmftsObject * s, FILE * fp, /*@unused@*/ int flags) /*@globals fileSystem @*/ /*@modifies fp, fileSystem @*/ { static int indent = 2; if (!(s != NULL && s->ftsp != NULL && s->fts != NULL)) return -1; fprintf(fp, "FTS_%-7s %*s%s", ftsInfoStr(s->fts->fts_info), indent * (s->fts->fts_level < 0 ? 0 : s->fts->fts_level), "", s->fts->fts_name); return 0; } /** */ /*@unchecked@*/ /*@observer@*/ static char rpmfts_doc[] = ""; /** \ingroup py_c */ /*@-fullinitblock@*/ PyTypeObject rpmfts_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "rpm.fts", /* tp_name */ sizeof(rpmftsObject), /* tp_size */ 0, /* tp_itemsize */ /* methods */ (destructor) rpmfts_dealloc, /* tp_dealloc */ (printfunc) rpmfts_print, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ (cmpfunc)0, /* tp_compare */ (reprfunc)0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)0, /* tp_hash */ (ternaryfunc)0, /* tp_call */ (reprfunc)0, /* tp_str */ (getattrofunc) rpmfts_getattro, /* tp_getattro */ (setattrofunc) rpmfts_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ rpmfts_doc, /* tp_doc */ (traverseproc) rpmfts_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc) rpmfts_iter, /* tp_iter */ (iternextfunc) rpmfts_iternext, /* tp_iternext */ rpmfts_methods, /* tp_methods */ rpmfts_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(rpmftsObject, md_dict),/* tp_dictoffset */ (initproc) rpmfts_init, /* tp_init */ rpmfts_alloc, /* tp_alloc */ rpmfts_new, /* tp_new */ rpmfts_free, /* tp_free */ 0, /* tp_is_gc */ }; /*@=fullinitblock@*/