rpm  4.5
rpmfts-py.c
Go to the documentation of this file.
1 
5 #include "system.h"
6 
7 #include "structmember.h"
8 
9 #ifdef __LCLINT__
10 #undef PyObject_HEAD
11 #define PyObject_HEAD int _PyObjectHead;
12 #endif
13 
14 #include <fts.h>
15 
16 #include "rpmfts-py.h"
17 
18 #include <rpmlib.h> /* XXX _free */
19 
20 #include "debug.h"
21 
22 /*@unchecked@*/
23 static int _rpmfts_debug = 1;
24 
25 #define infoBit(_ix) (1 << (((unsigned)(_ix)) & 0x1f))
26 
27 static const char * ftsInfoStrings[] = {
28  "UNKNOWN",
29  "D",
30  "DC",
31  "DEFAULT",
32  "DNR",
33  "DOT",
34  "DP",
35  "ERR",
36  "F",
37  "INIT",
38  "NS",
39  "NSOK",
40  "SL",
41  "SLNONE",
42  "W",
43 };
44 
45 /*@observer@*/
46 static const char * ftsInfoStr(int fts_info)
47  /*@*/
48 {
49  if (!(fts_info >= 1 && fts_info <= 14))
50  fts_info = 0;
51  return ftsInfoStrings[ fts_info ];
52 }
53 
54 #define RPMFTS_CLOSE 0
55 #define RPMFTS_OPEN 1
56 #define RPMFTS_OPEN_LAZY 2
57 
58 static void
59 rpmfts_debug (const char * msg, rpmftsObject * s)
60 {
61  if (_rpmfts_debug == 0)
62  return;
63  if (msg)
64  fprintf(stderr, "*** %s(%p)", msg, s);
65  if (s)
66  fprintf(stderr, " %d %d ftsp %p fts %p\n", s->ob_refcnt, s->active, s->ftsp, s->fts);
67 }
68 
69 static int
70 rpmfts_initialize(rpmftsObject * s, const char * root, int options, int ignore)
71  /*@modifies s @*/
72 {
73  int ac = 1;
74  size_t nb;
75 
76 /*@-branchstate@*/
77  if (root == NULL) root = "/";
78 /*@=branchstate@*/
79  if (options == -1) options = (FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOSTAT);
80  if (ignore == -1) ignore = infoBit(FTS_DP);
81 
82  s->roots = _free(s->roots);
83 
84  nb = (ac + 1) * sizeof(*s->roots);
85  nb += strlen(root) + 1;
86  s->roots = malloc(nb);
87  if (s->roots != NULL) {
88  char *t = (char *) &s->roots[ac + 1];
89  s->roots[0] = t;
90  s->roots[ac] = NULL;
91  (void) stpcpy(t, root);
92  }
93 
94  s->options = options;
95  s->ignore = ignore;
96  s->compare = NULL;
97 
98  s->ftsp = NULL;
99  s->fts = NULL;
100  s->active = RPMFTS_CLOSE;
101 
102  return 0;
103 
104 }
105 
106 static int
107 rpmfts_state(rpmftsObject * s, int nactive)
108  /*@modifies s @*/
109 {
110  int rc = 0;
111 
112 rpmfts_debug(__FUNCTION__, s);
113  switch (nactive) {
114  case RPMFTS_CLOSE:
115  if (s->ftsp != NULL) {
116  Py_BEGIN_ALLOW_THREADS
117  rc = Fts_close(s->ftsp);
118  Py_END_ALLOW_THREADS
119  s->ftsp = NULL;
120  }
121  break;
122  case RPMFTS_OPEN_LAZY:
123  case RPMFTS_OPEN:
124  if (s->ftsp == NULL) {
125  Py_BEGIN_ALLOW_THREADS
126  s->ftsp = Fts_open((char *const *)s->roots, s->options, (int (*)(const FTSENT **, const FTSENT **))s->compare);
127  Py_END_ALLOW_THREADS
128  }
129  break;
130  }
131  s->fts = NULL;
132  s->active = nactive;
133  return rc;
134 }
135 
136 /*@null@*/
137 static PyObject *
139  /*@modifies s @*/
140 {
141  PyObject * result = NULL;
142  int xx;
143 
144 rpmfts_debug(__FUNCTION__, s);
145  if (s->ftsp == NULL)
146  return NULL;
147 
148  do {
149  Py_BEGIN_ALLOW_THREADS
150  s->fts = Fts_read(s->ftsp);
151  Py_END_ALLOW_THREADS
152  } while (s->fts && (infoBit(s->fts->fts_info) & s->ignore));
153 
154  if (s->fts != NULL) {
155  Py_INCREF(s);
156  result = (PyObject *)s;
157  } else {
158  if (s->active == RPMFTS_OPEN_LAZY)
159  xx = rpmfts_state(s, RPMFTS_CLOSE);
160  s->active = RPMFTS_CLOSE;
161  }
162 
163  return result;
164 }
165 
166 /* ---------- */
167 
178 /*@null@*/
179 static PyObject *
180 rpmfts_Debug(/*@unused@*/ rpmftsObject * s, PyObject * args, PyObject * kwds)
181  /*@globals _Py_NoneStruct @*/
182  /*@modifies _Py_NoneStruct @*/
183 {
184  char * kwlist[] = {"debugLevel", NULL};
185 
186  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Debug", kwlist,
187  &_rpmfts_debug))
188  return NULL;
189 
190  Py_INCREF(Py_None);
191  return Py_None;
192 }
193 
194 /*@null@*/
195 static PyObject *
196 rpmfts_Open(rpmftsObject * s, PyObject * args, PyObject * kwds)
197  /*@modifies s @*/
198 {
199  char * root = NULL;
200  int options = -1;
201  int ignore = -1;
202  int xx;
203  /* XXX: there's bound to be a better name than "ignore" */
204  char * kwlist[] = {"root", "options", "ignore", NULL};
205 
206 rpmfts_debug(__FUNCTION__, s);
207  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sii:Open", kwlist,
208  &root, &options, &ignore))
209  return NULL;
210 
211  xx = rpmfts_initialize(s, root, options, ignore);
212  xx = rpmfts_state(s, RPMFTS_OPEN);
213 
214  return (PyObject *)s;
215 }
216 
217 /*@null@*/
218 static PyObject *
220  /*@globals _Py_NoneStruct @*/
221  /*@modifies s, _Py_NoneStruct @*/
222 {
223  PyObject * result;
224 
225 rpmfts_debug(__FUNCTION__, s);
226 
227  result = rpmfts_step(s);
228 
229  if (result == NULL) {
230  Py_INCREF(Py_None);
231  return Py_None;
232  }
233 
234  return result;
235 }
236 
237 /*@null@*/
238 static PyObject *
239 rpmfts_Children(rpmftsObject * s, PyObject * args, PyObject * kwds)
240  /*@globals _Py_NoneStruct @*/
241  /*@modifies s, _Py_NoneStruct @*/
242 {
243  int instr;
244  char * kwlist[] = {"instructions", NULL};
245 
246 rpmfts_debug(__FUNCTION__, s);
247  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Children", kwlist, &instr))
248  return NULL;
249 
250  if (!(s && s->ftsp))
251  return NULL;
252 
253  Py_BEGIN_ALLOW_THREADS
254  s->fts = Fts_children(s->ftsp, instr);
255  Py_END_ALLOW_THREADS
256 
257  Py_INCREF(Py_None);
258  return Py_None;
259 }
260 
261 /*@null@*/
262 static PyObject *
264  /*@modifies s @*/
265 {
266 
267 rpmfts_debug(__FUNCTION__, s);
268 
269  return Py_BuildValue("i", rpmfts_state(s, RPMFTS_CLOSE));
270 }
271 
272 /*@null@*/
273 static PyObject *
274 rpmfts_Set(rpmftsObject * s, PyObject * args, PyObject * kwds)
275  /*@modifies s @*/
276 {
277  int instr = 0;
278  int rc = 0;
279  char * kwlist[] = {"instructions", NULL};
280 
281 rpmfts_debug(__FUNCTION__, s);
282  if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Set", kwlist, &instr))
283  return NULL;
284 
285  if (s->ftsp && s->fts)
286  rc = Fts_set(s->ftsp, s->fts, instr);
287 
288  return Py_BuildValue("i", rc);
289 }
294 /*@-fullinitblock@*/
295 /*@unchecked@*/ /*@observer@*/
296 static struct PyMethodDef rpmfts_methods[] = {
297  {"Debug", (PyCFunction)rpmfts_Debug, METH_VARARGS|METH_KEYWORDS,
298  NULL},
299  {"open", (PyCFunction)rpmfts_Open, METH_VARARGS|METH_KEYWORDS,
300  NULL},
301  {"read", (PyCFunction)rpmfts_Read, METH_NOARGS,
302  NULL},
303  {"children",(PyCFunction)rpmfts_Children, METH_VARARGS|METH_KEYWORDS,
304  NULL},
305  {"close", (PyCFunction)rpmfts_Close, METH_NOARGS,
306  NULL},
307  {"set", (PyCFunction)rpmfts_Set, METH_VARARGS|METH_KEYWORDS,
308  NULL},
309  {NULL, NULL} /* sentinel */
310 };
311 /*@=fullinitblock@*/
312 
313 /* ---------- */
314 
315 static PyMemberDef rpmfts_members[] = {
316  {"__dict__",T_OBJECT,offsetof(rpmftsObject, md_dict), READONLY,
317  NULL},
318  {"callbacks",T_OBJECT,offsetof(rpmftsObject, callbacks), 0,
319 "Callback dictionary per fts_info state: FTS_{D|DC|DEFAULT|DNR|DOT|DP|ERR|F|INIT|NS|NSOK|SL|SLNONE|W}"},
320  {"options", T_INT, offsetof(rpmftsObject, options), 0,
321 "Option bit(s): FTS_{COMFOLLOW|LOGICAL|NOCHDIR|NOSTAT|PHYSICAL|SEEDOT|XDEV}"},
322  {"ignore", T_INT, offsetof(rpmftsObject, ignore), 0,
323 "Ignore bit(s): (1 << info) with info one of FTS_{D|DC|DEFAULT|DNR|DOT|DP|ERR|F|INIT|NS|NSOK|SL|SLNONE|W}"},
324  {NULL, 0, 0, 0, NULL}
325 };
326 
327 static PyObject * rpmfts_getattro(PyObject * o, PyObject * n)
328  /*@*/
329 {
330 rpmfts_debug(__FUNCTION__, (rpmftsObject *)o);
331  return PyObject_GenericGetAttr(o, n);
332 }
333 
334 static int rpmfts_setattro(PyObject * o, PyObject * n, PyObject * v)
335  /*@*/
336 {
337 rpmfts_debug(__FUNCTION__, (rpmftsObject *)o);
338  return PyObject_GenericSetAttr(o, n, v);
339 }
340 
341 /* ---------- */
342 
343 static PyObject *
345  /*@*/
346 {
347  Py_INCREF(s);
348  return (PyObject *)s;
349 }
350 
351 /*@null@*/
352 static PyObject *
354  /*@modifies s @*/
355 {
356  int xx;
357 
358  /* Reset loop indices on 1st entry. */
359  if (s->active == RPMFTS_CLOSE)
361  return rpmfts_step(s);
362 }
363 
364 /* ---------- */
365 
366 static void rpmfts_free(/*@only@*/ PyObject * s)
367  /*@*/
368 {
369  _PyObject_GC_Del(s);
370 }
371 
372 static PyObject * rpmfts_alloc(PyTypeObject * type, int nitems)
373  /*@*/
374 {
375  return PyType_GenericAlloc(type, nitems);
376 }
377 
378 static void rpmfts_dealloc(/*@only@*/ rpmftsObject * s)
379  /*@modifies s @*/
380 {
381  int xx;
382 
383 rpmfts_debug(__FUNCTION__, s);
384  xx = rpmfts_state(s, RPMFTS_CLOSE);
385 
386  s->roots = _free(s->roots);
387 
388  PyObject_GC_UnTrack((PyObject *)s);
389  if (s->md_dict != NULL) {
390  _PyModule_Clear((PyObject *)s);
391  Py_DECREF(s->md_dict);
392  }
393  if (s->callbacks != NULL) {
394  _PyModule_Clear((PyObject *)s);
395  Py_DECREF(s->callbacks);
396  }
397  _PyObject_GC_Del((PyObject *)s);
398 }
399 
400 static int rpmfts_init(rpmftsObject * s, PyObject *args, PyObject *kwds)
401  /*@modifies s @*/
402 {
403  char * root = NULL;
404  int options = -1;
405  int ignore = -1;
406  char * kwlist[] = {"root", "options", "ignore", NULL};
407 
408 rpmfts_debug(__FUNCTION__, s);
409  if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sii:rpmfts_init", kwlist,
410  &root, &options, &ignore))
411  return -1;
412 
413  return rpmfts_initialize(s, root, options, ignore);
414 }
415 
416 /*@null@*/
417 static PyObject * rpmfts_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
418  /*@*/
419 {
420  rpmftsObject *s;
421  PyObject *o;
422  PyObject *n = NULL;
423  char * kwlist[] = {0};
424 
425  /* All the other _new() functions claim to be _init in their errors...*/
426  if (!PyArg_ParseTupleAndKeywords(args, kwds, ":rpmfts_new", kwlist))
427  return NULL;
428 
429  if ((s = PyObject_GC_New(rpmftsObject, type)) == NULL)
430  return NULL;
431 rpmfts_debug(__FUNCTION__, s);
432 
433  s->md_dict = PyDict_New();
434  if (s->md_dict == NULL)
435  goto fail;
436  s->callbacks = PyDict_New();
437  if (s->md_dict == NULL)
438  goto fail;
439  if (type->tp_name) {
440  const char * name;
441  if ((name = strrchr(type->tp_name, '.')) != NULL)
442  name++;
443  else
444  name = type->tp_name;
445  n = PyString_FromString(name);
446  }
447  if (n != NULL && PyDict_SetItemString(s->md_dict, "__name__", n) != 0)
448  goto fail;
449  if (PyDict_SetItemString(s->md_dict, "__doc__", Py_None) != 0)
450  goto fail;
451 
452 #define CONSTANT(_v) \
453  PyDict_SetItemString(s->md_dict, #_v, o=PyInt_FromLong(_v)); Py_DECREF(o)
454 
457 
467 
470 
471  CONSTANT(FTS_D);
472  CONSTANT(FTS_DC);
474  CONSTANT(FTS_DNR);
475  CONSTANT(FTS_DOT);
476  CONSTANT(FTS_DP);
477  CONSTANT(FTS_ERR);
478  CONSTANT(FTS_F);
479  CONSTANT(FTS_NS);
481  CONSTANT(FTS_SL);
483  CONSTANT(FTS_W);
484 
487 
492 
493  s->roots = NULL;
494  s->compare = NULL;
495  s->ftsp = NULL;
496  s->fts = NULL;
497 
498  Py_XDECREF(n);
499  PyObject_GC_Track((PyObject *)s);
500  return (PyObject *)s;
501 
502  fail:
503  Py_XDECREF(n);
504  Py_DECREF(s);
505  return NULL;
506 }
507 
508 static int rpmfts_traverse(rpmftsObject * s, visitproc visit, void * arg)
509  /*@*/
510 {
511  if (s->md_dict != NULL)
512  return visit(s->md_dict, arg);
513  if (s->callbacks != NULL)
514  return visit(s->callbacks, arg);
515  return 0;
516 }
517 
518 static int rpmfts_print(rpmftsObject * s, FILE * fp, /*@unused@*/ int flags)
519  /*@globals fileSystem @*/
520  /*@modifies fp, fileSystem @*/
521 {
522  static int indent = 2;
523 
524  if (!(s != NULL && s->ftsp != NULL && s->fts != NULL))
525  return -1;
526  fprintf(fp, "FTS_%-7s %*s%s", ftsInfoStr(s->fts->fts_info),
527  indent * (s->fts->fts_level < 0 ? 0 : s->fts->fts_level), "",
528  s->fts->fts_name);
529  return 0;
530 }
531 
534 /*@unchecked@*/ /*@observer@*/
535 static char rpmfts_doc[] =
536 "";
537 
540 /*@-fullinitblock@*/
541 PyTypeObject rpmfts_Type = {
542  PyObject_HEAD_INIT(&PyType_Type)
543  0, /* ob_size */
544  "rpm.fts", /* tp_name */
545  sizeof(rpmftsObject), /* tp_size */
546  0, /* tp_itemsize */
547  /* methods */
548  (destructor) rpmfts_dealloc, /* tp_dealloc */
549  (printfunc) rpmfts_print, /* tp_print */
550  (getattrfunc)0, /* tp_getattr */
551  (setattrfunc)0, /* tp_setattr */
552  (cmpfunc)0, /* tp_compare */
553  (reprfunc)0, /* tp_repr */
554  0, /* tp_as_number */
555  0, /* tp_as_sequence */
556  0, /* tp_as_mapping */
557  (hashfunc)0, /* tp_hash */
558  (ternaryfunc)0, /* tp_call */
559  (reprfunc)0, /* tp_str */
560  (getattrofunc) rpmfts_getattro, /* tp_getattro */
561  (setattrofunc) rpmfts_setattro, /* tp_setattro */
562  0, /* tp_as_buffer */
563  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
564  rpmfts_doc, /* tp_doc */
565  (traverseproc) rpmfts_traverse, /* tp_traverse */
566  0, /* tp_clear */
567  0, /* tp_richcompare */
568  0, /* tp_weaklistoffset */
569  (getiterfunc) rpmfts_iter, /* tp_iter */
570  (iternextfunc) rpmfts_iternext, /* tp_iternext */
571  rpmfts_methods, /* tp_methods */
572  rpmfts_members, /* tp_members */
573  0, /* tp_getset */
574  0, /* tp_base */
575  0, /* tp_dict */
576  0, /* tp_descr_get */
577  0, /* tp_descr_set */
578  offsetof(rpmftsObject, md_dict),/* tp_dictoffset */
579  (initproc) rpmfts_init, /* tp_init */
580  rpmfts_alloc, /* tp_alloc */
581  rpmfts_new, /* tp_new */
582  rpmfts_free, /* tp_free */
583  0, /* tp_is_gc */
584 };
585 /*@=fullinitblock@*/