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