ab227c41dae30e6b268b0527fe7ef77103627ff6
[platform/upstream/rpm.git] / python / rpmds-py.c
1 #include "rpmsystem-py.h"
2
3 #include <rpm/rpmtypes.h>
4 #include <rpm/rpmstring.h>
5 #include <rpm/rpmlib.h>         /* rpmvercmp */
6
7 #include "header-py.h"
8 #include "rpmds-py.h"
9
10 #include "debug.h"
11
12 struct rpmdsObject_s {
13     PyObject_HEAD
14     PyObject *md_dict;          /*!< to look like PyModuleObject */
15     int         active;
16     rpmds       ds;
17 };
18
19
20 /**
21  * Split EVR into epoch, version, and release components.
22  * @param evr           [epoch:]version[-release] string
23  * @retval *ep          pointer to epoch
24  * @retval *vp          pointer to version
25  * @retval *rp          pointer to release
26  */
27 static
28 void rpmds_ParseEVR(char * evr,
29                 const char ** ep,
30                 const char ** vp,
31                 const char ** rp)
32 {
33     const char *epoch;
34     const char *version;                /* assume only version is present */
35     const char *release;
36     char *s, *se;
37
38     s = evr;
39     while (*s && risdigit(*s)) s++;     /* s points to epoch terminator */
40     se = strrchr(s, '-');               /* se points to version terminator */
41
42     if (*s == ':') {
43         epoch = evr;
44         *s++ = '\0';
45         version = s;
46         if (*epoch == '\0') epoch = "0";
47     } else {
48         epoch = NULL;   /* XXX disable epoch compare if missing */
49         version = evr;
50     }
51     if (se) {
52         *se++ = '\0';
53         release = se;
54     } else {
55         release = NULL;
56     }
57
58     if (ep) *ep = epoch;
59     if (vp) *vp = version;
60     if (rp) *rp = release;
61 }
62
63 static PyObject *
64 rpmds_Count(rpmdsObject * s)
65 {
66     return Py_BuildValue("i", rpmdsCount(s->ds));
67 }
68
69 static PyObject *
70 rpmds_Ix(rpmdsObject * s)
71 {
72     return Py_BuildValue("i", rpmdsIx(s->ds));
73 }
74
75 static PyObject *
76 rpmds_DNEVR(rpmdsObject * s)
77 {
78     return Py_BuildValue("s", rpmdsDNEVR(s->ds));
79 }
80
81 static PyObject *
82 rpmds_N(rpmdsObject * s)
83 {
84     return Py_BuildValue("s", rpmdsN(s->ds));
85 }
86
87 static PyObject *
88 rpmds_EVR(rpmdsObject * s)
89 {
90     return Py_BuildValue("s", rpmdsEVR(s->ds));
91 }
92
93 static PyObject *
94 rpmds_Flags(rpmdsObject * s)
95 {
96     return Py_BuildValue("i", rpmdsFlags(s->ds));
97 }
98
99 static PyObject *
100 rpmds_BT(rpmdsObject * s)
101 {
102     return Py_BuildValue("i", (int) rpmdsBT(s->ds));
103 }
104
105 static PyObject *
106 rpmds_TagN(rpmdsObject * s)
107 {
108     return Py_BuildValue("i", rpmdsTagN(s->ds));
109 }
110
111 static PyObject *
112 rpmds_Color(rpmdsObject * s)
113 {
114     return Py_BuildValue("i", rpmdsColor(s->ds));
115 }
116
117 static PyObject *
118 rpmds_Refs(rpmdsObject * s)
119 {
120     return Py_BuildValue("i", rpmdsRefs(s->ds));
121 }
122
123 static int compare_values(const char *str1, const char *str2)
124 {
125     if (!str1 && !str2)
126         return 0;
127     else if (str1 && !str2)
128         return 1;
129     else if (!str1 && str2)
130         return -1;
131     return rpmvercmp(str1, str2);
132 }
133
134 static int
135 rpmds_compare(rpmdsObject * a, rpmdsObject * b)
136 {
137     char *aEVR = xstrdup(rpmdsEVR(a->ds));
138     const char *aE, *aV, *aR;
139     char *bEVR = xstrdup(rpmdsEVR(b->ds));
140     const char *bE, *bV, *bR;
141     int rc;
142
143     /* XXX W2DO? should N be compared? */
144     rpmds_ParseEVR(aEVR, &aE, &aV, &aR);
145     rpmds_ParseEVR(bEVR, &bE, &bV, &bR);
146
147     rc = compare_values(aE, bE);
148     if (!rc) {
149         rc = compare_values(aV, bV);
150         if (!rc)
151             rc = compare_values(aR, bR);
152     }
153
154     aEVR = _free(aEVR);
155     bEVR = _free(bEVR);
156
157     return rc;
158 }
159
160 static PyObject *
161 rpmds_richcompare(rpmdsObject * a, rpmdsObject * b, int op)
162 {
163     int rc;
164
165     switch (op) {
166     case Py_NE:
167         /* XXX map ranges overlap boolean onto '!=' python syntax. */
168         rc = rpmdsCompare(a->ds, b->ds);
169         rc = (rc < 0 ? -1 : (rc == 0 ? 1 : 0));
170         break;
171     case Py_LT:
172     case Py_LE:
173     case Py_GT:
174     case Py_GE:
175     case Py_EQ:
176     default:
177         rc = -1;
178         break;
179     }
180     return Py_BuildValue("i", rc);
181 }
182
183 static PyObject *
184 rpmds_iternext(rpmdsObject * s)
185 {
186     PyObject * result = NULL;
187
188     /* Reset loop indices on 1st entry. */
189     if (!s->active) {
190         s->ds = rpmdsInit(s->ds);
191         s->active = 1;
192     }
193
194     /* If more to do, return a (N, EVR, Flags) tuple. */
195     if (rpmdsNext(s->ds) >= 0) {
196         const char * N = rpmdsN(s->ds);
197         const char * EVR = rpmdsEVR(s->ds);
198         rpmTag tagN = rpmdsTagN(s->ds);
199         rpmsenseFlags Flags = rpmdsFlags(s->ds);
200
201         result = rpmds_Wrap(s->ob_type, rpmdsSingle(tagN, N, EVR, Flags) );
202     } else
203         s->active = 0;
204
205     return result;
206 }
207
208 static PyObject *
209 rpmds_SetNoPromote(rpmdsObject * s, PyObject * args, PyObject * kwds)
210 {
211     int nopromote;
212     char * kwlist[] = {"noPromote", NULL};
213
214     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:SetNoPromote", kwlist,
215             &nopromote))
216         return NULL;
217
218     return Py_BuildValue("i", rpmdsSetNoPromote(s->ds, nopromote));
219 }
220
221 static PyObject *
222 rpmds_Notify(rpmdsObject * s, PyObject * args, PyObject * kwds)
223 {
224     const char * where;
225     int rc;
226     char * kwlist[] = {"location", "returnCode", NULL};
227
228     if (!PyArg_ParseTupleAndKeywords(args, kwds, "si:Notify", kwlist,
229             &where, &rc))
230         return NULL;
231
232     rpmdsNotify(s->ds, where, rc);
233     Py_RETURN_NONE;
234 }
235
236 /* XXX rpmdsFind uses bsearch on s->ds, so a sort is needed. */
237 static PyObject *
238 rpmds_Sort(rpmdsObject * s)
239 {
240     /* XXX sort on (N,EVR,F) here. */
241     Py_RETURN_NONE;
242 }
243
244 static PyObject *
245 rpmds_Find(rpmdsObject * s, PyObject * args, PyObject * kwds)
246 {
247     PyObject * to = NULL;
248     rpmdsObject * o;
249     int rc;
250     char * kwlist[] = {"element", NULL};
251
252     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:Find", kwlist, &to))
253         return NULL;
254
255     /* XXX ds type check needed. */
256     o = (rpmdsObject *)to;
257
258     /* XXX make sure ods index is valid, real fix in lib/rpmds.c. */
259     if (rpmdsIx(o->ds) == -1)   rpmdsSetIx(o->ds, 0);
260
261     rc = rpmdsFind(s->ds, o->ds);
262     return Py_BuildValue("i", rc);
263 }
264
265 static PyObject *
266 rpmds_Merge(rpmdsObject * s, PyObject * args, PyObject * kwds)
267 {
268     PyObject * to = NULL;
269     rpmdsObject * o;
270     char * kwlist[] = {"element", NULL};
271
272     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:Merge", kwlist, &to))
273         return NULL;
274
275     /* XXX ds type check needed. */
276     o = (rpmdsObject *)to;
277     return Py_BuildValue("i", rpmdsMerge(&s->ds, o->ds));
278 }
279 static PyObject *
280 rpmds_Search(rpmdsObject * s, PyObject * args, PyObject * kwds)
281 {
282     PyObject * to = NULL;
283     rpmdsObject * o;
284     char * kwlist[] = {"element", NULL};
285
286     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:Merge", kwlist, &to))
287         return NULL;
288
289     /* XXX ds type check needed. */
290     o = (rpmdsObject *)to;
291     return Py_BuildValue("i", rpmdsSearch(s->ds, o->ds));
292 }
293
294 static PyObject * rpmds_Rpmlib(rpmdsObject * s)
295 {
296     rpmds ds = NULL;
297     int xx;
298
299     /* XXX check return code, permit arg (NULL uses system default). */
300     xx = rpmdsRpmlib(&ds, NULL);
301
302     return rpmds_Wrap(&rpmds_Type, ds);
303 }
304
305
306 #ifdef  NOTYET
307 static PyObject *
308 rpmds_Compare(rpmdsObject * s, PyObject * args, PyObject * kwds)
309 {
310     PyObject * to = NULL;
311     rpmdsObject * o;
312     char * kwlist[] = {"other", NULL};
313
314     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:Compare", kwlist, &to))
315         return NULL;
316
317     /* XXX ds type check needed. */
318     o = (rpmdsObject *)to;
319     return Py_BuildValue("i", rpmdsCompare(s->ds, o->ds));
320 }
321
322 static PyObject *
323 rpmds_Problem(rpmdsObject * s)
324 {
325     if (!PyArg_ParseTuple(args, ":Problem"))
326         return NULL;
327     Py_RETURN_NONE;
328 }
329 #endif
330
331 static struct PyMethodDef rpmds_methods[] = {
332  {"Count",      (PyCFunction)rpmds_Count,       METH_NOARGS,
333         "ds.Count -> Count      - Return no. of elements.\n" },
334  {"Ix",         (PyCFunction)rpmds_Ix,          METH_NOARGS,
335         "ds.Ix -> Ix            - Return current element index.\n" },
336  {"DNEVR",      (PyCFunction)rpmds_DNEVR,       METH_NOARGS,
337         "ds.DNEVR -> DNEVR      - Return current DNEVR.\n" },
338  {"N",          (PyCFunction)rpmds_N,           METH_NOARGS,
339         "ds.N -> N              - Return current N.\n" },
340  {"EVR",        (PyCFunction)rpmds_EVR,         METH_NOARGS,
341         "ds.EVR -> EVR          - Return current EVR.\n" },
342  {"Flags",      (PyCFunction)rpmds_Flags,       METH_NOARGS,
343         "ds.Flags -> Flags      - Return current Flags.\n" },
344  {"BT",         (PyCFunction)rpmds_BT,          METH_NOARGS,
345         "ds.BT -> BT    - Return build time.\n" },
346  {"TagN",       (PyCFunction)rpmds_TagN,        METH_NOARGS,
347         "ds.TagN -> TagN        - Return current TagN.\n" },
348  {"Color",      (PyCFunction)rpmds_Color,       METH_NOARGS,
349         "ds.Color -> Color      - Return current Color.\n" },
350  {"Refs",       (PyCFunction)rpmds_Refs,        METH_NOARGS,
351         "ds.Refs -> Refs        - Return current Refs.\n" },
352  {"SetNoPromote",(PyCFunction)rpmds_SetNoPromote, METH_VARARGS|METH_KEYWORDS,
353         NULL},
354  {"Notify",     (PyCFunction)rpmds_Notify,      METH_VARARGS|METH_KEYWORDS,
355         NULL},
356  {"Sort",       (PyCFunction)rpmds_Sort,        METH_NOARGS,
357         NULL},
358  {"Find",       (PyCFunction)rpmds_Find,        METH_VARARGS|METH_KEYWORDS,
359         NULL},
360  {"Merge",      (PyCFunction)rpmds_Merge,       METH_VARARGS|METH_KEYWORDS,
361         NULL},
362  {"Search",     (PyCFunction)rpmds_Search,      METH_VARARGS|METH_KEYWORDS,
363 "ds.Search(element) -> matching ds index (-1 on failure)\n\
364 - Check that element dependency range overlaps some member of ds.\n\
365 The current index in ds is positioned at overlapping member upon success.\n" },
366  {"Rpmlib",     (PyCFunction)rpmds_Rpmlib,      METH_NOARGS|METH_STATIC,
367         "ds.Rpmlib -> nds       - Return internal rpmlib dependency set.\n"},
368 #ifdef  NOTYET
369  {"Compare",    (PyCFunction)rpmds_Compare,     METH_VARARGS|METH_KEYWORDS,
370         NULL},
371  {"Problem",    (PyCFunction)rpmds_Problem,     METH_NOARGS,
372         NULL},
373 #endif
374  {NULL,         NULL}           /* sentinel */
375 };
376
377 /* ---------- */
378
379 static void
380 rpmds_dealloc(rpmdsObject * s)
381 {
382     s->ds = rpmdsFree(s->ds);
383     s->ob_type->tp_free((PyObject *)s);
384 }
385
386 static int
387 rpmds_length(rpmdsObject * s)
388 {
389     return rpmdsCount(s->ds);
390 }
391
392 static PyObject *
393 rpmds_subscript(rpmdsObject * s, PyObject * key)
394 {
395     int ix;
396
397     if (!PyInt_Check(key)) {
398         PyErr_SetString(PyExc_TypeError, "integer expected");
399         return NULL;
400     }
401
402     ix = (int) PyInt_AsLong(key);
403     rpmdsSetIx(s->ds, ix);
404     return Py_BuildValue("s", rpmdsDNEVR(s->ds));
405 }
406
407 static PyMappingMethods rpmds_as_mapping = {
408         (lenfunc) rpmds_length,         /* mp_length */
409         (binaryfunc) rpmds_subscript,   /* mp_subscript */
410         (objobjargproc)0,               /* mp_ass_subscript */
411 };
412
413 static int rpmds_init(rpmdsObject * s, PyObject *args, PyObject *kwds)
414 {
415     s->active = 0;
416     return 0;
417 }
418
419 static PyObject * rpmds_new(PyTypeObject * subtype, PyObject *args, PyObject *kwds)
420 {
421     hdrObject * ho = NULL;
422     rpmTag tagN = RPMTAG_REQUIRENAME;
423     rpmsenseFlags flags = 0;
424     rpmds ds = NULL;
425     char * kwlist[] = {"header", "tag", "flags", NULL};
426
427     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O&i:rpmds_new", kwlist, 
428             &hdr_Type, &ho, tagNumFromPyObject, &tagN, &flags))
429         return NULL;
430
431     ds = rpmdsNew(hdrGetHeader(ho), tagN, 0);
432
433     return rpmds_Wrap(subtype, ds);
434 }
435
436 static char rpmds_doc[] =
437 "";
438
439 PyTypeObject rpmds_Type = {
440         PyObject_HEAD_INIT(&PyType_Type)
441         0,                              /* ob_size */
442         "rpm.ds",                       /* tp_name */
443         sizeof(rpmdsObject),            /* tp_basicsize */
444         0,                              /* tp_itemsize */
445         /* methods */
446         (destructor) rpmds_dealloc,     /* tp_dealloc */
447         0,                              /* tp_print */
448         (getattrfunc)0,                 /* tp_getattr */
449         (setattrfunc)0,                 /* tp_setattr */
450         (cmpfunc) rpmds_compare,        /* tp_compare */
451         (reprfunc)0,                    /* tp_repr */
452         0,                              /* tp_as_number */
453         0,                              /* tp_as_sequence */
454         &rpmds_as_mapping,              /* tp_as_mapping */
455         (hashfunc)0,                    /* tp_hash */
456         (ternaryfunc)0,                 /* tp_call */
457         (reprfunc)0,                    /* tp_str */
458         PyObject_GenericGetAttr,        /* tp_getattro */
459         PyObject_GenericSetAttr,        /* tp_setattro */
460         0,                              /* tp_as_buffer */
461         Py_TPFLAGS_DEFAULT |            /* tp_flags */
462             Py_TPFLAGS_HAVE_RICHCOMPARE |
463             Py_TPFLAGS_BASETYPE,
464         rpmds_doc,                      /* tp_doc */
465         0,                              /* tp_traverse */
466         0,                              /* tp_clear */
467         (richcmpfunc) rpmds_richcompare,/* tp_richcompare */
468         0,                              /* tp_weaklistoffset */
469         PyObject_SelfIter,              /* tp_iter */
470         (iternextfunc) rpmds_iternext,  /* tp_iternext */
471         rpmds_methods,                  /* tp_methods */
472         0,                              /* tp_members */
473         0,                              /* tp_getset */
474         0,                              /* tp_base */
475         0,                              /* tp_dict */
476         0,                              /* tp_descr_get */
477         0,                              /* tp_descr_set */
478         0,                              /* tp_dictoffset */
479         (initproc) rpmds_init,          /* tp_init */
480         0,                              /* tp_alloc */
481         (newfunc) rpmds_new,            /* tp_new */
482         0,                              /* tp_free */
483         0,                              /* tp_is_gc */
484 };
485
486 /* ---------- */
487
488 rpmds dsFromDs(rpmdsObject * s)
489 {
490     return s->ds;
491 }
492
493 PyObject * rpmds_Wrap(PyTypeObject *subtype, rpmds ds)
494 {
495     rpmdsObject * s = (rpmdsObject *)subtype->tp_alloc(subtype, 0);
496     if (s == NULL) return PyErr_NoMemory();
497
498     s->ds = ds;
499     s->active = 0;
500     return (PyObject *) s;
501 }
502
503 PyObject * rpmds_Single(PyObject * s, PyObject * args, PyObject * kwds)
504 {
505     rpmTag tagN = RPMTAG_PROVIDENAME;
506     const char * N;
507     const char * EVR = NULL;
508     rpmsenseFlags Flags = 0;
509     char * kwlist[] = {"to", "name", "evr", "flags", NULL};
510
511     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&s|si:Single", kwlist,
512             tagNumFromPyObject, &tagN, &N, &EVR, &Flags))
513         return NULL;
514
515     return rpmds_Wrap(&rpmds_Type, rpmdsSingle(tagN, N, EVR, Flags));
516 }
517
518 PyObject * hdr_dsFromHeader(PyObject * s, PyObject * args, PyObject * kwds)
519 {
520     return PyObject_Call((PyObject *) &rpmds_Type,
521                          Py_BuildValue("(O)", s), kwds);
522 }
523
524 PyObject * hdr_dsOfHeader(PyObject * s)
525 {
526     hdrObject * ho = (hdrObject *)s;
527     rpmTag tagN = RPMTAG_PROVIDENAME;
528     rpmsenseFlags Flags = RPMSENSE_EQUAL;
529
530     return rpmds_Wrap(&rpmds_Type, rpmdsThis(hdrGetHeader(ho), tagN, Flags));
531 }