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