Use rpmTagType always now that it's available everywhere
[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(), providePackageNVR() */
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     rpm_tag_t tag;
147     rpmTagType type;
148
149     list = PyList_New(0);
150
151     hi = headerInitIterator(s->h);
152     while (headerNextIterator(hi, &tag, &type, NULL, NULL)) {
153         if (tag == HEADER_I18NTABLE) continue;
154
155         switch (type) {
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     }
172     headerFreeIterator(hi);
173
174     return list;
175 }
176
177 /** \ingroup py_c
178  */
179 static PyObject * hdrUnload(hdrObject * s, PyObject * args, PyObject *keywords)
180 {
181     char * buf;
182     PyObject * rc;
183     int len, legacy = 0;
184     Header h;
185     static char *kwlist[] = { "legacyHeader", NULL};
186
187     if (!PyArg_ParseTupleAndKeywords(args, keywords, "|i", kwlist, &legacy))
188         return NULL;
189
190     h = headerLink(s->h);
191     /* XXX this legacy switch is a hack, needs to be removed. */
192     if (legacy) {
193         h = headerCopy(s->h);   /* XXX strip region tags, etc */
194         headerFree(s->h);
195     }
196     len = headerSizeof(h, 0);
197     buf = headerUnload(h);
198     h = headerFree(h);
199
200     if (buf == NULL || len == 0) {
201         PyErr_SetString(pyrpmError, "can't unload bad header\n");
202         return NULL;
203     }
204
205     rc = PyString_FromStringAndSize(buf, len);
206     buf = _free(buf);
207
208     return rc;
209 }
210
211 /** \ingroup py_c
212  */
213 static PyObject * hdrExpandFilelist(hdrObject * s)
214 {
215     expandFilelist (s->h);
216
217     Py_INCREF(Py_None);
218     return Py_None;
219 }
220
221 /** \ingroup py_c
222  */
223 static PyObject * hdrCompressFilelist(hdrObject * s)
224 {
225     compressFilelist (s->h);
226
227     Py_INCREF(Py_None);
228     return Py_None;
229 }
230
231 /* make a header with _all_ the tags we need */
232 /** \ingroup py_c
233  */
234 static void mungeFilelist(Header h)
235 {
236     const char ** fileNames = NULL;
237     rpm_count_t count = 0;
238
239     if (!headerIsEntry (h, RPMTAG_BASENAMES)
240         || !headerIsEntry (h, RPMTAG_DIRNAMES)
241         || !headerIsEntry (h, RPMTAG_DIRINDEXES))
242         compressFilelist(h);
243
244     rpmfiBuildFNames(h, RPMTAG_BASENAMES, &fileNames, &count);
245
246     if (fileNames == NULL || count <= 0)
247         return;
248
249     /* XXX Legacy tag needs to go away. */
250     headerAddEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
251                         fileNames, count);
252
253     fileNames = _free(fileNames);
254 }
255
256 /** \ingroup py_c
257  */
258 static PyObject * hdrFullFilelist(hdrObject * s)
259 {
260     mungeFilelist (s->h);
261
262     Py_INCREF(Py_None);
263     return Py_None;
264 }
265
266 /** \ingroup py_c
267  */
268 static PyObject * hdrSprintf(hdrObject * s, PyObject * args, PyObject * kwds)
269 {
270     char * fmt;
271     char * r;
272     errmsg_t err;
273     PyObject * result;
274     char * kwlist[] = {"format", NULL};
275
276     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &fmt))
277         return NULL;
278
279     r = headerSprintf(s->h, fmt, rpmTagTable, rpmHeaderFormats, &err);
280     if (!r) {
281         PyErr_SetString(pyrpmError, err);
282         return NULL;
283     }
284
285     result = Py_BuildValue("s", r);
286     r = _free(r);
287
288     return result;
289 }
290
291 /**
292  */
293 static int hdr_compare(hdrObject * a, hdrObject * b)
294 {
295     return rpmVersionCompare(a->h, b->h);
296 }
297
298 static long hdr_hash(PyObject * h)
299 {
300     return (long) h;
301 }
302
303 /** \ingroup py_c
304  */
305 static struct PyMethodDef hdr_methods[] = {
306     {"keys",            (PyCFunction) hdrKeyList,       METH_NOARGS,
307         NULL },
308     {"unload",          (PyCFunction) hdrUnload,        METH_VARARGS|METH_KEYWORDS,
309         NULL },
310     {"expandFilelist",  (PyCFunction) hdrExpandFilelist,METH_NOARGS,
311         NULL },
312     {"compressFilelist",(PyCFunction) hdrCompressFilelist,METH_NOARGS,
313         NULL },
314     {"fullFilelist",    (PyCFunction) hdrFullFilelist,  METH_NOARGS,
315         NULL },
316     {"sprintf",         (PyCFunction) hdrSprintf,       METH_VARARGS|METH_KEYWORDS,
317         NULL },
318
319     {"dsOfHeader",      (PyCFunction)hdr_dsOfHeader,    METH_NOARGS,
320         NULL},
321     {"dsFromHeader",    (PyCFunction)hdr_dsFromHeader,  METH_VARARGS|METH_KEYWORDS,
322         NULL},
323     {"fiFromHeader",    (PyCFunction)hdr_fiFromHeader,  METH_VARARGS|METH_KEYWORDS,
324         NULL},
325
326     {NULL,              NULL}           /* sentinel */
327 };
328
329 /** \ingroup py_c
330  */
331 static void hdr_dealloc(hdrObject * s)
332 {
333     if (s->h) headerFree(s->h);
334     PyObject_Del(s);
335 }
336
337 /** \ingroup py_c
338  */
339 rpm_tag_t tagNumFromPyObject (PyObject *item)
340 {
341     char * str;
342
343     if (PyInt_Check(item)) {
344         return PyInt_AsLong(item);
345     } else if (PyString_Check(item)) {
346         str = PyString_AsString(item);
347         return rpmTagGetValue(str);
348     }
349     return RPMTAG_NOT_FOUND;
350 }
351
352 /** \ingroup py_c
353  * Retrieve tag info from header.
354  * This is a "dressed" entry to headerGetEntry to do:
355  *     1) DIRNAME/BASENAME/DIRINDICES -> FILENAMES tag conversions.
356  *     2) i18n lookaside (if enabled).
357  *
358  * @param h            header
359  * @param tag          tag
360  * @retval type                address of tag value data type
361  * @retval p           address of pointer to tag value(s)
362  * @retval c           address of number of values
363  * @return             0 on success, 1 on bad magic, 2 on error
364  */
365 static int dressedHeaderGetEntry(Header h, rpm_tag_t tag, rpmTagType *type,
366         void **p, rpm_count_t *c)
367 {
368     switch (tag) {
369     case RPMTAG_OLDFILENAMES:
370     {   const char ** fl = NULL;
371         rpm_count_t count;
372         rpmfiBuildFNames(h, RPMTAG_BASENAMES, &fl, &count);
373         if (count > 0) {
374             *p = fl;
375             if (c)      *c = count;
376             if (type)   *type = RPM_STRING_ARRAY_TYPE;
377             return 1;
378         }
379         if (c)  *c = 0;
380         return 0;
381     }   break;
382
383     case RPMTAG_GROUP:
384     case RPMTAG_DESCRIPTION:
385     case RPMTAG_SUMMARY:
386     {   char fmt[128];
387         const char * msgstr;
388         const char * errstr;
389
390         fmt[0] = '\0';
391         (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), rpmTagGetName(tag)), "}\n");
392
393         /* XXX FIXME: memory leak. */
394         msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
395         if (msgstr) {
396             *p = (void *) msgstr;
397             if (type)   *type = RPM_STRING_TYPE;
398             if (c)      *c = 1;
399             return 1;
400         } else {
401             if (c)      *c = 0;
402             return 0;
403         }
404     }   break;
405
406     default:
407         return headerGetEntry(h, tag, type, p, c);
408         break;
409     }
410 }
411
412 /** \ingroup py_c
413  */
414 static PyObject * hdr_subscript(hdrObject * s, PyObject * item)
415 {
416     rpmTagType tagtype, type;
417     rpm_tag_t tag = RPMTAG_NOT_FOUND;
418     rpm_count_t count, i;
419     rpm_data_t data;
420     PyObject * o, * metao;
421     char ** stringArray;
422     int forceArray = 0;
423     int freeData = 0;
424     char * str;
425     const struct headerSprintfExtension_s * ext = NULL;
426     const struct headerSprintfExtension_s * extensions = rpmHeaderFormats;
427
428     if (PyCObject_Check (item))
429         ext = PyCObject_AsVoidPtr(item);
430     else
431         tag = tagNumFromPyObject (item);
432     if (tag == RPMTAG_NOT_FOUND && PyString_Check(item)) {
433         /* if we still don't have the tag, go looking for the header
434            extensions */
435         str = PyString_AsString(item);
436         while (extensions->name) {
437             if (extensions->type == HEADER_EXT_TAG
438              && !xstrcasecmp(extensions->name + 7, str)) {
439                 ext = extensions;
440             }
441             extensions++;
442         }
443     }
444
445     /* Retrieve data from extension or header. */
446     if (ext) {
447         ext->u.tagFunction(s->h, &type, &data, &count, &freeData);
448     } else {
449         if (tag == RPMTAG_NOT_FOUND) {
450             PyErr_SetString(PyExc_KeyError, "unknown header tag");
451             return NULL;
452         }
453         
454         if (!dressedHeaderGetEntry(s->h, tag, &type, &data, &count)) {
455             switch (tag) {
456             case RPMTAG_EPOCH:
457             case RPMTAG_NAME:
458             case RPMTAG_VERSION:
459             case RPMTAG_RELEASE:
460             case RPMTAG_ARCH:
461             case RPMTAG_OS:
462                 Py_INCREF(Py_None);
463                 return Py_None;
464                 break;
465             default:
466                 return PyList_New(0);
467                 break;
468             }
469         }
470     }
471
472     tagtype = rpmTagGetType(tag); 
473 #if NOTYET
474     /* this blows up with header extension types */
475     type = tagtype & RPM_MASK_TYPE;
476 #endif
477     forceArray = (tagtype & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE;
478     freeData = (tagtype & RPM_MASK_TYPE) == RPM_I18NSTRING_TYPE;
479
480     switch (type) {
481     case RPM_BIN_TYPE:
482         o = PyString_FromStringAndSize(data, count);
483         break;
484
485     case RPM_INT32_TYPE:
486         if (count != 1 || forceArray) {
487             metao = PyList_New(0);
488             for (i = 0; i < count; i++) {
489                 o = PyInt_FromLong(((int *) data)[i]);
490                 PyList_Append(metao, o);
491                 Py_DECREF(o);
492             }
493             o = metao;
494         } else {
495             o = PyInt_FromLong(*((int *) data));
496         }
497         break;
498
499     case RPM_CHAR_TYPE:
500     case RPM_INT8_TYPE:
501         if (count != 1 || forceArray) {
502             metao = PyList_New(0);
503             for (i = 0; i < count; i++) {
504                 o = PyInt_FromLong(((char *) data)[i]);
505                 PyList_Append(metao, o);
506                 Py_DECREF(o);
507             }
508             o = metao;
509         } else {
510             o = PyInt_FromLong(*((char *) data));
511         }
512         break;
513
514     case RPM_INT16_TYPE:
515         if (count != 1 || forceArray) {
516             metao = PyList_New(0);
517             for (i = 0; i < count; i++) {
518                 o = PyInt_FromLong(((short *) data)[i]);
519                 PyList_Append(metao, o);
520                 Py_DECREF(o);
521             }
522             o = metao;
523         } else {
524             o = PyInt_FromLong(*((short *) data));
525         }
526         break;
527
528     case RPM_STRING_ARRAY_TYPE:
529         stringArray = data;
530
531         metao = PyList_New(0);
532         for (i = 0; i < count; i++) {
533             o = PyString_FromString(stringArray[i]);
534             PyList_Append(metao, o);
535             Py_DECREF(o);
536         }
537         free (stringArray);
538         o = metao;
539         break;
540
541     case RPM_STRING_TYPE:
542     case RPM_I18NSTRING_TYPE:
543         if (count != 1 || forceArray) {
544             stringArray = data;
545
546             metao = PyList_New(0);
547             for (i=0; i < count; i++) {
548                 o = PyString_FromString(stringArray[i]);
549                 PyList_Append(metao, o);
550                 Py_DECREF(o);
551             }
552             o = metao;
553         } else {
554             o = PyString_FromString(data);
555             if (freeData)
556                 free (data);
557         }
558         break;
559
560     default:
561         PyErr_SetString(PyExc_TypeError, "unsupported type in header");
562         return NULL;
563     }
564
565     return o;
566 }
567
568 static PyObject * hdr_getattro(PyObject * o, PyObject * n)
569 {
570     PyObject * res;
571     res = PyObject_GenericGetAttr(o, n);
572     if (res == NULL)
573         res = hdr_subscript(o, n);
574     return res;
575 }
576
577 static int hdr_setattro(PyObject * o, PyObject * n, PyObject * v)
578 {
579     return PyObject_GenericSetAttr(o, n, v);
580 }
581
582 /** \ingroup py_c
583  */
584 static PyMappingMethods hdr_as_mapping = {
585         (lenfunc) 0,                    /* mp_length */
586         (binaryfunc) hdr_subscript,     /* mp_subscript */
587         (objobjargproc)0,               /* mp_ass_subscript */
588 };
589
590 /**
591  */
592 static char hdr_doc[] =
593 "";
594
595 /** \ingroup py_c
596  */
597 PyTypeObject hdr_Type = {
598         PyObject_HEAD_INIT(&PyType_Type)
599         0,                              /* ob_size */
600         "rpm.hdr",                      /* tp_name */
601         sizeof(hdrObject),              /* tp_size */
602         0,                              /* tp_itemsize */
603         (destructor) hdr_dealloc,       /* tp_dealloc */
604         0,                              /* tp_print */
605         (getattrfunc) 0,                /* tp_getattr */
606         0,                              /* tp_setattr */
607         (cmpfunc) hdr_compare,          /* tp_compare */
608         0,                              /* tp_repr */
609         0,                              /* tp_as_number */
610         0,                              /* tp_as_sequence */
611         &hdr_as_mapping,                /* tp_as_mapping */
612         hdr_hash,                       /* tp_hash */
613         0,                              /* tp_call */
614         0,                              /* tp_str */
615         (getattrofunc) hdr_getattro,    /* tp_getattro */
616         (setattrofunc) hdr_setattro,    /* tp_setattro */
617         0,                              /* tp_as_buffer */
618         Py_TPFLAGS_DEFAULT,             /* tp_flags */
619         hdr_doc,                        /* tp_doc */
620 #if Py_TPFLAGS_HAVE_ITER
621         0,                              /* tp_traverse */
622         0,                              /* tp_clear */
623         0,                              /* tp_richcompare */
624         0,                              /* tp_weaklistoffset */
625         0,                              /* tp_iter */
626         0,                              /* tp_iternext */
627         hdr_methods,                    /* tp_methods */
628         0,                              /* tp_members */
629         0,                              /* tp_getset */
630         0,                              /* tp_base */
631         0,                              /* tp_dict */
632         0,                              /* tp_descr_get */
633         0,                              /* tp_descr_set */
634         0,                              /* tp_dictoffset */
635         0,                              /* tp_init */
636         0,                              /* tp_alloc */
637         0,                              /* tp_new */
638         0,                              /* tp_free */
639         0,                              /* tp_is_gc */
640 #endif
641 };
642
643 hdrObject * hdr_Wrap(Header h)
644 {
645     hdrObject * hdr = PyObject_New(hdrObject, &hdr_Type);
646     hdr->h = headerLink(h);
647     return hdr;
648 }
649
650 Header hdrGetHeader(hdrObject * s)
651 {
652     return s->h;
653 }
654
655 /**
656  */
657 PyObject * hdrLoad(PyObject * self, PyObject * args, PyObject * kwds)
658 {
659     hdrObject * hdr;
660     char * obj;
661     Header h;
662     int len;
663     char * kwlist[] = {"headers", NULL};
664
665     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#", kwlist, &obj, &len))
666         return NULL;
667
668     /* copy is needed to avoid surprises from data swab in headerLoad(). */
669     h = headerCopyLoad(obj);
670     if (!h) {
671         if (errno == ENOMEM) {
672             PyErr_SetString(pyrpmError, "out of memory");
673         } else {
674             PyErr_SetString(pyrpmError, "bad header");
675         }
676         return NULL;
677     }
678     compressFilelist (h);
679     providePackageNVR (h);
680
681     hdr = hdr_Wrap(h);
682     h = headerFree(h);  /* XXX ref held by hdr */
683
684     return (PyObject *) hdr;
685 }
686
687 /**
688  */
689 PyObject * rpmReadHeaders (FD_t fd)
690 {
691     PyObject * list;
692     Header h;
693     hdrObject * hdr;
694
695     if (!fd) {
696         PyErr_SetFromErrno(pyrpmError);
697         return NULL;
698     }
699
700     list = PyList_New(0);
701     Py_BEGIN_ALLOW_THREADS
702     h = headerRead(fd, HEADER_MAGIC_YES);
703     Py_END_ALLOW_THREADS
704
705     while (h) {
706         compressFilelist(h);
707         providePackageNVR(h);
708         hdr = hdr_Wrap(h);
709         if (PyList_Append(list, (PyObject *) hdr)) {
710             Py_DECREF(list);
711             Py_DECREF(hdr);
712             return NULL;
713         }
714         Py_DECREF(hdr);
715
716         h = headerFree(h);      /* XXX ref held by hdr */
717
718         Py_BEGIN_ALLOW_THREADS
719         h = headerRead(fd, HEADER_MAGIC_YES);
720         Py_END_ALLOW_THREADS
721     }
722
723     return list;
724 }
725
726 /**
727  */
728 PyObject * rpmHeaderFromFD(PyObject * self, PyObject * args, PyObject * kwds)
729 {
730     FD_t fd;
731     int fileno;
732     PyObject * list;
733     char * kwlist[] = {"fd", NULL};
734
735     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &fileno))
736         return NULL;
737
738     fd = fdDup(fileno);
739
740     list = rpmReadHeaders (fd);
741     Fclose(fd);
742
743     return list;
744 }
745
746 /**
747  */
748 PyObject * rpmHeaderFromFile(PyObject * self, PyObject * args, PyObject *kwds)
749 {
750     char * filespec;
751     FD_t fd;
752     PyObject * list;
753     char * kwlist[] = {"file", NULL};
754
755     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &filespec))
756         return NULL;
757
758     fd = Fopen(filespec, "r.fdio");
759
760     if (!fd) {
761         PyErr_SetFromErrno(pyrpmError);
762         return NULL;
763     }
764
765     list = rpmReadHeaders (fd);
766     Fclose(fd);
767
768     return list;
769 }
770
771 /**
772  * This assumes the order of list matches the order of the new headers, and
773  * throws an exception if that isn't true.
774  */
775 int rpmMergeHeaders(PyObject * list, FD_t fd, int matchTag)
776 {
777     Header h;
778     HeaderIterator hi;
779     int32_t * newMatch;
780     int32_t * oldMatch;
781     hdrObject * hdr;
782     rpm_count_t c, count = 0;
783     rpm_tag_t tag;
784     rpmTagType type;
785     void * p;
786
787     Py_BEGIN_ALLOW_THREADS
788     h = headerRead(fd, HEADER_MAGIC_YES);
789     Py_END_ALLOW_THREADS
790
791     while (h) {
792         if (!headerGetEntry(h, matchTag, NULL, (void **) &newMatch, NULL)) {
793             PyErr_SetString(pyrpmError, "match tag missing in new header");
794             return 1;
795         }
796
797         hdr = (hdrObject *) PyList_GetItem(list, count++);
798         if (!hdr) return 1;
799
800         if (!headerGetEntry(hdr->h, matchTag, NULL, (void **) &oldMatch, NULL)) {
801             PyErr_SetString(pyrpmError, "match tag missing in new header");
802             return 1;
803         }
804
805         if (*newMatch != *oldMatch) {
806             PyErr_SetString(pyrpmError, "match tag mismatch");
807             return 1;
808         }
809
810         for (hi = headerInitIterator(h);
811             headerNextIterator(hi, &tag, &type, (void *) &p, &c);
812             p = headerFreeData(p, type))
813         {
814             /* could be dupes */
815             headerRemoveEntry(hdr->h, tag);
816             headerAddEntry(hdr->h, tag, type, p, c);
817         }
818
819         headerFreeIterator(hi);
820         h = headerFree(h);
821
822         Py_BEGIN_ALLOW_THREADS
823         h = headerRead(fd, HEADER_MAGIC_YES);
824         Py_END_ALLOW_THREADS
825     }
826
827     return 0;
828 }
829
830 PyObject *
831 rpmMergeHeadersFromFD(PyObject * self, PyObject * args, PyObject * kwds)
832 {
833     FD_t fd;
834     int fileno;
835     PyObject * list;
836     int rc;
837     int matchTag;
838     char * kwlist[] = {"list", "fd", "matchTag", NULL};
839
840     if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oii", kwlist, &list,
841             &fileno, &matchTag))
842         return NULL;
843
844     if (!PyList_Check(list)) {
845         PyErr_SetString(PyExc_TypeError, "first parameter must be a list");
846         return NULL;
847     }
848
849     fd = fdDup(fileno);
850
851     rc = rpmMergeHeaders (list, fd, matchTag);
852     Fclose(fd);
853
854     if (rc) {
855         return NULL;
856     }
857
858     Py_INCREF(Py_None);
859     return Py_None;
860 }
861
862 /**
863  */
864 PyObject *
865 rpmSingleHeaderFromFD(PyObject * self, PyObject * args, PyObject * kwds)
866 {
867     FD_t fd;
868     int fileno;
869     off_t offset;
870     PyObject * tuple;
871     Header h;
872     char * kwlist[] = {"fd", NULL};
873
874     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &fileno))
875         return NULL;
876
877     offset = lseek(fileno, 0, SEEK_CUR);
878
879     fd = fdDup(fileno);
880
881     if (!fd) {
882         PyErr_SetFromErrno(pyrpmError);
883         return NULL;
884     }
885
886     Py_BEGIN_ALLOW_THREADS
887     h = headerRead(fd, HEADER_MAGIC_YES);
888     Py_END_ALLOW_THREADS
889
890     Fclose(fd);
891
892     tuple = PyTuple_New(2);
893
894     if (h && tuple) {
895         PyTuple_SET_ITEM(tuple, 0, (PyObject *) hdr_Wrap(h));
896         PyTuple_SET_ITEM(tuple, 1, PyLong_FromLong(offset));
897         h = headerFree(h);
898     } else {
899         Py_INCREF(Py_None);
900         Py_INCREF(Py_None);
901         PyTuple_SET_ITEM(tuple, 0, Py_None);
902         PyTuple_SET_ITEM(tuple, 1, Py_None);
903     }
904
905     return tuple;
906 }
907
908 /**
909  */
910 PyObject * versionCompare (PyObject * self, PyObject * args, PyObject * kwds)
911 {
912     hdrObject * h1, * h2;
913     char * kwlist[] = {"version0", "version1", NULL};
914
915     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", kwlist, &hdr_Type,
916             &h1, &hdr_Type, &h2))
917         return NULL;
918
919     return Py_BuildValue("i", hdr_compare(h1, h2));
920 }
921
922 /**
923  */
924 static int compare_values(const char *str1, const char *str2)
925 {
926     if (!str1 && !str2)
927         return 0;
928     else if (str1 && !str2)
929         return 1;
930     else if (!str1 && str2)
931         return -1;
932     return rpmvercmp(str1, str2);
933 }
934
935 PyObject * labelCompare (PyObject * self, PyObject * args)
936 {
937     char *v1, *r1, *v2, *r2;
938     const char *e1, *e2;
939     int rc;
940
941     if (!PyArg_ParseTuple(args, "(zzz)(zzz)",
942                         &e1, &v1, &r1, &e2, &v2, &r2))
943         return NULL;
944
945     if (e1 == NULL)     e1 = "0";
946     if (e2 == NULL)     e2 = "0";
947
948     rc = compare_values(e1, e2);
949     if (!rc) {
950         rc = compare_values(v1, v2);
951         if (!rc)
952             rc = compare_values(r1, r2);
953     }
954     return Py_BuildValue("i", rc);
955 }
956