Rpm's allocators dont return on OOM, remove unnecessary check
[platform/upstream/rpm.git] / python / header-py.c
1 #include "rpmsystem-py.h"
2
3 #include <rpm/rpmlib.h>         /* rpmvercmp */
4 #include <rpm/rpmtag.h>
5 #include <rpm/rpmstring.h>
6 #include <rpm/rpmts.h>  /* XXX rpmtsCreate/rpmtsFree */
7
8 #include "header-py.h"
9 #include "rpmds-py.h"
10 #include "rpmfi-py.h"
11
12 #include "debug.h"
13
14 /** \ingroup python
15  * \class Rpm
16  * \brief START HERE / RPM base module for the Python API
17  *
18  * The rpm base module provides the main starting point for
19  * accessing RPM from Python. For most usage, call
20  * the TransactionSet method to get a transaction set (rpmts).
21  *
22  * For example:
23  * \code
24  *      import rpm
25  *
26  *      ts = rpm.TransactionSet()
27  * \endcode
28  *
29  * The transaction set will open the RPM database as needed, so
30  * in most cases, you do not need to explicitly open the
31  * database. The transaction set is the workhorse of RPM.
32  *
33  * You can open another RPM database, such as one that holds
34  * all packages for a given Linux distribution, to provide
35  * packages used to solve dependencies. To do this, use
36  * the following code:
37  *
38  * \code
39  * rpm.addMacro('_dbpath', '/path/to/alternate/database')
40  * solvets = rpm.TransactionSet()
41  * solvets.openDB()
42  * rpm.delMacro('_dbpath')
43  *
44  * # Open default database
45  * ts = rpm.TransactionSet()
46  * \endcode
47  *
48  * This code gives you access to two RPM databases through
49  * two transaction sets (rpmts): ts is a transaction set
50  * associated with the default RPM database and solvets
51  * is a transaction set tied to an alternate database, which
52  * is very useful for resolving dependencies.
53  *
54  * The rpm methods used here are:
55  *
56  * - addMacro(macro, value)
57  * @param macro   Name of macro to add
58  * @param value   Value for the macro
59  *
60  * - delMacro(macro)
61  * @param macro   Name of macro to delete
62  *
63  */
64
65 /** \ingroup python
66  * \class Rpmhdr
67  * \brief A python header object represents an RPM package header.
68  *
69  * All RPM packages have headers that provide metadata for the package.
70  * Header objects can be returned by database queries or loaded from a
71  * binary package on disk.
72  *
73  * The ts.hdrFromFdno() function returns the package header from a
74  * package on disk, verifying package signatures and digests of the
75  * package while reading.
76  *
77  * Note: The older method rpm.headerFromPackage() which has been replaced
78  * by ts.hdrFromFdno() used to return a (hdr, isSource) tuple.
79  *
80  * If you need to distinguish source/binary headers, do:
81  * \code
82  *      import os, rpm
83  *
84  *      ts = rpm.TransactionSet()
85  *      fdno = os.open("/tmp/foo-1.0-1.i386.rpm", os.O_RDONLY)
86  *      hdr = ts.hdrFromFdno(fdno)
87  *      os.close(fdno)
88  *      if hdr[rpm.RPMTAG_SOURCEPACKAGE]:
89  *         print "header is from a source package"
90  *      else:
91  *         print "header is from a binary package"
92  * \endcode
93  *
94  * The Python interface to the header data is quite elegant.  It
95  * presents the data in a dictionary form.  We'll take the header we
96  * just loaded and access the data within it:
97  * \code
98  *      print hdr[rpm.RPMTAG_NAME]
99  *      print hdr[rpm.RPMTAG_VERSION]
100  *      print hdr[rpm.RPMTAG_RELEASE]
101  * \endcode
102  * in the case of our "foo-1.0-1.i386.rpm" package, this code would
103  * output:
104 \verbatim
105         foo
106         1.0
107         1
108 \endverbatim
109  *
110  * You make also access the header data by string name:
111  * \code
112  *      print hdr['name']
113  *      print hdr['version']
114  *      print hdr['release']
115  * \endcode
116  *
117  * This method of access is a teensy bit slower because the name must be
118  * translated into the tag number dynamically. You also must make sure
119  * the strings in header lookups don't get translated, or the lookups
120  * will fail.
121  */
122
123 /** \ingroup python
124  * \name Class: rpm.hdr
125  */
126
127 struct hdrObject_s {
128     PyObject_HEAD
129     Header h;
130 } ;
131
132 static PyObject * hdrKeyList(hdrObject * s)
133 {
134     PyObject * list, *o;
135     HeaderIterator hi;
136     rpmtd td = rpmtdNew();
137
138     list = PyList_New(0);
139
140     hi = headerInitIterator(s->h);
141     while (headerNext(hi, td)) {
142         rpmTag tag = rpmtdTag(td);
143         if (tag == HEADER_I18NTABLE) continue;
144
145         switch (rpmtdType(td)) {
146         case RPM_BIN_TYPE:
147         case RPM_INT32_TYPE:
148         case RPM_CHAR_TYPE:
149         case RPM_INT8_TYPE:
150         case RPM_INT16_TYPE:
151         case RPM_STRING_ARRAY_TYPE:
152         case RPM_STRING_TYPE:
153             PyList_Append(list, o=PyInt_FromLong(tag));
154             Py_DECREF(o);
155             break;
156         case RPM_I18NSTRING_TYPE: /* hum.. ?`*/
157         case RPM_NULL_TYPE:
158         default:
159             break;
160         }
161         rpmtdFreeData(td);
162     }
163     headerFreeIterator(hi);
164     rpmtdFree(td);
165
166     return list;
167 }
168
169 static PyObject * hdrUnload(hdrObject * s, PyObject * args, PyObject *keywords)
170 {
171     char * buf;
172     PyObject * rc;
173     int len, legacy = 0;
174     Header h;
175     static char *kwlist[] = { "legacyHeader", NULL};
176
177     if (!PyArg_ParseTupleAndKeywords(args, keywords, "|i", kwlist, &legacy))
178         return NULL;
179
180     h = headerLink(s->h);
181     /* XXX this legacy switch is a hack, needs to be removed. */
182     if (legacy) {
183         h = headerCopy(s->h);   /* XXX strip region tags, etc */
184         headerFree(s->h);
185     }
186     len = headerSizeof(h, 0);
187     buf = headerUnload(h);
188     h = headerFree(h);
189
190     if (buf == NULL || len == 0) {
191         PyErr_SetString(pyrpmError, "can't unload bad header\n");
192         return NULL;
193     }
194
195     rc = PyString_FromStringAndSize(buf, len);
196     buf = _free(buf);
197
198     return rc;
199 }
200
201 static PyObject * hdrExpandFilelist(hdrObject * s)
202 {
203     headerConvert(s->h, HEADERCONV_EXPANDFILELIST);
204
205     Py_RETURN_NONE;
206 }
207
208 static PyObject * hdrCompressFilelist(hdrObject * s)
209 {
210     headerConvert(s->h, HEADERCONV_COMPRESSFILELIST);
211
212     Py_RETURN_NONE;
213 }
214
215 /* make a header with _all_ the tags we need */
216 static void mungeFilelist(Header h)
217 {
218     rpmtd fileNames = rpmtdNew();
219
220     if (!headerIsEntry (h, RPMTAG_BASENAMES)
221         || !headerIsEntry (h, RPMTAG_DIRNAMES)
222         || !headerIsEntry (h, RPMTAG_DIRINDEXES))
223         headerConvert(h, HEADERCONV_COMPRESSFILELIST);
224
225     if (headerGet(h, RPMTAG_FILENAMES, fileNames, HEADERGET_EXT)) {
226         rpmtdSetTag(fileNames, RPMTAG_OLDFILENAMES);
227         headerPut(h, fileNames, HEADERPUT_DEFAULT);
228         rpmtdFreeData(fileNames);
229     }
230     rpmtdFree(fileNames);
231 }
232
233 static PyObject * hdrFullFilelist(hdrObject * s)
234 {
235     mungeFilelist (s->h);
236
237     Py_RETURN_NONE;
238 }
239
240 static PyObject * hdrSprintf(hdrObject * s, PyObject * args, PyObject * kwds)
241 {
242     char * fmt;
243     char * r;
244     errmsg_t err;
245     PyObject * result;
246     char * kwlist[] = {"format", NULL};
247
248     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &fmt))
249         return NULL;
250
251     r = headerFormat(s->h, fmt, &err);
252     if (!r) {
253         PyErr_SetString(pyrpmError, err);
254         return NULL;
255     }
256
257     result = Py_BuildValue("s", r);
258     r = _free(r);
259
260     return result;
261 }
262
263 static PyObject *hdrIsSource(hdrObject *s)
264 {
265     return PyBool_FromLong(headerIsSource(s->h));
266 }
267
268 static int hdr_compare(hdrObject * a, hdrObject * b)
269 {
270     return rpmVersionCompare(a->h, b->h);
271 }
272
273 static long hdr_hash(PyObject * h)
274 {
275     return (long) h;
276 }
277
278 static struct PyMethodDef hdr_methods[] = {
279     {"keys",            (PyCFunction) hdrKeyList,       METH_NOARGS,
280         NULL },
281     {"unload",          (PyCFunction) hdrUnload,        METH_VARARGS|METH_KEYWORDS,
282         NULL },
283     {"expandFilelist",  (PyCFunction) hdrExpandFilelist,METH_NOARGS,
284         NULL },
285     {"compressFilelist",(PyCFunction) hdrCompressFilelist,METH_NOARGS,
286         NULL },
287     {"fullFilelist",    (PyCFunction) hdrFullFilelist,  METH_NOARGS,
288         NULL },
289     {"sprintf",         (PyCFunction) hdrSprintf,       METH_VARARGS|METH_KEYWORDS,
290         NULL },
291     {"isSource",        (PyCFunction)hdrIsSource,       METH_NOARGS, 
292         NULL },
293
294     {"dsOfHeader",      (PyCFunction)hdr_dsOfHeader,    METH_NOARGS,
295         NULL},
296     {"dsFromHeader",    (PyCFunction)hdr_dsFromHeader,  METH_VARARGS|METH_KEYWORDS,
297         NULL},
298     {"fiFromHeader",    (PyCFunction)hdr_fiFromHeader,  METH_VARARGS|METH_KEYWORDS,
299         NULL},
300
301     {NULL,              NULL}           /* sentinel */
302 };
303
304 static void hdr_dealloc(hdrObject * s)
305 {
306     if (s->h) headerFree(s->h);
307     PyObject_Del(s);
308 }
309
310 rpmTag tagNumFromPyObject (PyObject *item)
311 {
312     rpmTag tag = RPMTAG_NOT_FOUND;
313
314     if (PyInt_Check(item)) {
315         /* XXX we should probably validate tag numbers too */
316         tag = PyInt_AsLong(item);
317     } else if (PyString_Check(item)) {
318         tag = rpmTagGetValue(PyString_AsString(item));
319     }
320     if (tag == RPMTAG_NOT_FOUND) {
321         PyErr_SetString(PyExc_ValueError, "unknown header tag");
322     }
323         
324     return tag;
325 }
326
327 static PyObject * hdr_subscript(hdrObject * s, PyObject * item)
328 {
329     rpmTagType tagtype, type;
330     rpmTag tag = RPMTAG_NOT_FOUND;
331     rpm_count_t count, i;
332     rpm_data_t data;
333     PyObject * o, * metao;
334     char ** stringArray;
335     int forceArray = 0;
336     struct rpmtd_s td;
337
338     tag = tagNumFromPyObject (item);
339     if (tag == RPMTAG_NOT_FOUND) return NULL;
340
341     tagtype = rpmTagGetType(tag); 
342     forceArray = (tagtype & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE;
343
344     /* Retrieve data from extension or header. */
345     if (!headerGet(s->h, tag, &td, HEADERGET_EXT)) {
346         if (forceArray) {
347             o = PyList_New(0);
348         } else {
349             o = Py_None;
350             Py_INCREF(o);
351         }
352         return o;
353     }
354
355     count = td.count;
356     data = td.data;
357     type = td.type;
358
359     switch (type) {
360     case RPM_BIN_TYPE:
361         o = PyString_FromStringAndSize(data, count);
362         break;
363
364     case RPM_INT32_TYPE:
365         if (count != 1 || forceArray) {
366             metao = PyList_New(0);
367             for (i = 0; i < count; i++) {
368                 o = PyInt_FromLong(((int *) data)[i]);
369                 PyList_Append(metao, o);
370                 Py_DECREF(o);
371             }
372             o = metao;
373         } else {
374             o = PyInt_FromLong(*((int *) data));
375         }
376         break;
377
378     case RPM_CHAR_TYPE:
379     case RPM_INT8_TYPE:
380         if (count != 1 || forceArray) {
381             metao = PyList_New(0);
382             for (i = 0; i < count; i++) {
383                 o = PyInt_FromLong(((char *) data)[i]);
384                 PyList_Append(metao, o);
385                 Py_DECREF(o);
386             }
387             o = metao;
388         } else {
389             o = PyInt_FromLong(*((char *) data));
390         }
391         break;
392
393     case RPM_INT16_TYPE:
394         if (count != 1 || forceArray) {
395             metao = PyList_New(0);
396             for (i = 0; i < count; i++) {
397                 o = PyInt_FromLong(((short *) data)[i]);
398                 PyList_Append(metao, o);
399                 Py_DECREF(o);
400             }
401             o = metao;
402         } else {
403             o = PyInt_FromLong(*((short *) data));
404         }
405         break;
406
407     case RPM_STRING_ARRAY_TYPE:
408         stringArray = data;
409
410         metao = PyList_New(0);
411         for (i = 0; i < count; i++) {
412             o = PyString_FromString(stringArray[i]);
413             PyList_Append(metao, o);
414             Py_DECREF(o);
415         }
416         o = metao;
417         break;
418
419     case RPM_STRING_TYPE:
420     case RPM_I18NSTRING_TYPE:
421         if (count != 1 || forceArray) {
422             stringArray = data;
423
424             metao = PyList_New(0);
425             for (i=0; i < count; i++) {
426                 o = PyString_FromString(stringArray[i]);
427                 PyList_Append(metao, o);
428                 Py_DECREF(o);
429             }
430             o = metao;
431         } else {
432             o = PyString_FromString(data);
433         }
434         break;
435
436     default:
437         PyErr_SetString(PyExc_TypeError, "unsupported type in header");
438         return NULL;
439     }
440     rpmtdFreeData(&td);
441
442     return o;
443 }
444
445 static PyObject * hdr_getattro(PyObject * o, PyObject * n)
446 {
447     PyObject * res;
448     res = PyObject_GenericGetAttr(o, n);
449     if (res == NULL)
450         res = hdr_subscript((hdrObject *)o, n);
451     return res;
452 }
453
454 static int hdr_setattro(PyObject * o, PyObject * n, PyObject * v)
455 {
456     return PyObject_GenericSetAttr(o, n, v);
457 }
458
459 static PyMappingMethods hdr_as_mapping = {
460         (lenfunc) 0,                    /* mp_length */
461         (binaryfunc) hdr_subscript,     /* mp_subscript */
462         (objobjargproc)0,               /* mp_ass_subscript */
463 };
464
465 static char hdr_doc[] =
466 "";
467
468 PyTypeObject hdr_Type = {
469         PyObject_HEAD_INIT(&PyType_Type)
470         0,                              /* ob_size */
471         "rpm.hdr",                      /* tp_name */
472         sizeof(hdrObject),              /* tp_size */
473         0,                              /* tp_itemsize */
474         (destructor) hdr_dealloc,       /* tp_dealloc */
475         0,                              /* tp_print */
476         (getattrfunc) 0,                /* tp_getattr */
477         0,                              /* tp_setattr */
478         (cmpfunc) hdr_compare,          /* tp_compare */
479         0,                              /* tp_repr */
480         0,                              /* tp_as_number */
481         0,                              /* tp_as_sequence */
482         &hdr_as_mapping,                /* tp_as_mapping */
483         hdr_hash,                       /* tp_hash */
484         0,                              /* tp_call */
485         0,                              /* tp_str */
486         (getattrofunc) hdr_getattro,    /* tp_getattro */
487         (setattrofunc) hdr_setattro,    /* tp_setattro */
488         0,                              /* tp_as_buffer */
489         Py_TPFLAGS_DEFAULT,             /* tp_flags */
490         hdr_doc,                        /* tp_doc */
491         0,                              /* tp_traverse */
492         0,                              /* tp_clear */
493         0,                              /* tp_richcompare */
494         0,                              /* tp_weaklistoffset */
495         0,                              /* tp_iter */
496         0,                              /* tp_iternext */
497         hdr_methods,                    /* tp_methods */
498         0,                              /* tp_members */
499         0,                              /* tp_getset */
500         0,                              /* tp_base */
501         0,                              /* tp_dict */
502         0,                              /* tp_descr_get */
503         0,                              /* tp_descr_set */
504         0,                              /* tp_dictoffset */
505         0,                              /* tp_init */
506         0,                              /* tp_alloc */
507         0,                              /* tp_new */
508         0,                              /* tp_free */
509         0,                              /* tp_is_gc */
510 };
511
512 PyObject * hdr_Wrap(Header h)
513 {
514     hdrObject * hdr = PyObject_New(hdrObject, &hdr_Type);
515     if (hdr == NULL) return PyErr_NoMemory();
516
517     hdr->h = headerLink(h);
518     return (PyObject *) hdr;
519 }
520
521 Header hdrGetHeader(hdrObject * s)
522 {
523     return s->h;
524 }
525
526 PyObject * hdrLoad(PyObject * self, PyObject * args, PyObject * kwds)
527 {
528     PyObject * hdr;
529     char * obj;
530     Header h;
531     int len;
532     char * kwlist[] = {"headers", NULL};
533
534     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#", kwlist, &obj, &len))
535         return NULL;
536
537     /* copy is needed to avoid surprises from data swab in headerLoad(). */
538     h = headerCopyLoad(obj);
539     if (!h) {
540         PyErr_SetString(pyrpmError, "bad header");
541         return NULL;
542     }
543     headerConvert(h, HEADERCONV_RETROFIT_V3);
544
545     hdr = hdr_Wrap(h);
546     h = headerFree(h);  /* XXX ref held by hdr */
547
548     return hdr;
549 }
550
551 PyObject * rpmReadHeaders (FD_t fd)
552 {
553     PyObject * list;
554     Header h;
555     PyObject * hdr;
556
557     if (!fd) {
558         PyErr_SetFromErrno(pyrpmError);
559         return NULL;
560     }
561
562     list = PyList_New(0);
563     Py_BEGIN_ALLOW_THREADS
564     h = headerRead(fd, HEADER_MAGIC_YES);
565     Py_END_ALLOW_THREADS
566
567     while (h) {
568         headerConvert(h, HEADERCONV_RETROFIT_V3);
569         hdr = hdr_Wrap(h);
570         if (PyList_Append(list, (PyObject *) hdr)) {
571             Py_DECREF(list);
572             Py_DECREF(hdr);
573             return NULL;
574         }
575         Py_DECREF(hdr);
576
577         h = headerFree(h);      /* XXX ref held by hdr */
578
579         Py_BEGIN_ALLOW_THREADS
580         h = headerRead(fd, HEADER_MAGIC_YES);
581         Py_END_ALLOW_THREADS
582     }
583
584     return list;
585 }
586
587 PyObject * rpmHeaderFromFD(PyObject * self, PyObject * args, PyObject * kwds)
588 {
589     FD_t fd;
590     int fileno;
591     PyObject * list;
592     char * kwlist[] = {"fd", NULL};
593
594     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &fileno))
595         return NULL;
596
597     fd = fdDup(fileno);
598
599     list = rpmReadHeaders (fd);
600     Fclose(fd);
601
602     return list;
603 }
604
605 PyObject * rpmHeaderFromFile(PyObject * self, PyObject * args, PyObject *kwds)
606 {
607     char * filespec;
608     FD_t fd;
609     PyObject * list;
610     char * kwlist[] = {"file", NULL};
611
612     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &filespec))
613         return NULL;
614
615     fd = Fopen(filespec, "r.fdio");
616
617     if (!fd) {
618         PyErr_SetFromErrno(pyrpmError);
619         return NULL;
620     }
621
622     list = rpmReadHeaders (fd);
623     Fclose(fd);
624
625     return list;
626 }
627
628 /**
629  * This assumes the order of list matches the order of the new headers, and
630  * throws an exception if that isn't true.
631  */
632 int rpmMergeHeaders(PyObject * list, FD_t fd, int matchTag)
633 {
634     Header h;
635     HeaderIterator hi;
636     rpmTag newMatch, oldMatch;
637     hdrObject * hdr;
638     rpm_count_t count = 0;
639     int rc = 1; /* assume failure */
640     rpmtd td = rpmtdNew();
641
642     Py_BEGIN_ALLOW_THREADS
643     h = headerRead(fd, HEADER_MAGIC_YES);
644     Py_END_ALLOW_THREADS
645
646     while (h) {
647         if (!headerGet(h, matchTag, td, HEADERGET_MINMEM)) {
648             PyErr_SetString(pyrpmError, "match tag missing in new header");
649             goto exit;
650         }
651         newMatch = rpmtdTag(td);
652         rpmtdFreeData(td);
653
654         hdr = (hdrObject *) PyList_GetItem(list, count++);
655         if (!hdr) goto exit;
656
657         if (!headerGet(hdr->h, matchTag, td, HEADERGET_MINMEM)) {
658             PyErr_SetString(pyrpmError, "match tag missing in new header");
659             goto exit;
660         }
661         oldMatch = rpmtdTag(td);
662         rpmtdFreeData(td);
663
664         if (newMatch != oldMatch) {
665             PyErr_SetString(pyrpmError, "match tag mismatch");
666             goto exit;
667         }
668
669         for (hi = headerInitIterator(h); headerNext(hi, td); rpmtdFreeData(td))
670         {
671             /* could be dupes */
672             headerDel(hdr->h, rpmtdTag(td));
673             headerPut(hdr->h, td, HEADERPUT_DEFAULT);
674         }
675
676         headerFreeIterator(hi);
677         h = headerFree(h);
678
679         Py_BEGIN_ALLOW_THREADS
680         h = headerRead(fd, HEADER_MAGIC_YES);
681         Py_END_ALLOW_THREADS
682     }
683     rc = 0;
684
685 exit:
686     rpmtdFree(td);
687     return rc;
688 }
689
690 PyObject *
691 rpmMergeHeadersFromFD(PyObject * self, PyObject * args, PyObject * kwds)
692 {
693     FD_t fd;
694     int fileno;
695     PyObject * list;
696     int rc;
697     int matchTag;
698     char * kwlist[] = {"list", "fd", "matchTag", NULL};
699
700     if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oii", kwlist, &list,
701             &fileno, &matchTag))
702         return NULL;
703
704     if (!PyList_Check(list)) {
705         PyErr_SetString(PyExc_TypeError, "first parameter must be a list");
706         return NULL;
707     }
708
709     fd = fdDup(fileno);
710
711     rc = rpmMergeHeaders (list, fd, matchTag);
712     Fclose(fd);
713
714     if (rc) {
715         return NULL;
716     }
717
718     Py_RETURN_NONE;
719 }
720
721 PyObject *
722 rpmSingleHeaderFromFD(PyObject * self, PyObject * args, PyObject * kwds)
723 {
724     FD_t fd;
725     int fileno;
726     off_t offset;
727     PyObject * tuple;
728     Header h;
729     char * kwlist[] = {"fd", NULL};
730
731     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &fileno))
732         return NULL;
733
734     offset = lseek(fileno, 0, SEEK_CUR);
735
736     fd = fdDup(fileno);
737
738     if (!fd) {
739         PyErr_SetFromErrno(pyrpmError);
740         return NULL;
741     }
742
743     Py_BEGIN_ALLOW_THREADS
744     h = headerRead(fd, HEADER_MAGIC_YES);
745     Py_END_ALLOW_THREADS
746
747     Fclose(fd);
748
749     tuple = PyTuple_New(2);
750
751     if (h && tuple) {
752         PyTuple_SET_ITEM(tuple, 0, hdr_Wrap(h));
753         PyTuple_SET_ITEM(tuple, 1, PyLong_FromLong(offset));
754         h = headerFree(h);
755     } else {
756         Py_INCREF(Py_None);
757         Py_INCREF(Py_None);
758         PyTuple_SET_ITEM(tuple, 0, Py_None);
759         PyTuple_SET_ITEM(tuple, 1, Py_None);
760     }
761
762     return tuple;
763 }
764
765 PyObject * versionCompare (PyObject * self, PyObject * args, PyObject * kwds)
766 {
767     hdrObject * h1, * h2;
768     char * kwlist[] = {"version0", "version1", NULL};
769
770     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", kwlist, &hdr_Type,
771             &h1, &hdr_Type, &h2))
772         return NULL;
773
774     return Py_BuildValue("i", hdr_compare(h1, h2));
775 }
776
777 static int compare_values(const char *str1, const char *str2)
778 {
779     if (!str1 && !str2)
780         return 0;
781     else if (str1 && !str2)
782         return 1;
783     else if (!str1 && str2)
784         return -1;
785     return rpmvercmp(str1, str2);
786 }
787
788 PyObject * labelCompare (PyObject * self, PyObject * args)
789 {
790     char *v1, *r1, *v2, *r2;
791     const char *e1, *e2;
792     int rc;
793
794     if (!PyArg_ParseTuple(args, "(zzz)(zzz)",
795                         &e1, &v1, &r1, &e2, &v2, &r2))
796         return NULL;
797
798     if (e1 == NULL)     e1 = "0";
799     if (e2 == NULL)     e2 = "0";
800
801     rc = compare_values(e1, e2);
802     if (!rc) {
803         rc = compare_values(v1, v2);
804         if (!rc)
805             rc = compare_values(r1, r2);
806     }
807     return Py_BuildValue("i", rc);
808 }
809