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