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