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