- return multiple suggested packages (Pawel Kolodziej <pawelk@pld.org.pl>).
[platform/upstream/rpm.git] / python / rpmmodule.c
1 /** \ingroup python
2  * \file python/rpmmodule.c
3  */
4
5 #include <alloca.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <time.h>
9 #include <sys/stat.h>
10 #include <sys/time.h>
11 #include <unistd.h>
12 #include <glob.h>       /* XXX rpmio.h */
13 #include <dirent.h>     /* XXX rpmio.h */
14 #include <locale.h>
15
16 #include "Python.h"
17 #include "rpmlib.h"
18 #include "misc.h"
19 #include "rpmmacro.h"
20 #include "rpmio_internal.h"
21 #include "upgrade.h"
22
23 extern int _rpmio_debug;
24
25 #ifdef __LCLINT__
26 #undef  PyObject_HEAD
27 #defin  PyObject_HEAD   int _PyObjectHead
28 #endif
29
30 extern int mdfile(const char *fn, unsigned char *digest);
31
32 void initrpm(void);
33
34 /* from lib/misc.c */
35 int rpmvercmp(const char * one, const char * two);
36
37
38 /** \ingroup python
39  */
40 typedef struct rpmdbObject_s rpmdbObject;
41
42 /** \ingroup python
43  */
44 typedef struct rpmdbMIObject_s rpmdbMIObject;
45
46 /** \ingroup python
47  */
48 typedef struct rpmtransObject_s rpmtransObject;
49
50 /** \ingroup python
51  */
52 typedef struct hdrObject_s hdrObject;
53
54 /** \ingroup python
55  */
56 static PyObject * pyrpmError;
57
58 /** \ingroup python
59  * \class header
60  * \brief A python header object represents an RPM package header.
61  * 
62  * All RPM packages have headers that provide metadata for the package.
63  * Header objects can be returned by database queries or loaded from a
64  * binary package on disk.
65  * 
66  * The headerFromPackage function loads the package header from a
67  * package on disk.  It returns a tuple of a "isSource" flag and the
68  * header object.  The "isSource" flag is set to 1 if the package
69  * header was read from a source rpm or to 0 if the package header was
70  * read from a binary rpm.
71  * 
72  * For example:
73  * \code
74  *      import os, rpm
75  *  
76  *      fd = os.open("/tmp/foo-1.0-1.i386.rpm", os.O_RDONLY)
77  *      (header, isSource) = rpm.headerFromPackage(fd)
78  *      fd.close()
79  * \endcode
80  * The Python interface to the header data is quite elegant.  It
81  * presents the data in a dictionary form.  We'll take the header we
82  * just loaded and access the data within it:
83  * \code
84  *      print header[rpm.RPMTAG_NAME]
85  *      print header[rpm.RPMTAG_VERSION]
86  *      print header[rpm.RPMTAG_RELEASE]
87  * \endcode
88  * in the case of our "foor-1.0-1.i386.rpm" package, this code would
89  * output:
90 \verbatim
91         foo
92         1.0
93         1
94 \endverbatim
95  * You make also access the header data by string name:
96  * \code
97  *      print header['name']
98  * \endcode
99  * This method of access is a bit slower because the name must be
100  * translated into the tag number dynamically.
101  */
102
103 /** \ingroup python
104  * \name Class: header
105  */
106 /*@{*/
107
108 /** \ingroup python
109  */
110 struct hdrObject_s {
111     PyObject_HEAD;
112     Header h;
113     Header sigs;
114     char ** md5list;
115     char ** fileList;
116     char ** linkList;
117     int_32 * fileSizes;
118     int_32 * mtimes;
119     int_32 * uids, * gids;
120     unsigned short * rdevs;
121     unsigned short * modes;
122 } ;
123
124 /** \ingroup python
125  */
126 static PyObject * hdrKeyList(hdrObject * s, PyObject * args) {
127     PyObject * list, *o;
128     HeaderIterator iter;
129     int tag, type;
130
131     if (!PyArg_ParseTuple(args, "")) return NULL;
132
133     list = PyList_New(0);
134
135     iter = headerInitIterator(s->h);
136     while (headerNextIterator(iter, &tag, &type, NULL, NULL)) {
137         if (tag == HEADER_I18NTABLE) continue;
138
139         switch (type) {
140           case RPM_BIN_TYPE:
141           case RPM_INT32_TYPE:
142           case RPM_CHAR_TYPE:
143           case RPM_INT8_TYPE:
144           case RPM_INT16_TYPE:
145           case RPM_STRING_ARRAY_TYPE:
146           case RPM_STRING_TYPE:
147             PyList_Append(list, o=PyInt_FromLong(tag));
148             Py_DECREF(o);
149         }
150     }
151
152     headerFreeIterator(iter);
153
154     return list;
155 }
156
157 /** \ingroup python
158  */
159 static PyObject * hdrUnload(hdrObject * s, PyObject * args, PyObject *keywords) {
160     char * buf;
161     PyObject * rc;
162     int len, legacy = 0;
163     static char *kwlist[] = { "legacyHeader", NULL};
164
165     if (!PyArg_ParseTupleAndKeywords(args, keywords, "|i", kwlist, &legacy))
166         return NULL; 
167
168     if (legacy) {
169         Header h;
170
171         h = headerCopy(s->h);
172         len = headerSizeof(h, 0);
173         buf = headerUnload(h);
174     } else {
175         len = headerSizeof(s->h, 0);
176         buf = headerUnload(s->h);
177     }
178
179     rc = PyString_FromStringAndSize(buf, len);
180     free(buf);
181
182     return rc;
183 }
184
185 /* Returns a list of these tuple for each part which failed:
186
187         (attr_name, correctValue, currentValue)
188
189         It should be passwd the file number to verify.
190 */
191 /** \ingroup python
192  */
193 static PyObject * hdrVerifyFile(hdrObject * s, PyObject * args) {
194     int fileNumber;
195     rpmVerifyAttrs verifyResult = 0;
196     PyObject * list, * tuple, * attrName;
197     int type, count;
198     struct stat sb;
199     char buf[2048];
200     int i;
201     time_t timeInt;
202     struct tm * timeStruct;
203
204     if (!PyInt_Check(args)) {
205         PyErr_SetString(PyExc_TypeError, "integer expected");
206         return NULL;
207     }
208
209     fileNumber = (int) PyInt_AsLong(args);
210
211     if (rpmVerifyFile("", s->h, fileNumber, &verifyResult, RPMVERIFY_NONE)) {
212         Py_INCREF(Py_None);
213         return Py_None;
214     }
215
216     list = PyList_New(0);
217
218     if (!verifyResult) return list;
219
220     if (!s->fileList) {
221         headerGetEntry(s->h, RPMTAG_OLDFILENAMES, &type, (void **) &s->fileList,
222                  &count);
223     }
224
225     lstat(s->fileList[fileNumber], &sb);
226
227     if (verifyResult & RPMVERIFY_MD5) {
228         if (!s->md5list) {
229             headerGetEntry(s->h, RPMTAG_FILEMD5S, &type, (void **) &s->md5list,
230                      &count);
231         }
232
233         if (mdfile(s->fileList[fileNumber], buf)) {
234             strcpy(buf, "(unknown)");
235         }
236
237         tuple = PyTuple_New(3);
238         attrName = PyString_FromString("checksum");
239         PyTuple_SetItem(tuple, 0, attrName);
240         PyTuple_SetItem(tuple, 1, PyString_FromString(s->md5list[fileNumber]));
241         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
242         PyList_Append(list, tuple);
243         Py_DECREF(tuple);
244     }
245
246     if (verifyResult & RPMVERIFY_FILESIZE) {
247         if (!s->fileSizes) {
248             headerGetEntry(s->h, RPMTAG_FILESIZES, &type, (void **) &s->fileSizes,
249                      &count);
250
251         }
252
253         tuple = PyTuple_New(3);
254         attrName = PyString_FromString("size");
255         PyTuple_SetItem(tuple, 0, attrName);
256
257         sprintf(buf, "%d", 100);
258         PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
259         sprintf(buf, "%ld", sb.st_size);
260         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
261         PyList_Append(list, tuple);
262         Py_DECREF(tuple);
263     }
264
265     if (verifyResult & RPMVERIFY_LINKTO) {
266         if (!s->linkList) {
267             headerGetEntry(s->h, RPMTAG_FILELINKTOS, &type, (void **) &s->linkList,
268                      &count);
269         }
270
271         i = readlink(s->fileList[fileNumber], buf, sizeof(buf));
272         if (i <= 0)
273             strcpy(buf, "(unknown)");
274         else
275             buf[i] = '\0';
276
277         tuple = PyTuple_New(3);
278         attrName = PyString_FromString("link");
279         PyTuple_SetItem(tuple, 0, attrName);
280         PyTuple_SetItem(tuple, 1, PyString_FromString(s->linkList[fileNumber]));
281         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
282         PyList_Append(list, tuple);
283         Py_DECREF(tuple);
284     }
285
286     if (verifyResult & RPMVERIFY_MTIME) {
287         if (!s->mtimes) {
288             headerGetEntry(s->h, RPMTAG_FILEMTIMES, &type, (void **) &s->mtimes,
289                      &count);
290         }
291
292         tuple = PyTuple_New(3);
293         attrName = PyString_FromString("time");
294         PyTuple_SetItem(tuple, 0, attrName);
295
296         timeInt = sb.st_mtime;
297         timeStruct = localtime(&timeInt);
298         strftime(buf, sizeof(buf) - 1, "%c", timeStruct);
299         PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
300
301         timeInt = s->mtimes[fileNumber];
302         timeStruct = localtime(&timeInt);
303         strftime(buf, sizeof(buf) - 1, "%c", timeStruct);
304
305         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
306
307         PyList_Append(list, tuple);
308         Py_DECREF(tuple);
309     }
310
311     if (verifyResult & RPMVERIFY_RDEV) {
312         if (!s->rdevs) {
313             headerGetEntry(s->h, RPMTAG_FILERDEVS, &type, (void **) &s->rdevs,
314                      &count);
315         }
316
317         tuple = PyTuple_New(3);
318         attrName = PyString_FromString("device");
319
320         PyTuple_SetItem(tuple, 0, attrName);
321         sprintf(buf, "0x%-4x", s->rdevs[fileNumber]);
322         PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
323         sprintf(buf, "0x%-4x", (unsigned int) sb.st_rdev);
324         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
325         PyList_Append(list, tuple);
326         Py_DECREF(tuple);
327     }
328
329     /* RPMVERIFY_USER and RPM_VERIFY_GROUP are handled wrong here, but rpmlib.a
330        doesn't do these correctly either. At least this is consistent. */
331     if (verifyResult & RPMVERIFY_USER) {
332         if (!s->uids) {
333             headerGetEntry(s->h, RPMTAG_FILEUIDS, &type, (void **) &s->uids,
334                      &count);
335         }
336
337         tuple = PyTuple_New(3);
338         attrName = PyString_FromString("uid");
339         PyTuple_SetItem(tuple, 0, attrName);
340         sprintf(buf, "%d", s->uids[fileNumber]);
341         PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
342         sprintf(buf, "%d", sb.st_uid);
343         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
344         PyList_Append(list, tuple);
345         Py_DECREF(tuple);
346     }
347
348     if (verifyResult & RPMVERIFY_GROUP) {
349         if (!s->gids) {
350             headerGetEntry(s->h, RPMTAG_FILEGIDS, &type, (void **) &s->gids,
351                      &count);
352         }
353
354         tuple = PyTuple_New(3);
355         attrName = PyString_FromString("gid");
356         PyTuple_SetItem(tuple, 0, attrName);
357         sprintf(buf, "%d", s->gids[fileNumber]);
358         PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
359         sprintf(buf, "%d", sb.st_gid);
360         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
361         PyList_Append(list, tuple);
362         Py_DECREF(tuple);
363     }
364
365     if (verifyResult & RPMVERIFY_MODE) {
366         if (!s->modes) {
367             headerGetEntry(s->h, RPMTAG_FILEMODES, &type, (void **) &s->modes,
368                      &count);
369         }
370
371         tuple = PyTuple_New(3);
372         attrName = PyString_FromString("permissions");
373         PyTuple_SetItem(tuple, 0, attrName);
374         sprintf(buf, "0%-4o", s->modes[fileNumber]);
375         PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
376         sprintf(buf, "0%-4o", sb.st_mode);
377         PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
378         PyList_Append(list, tuple);
379         Py_DECREF(tuple);
380     }
381
382     return list;
383 }
384
385 /** \ingroup python
386  */
387 static PyObject * hdrExpandFilelist(hdrObject * s, PyObject * args) {
388     expandFilelist (s->h);
389
390     Py_INCREF(Py_None);
391     return Py_None;
392 }
393
394 /** \ingroup python
395  */
396 static PyObject * hdrCompressFilelist(hdrObject * s, PyObject * args) {
397     compressFilelist (s->h);
398
399     Py_INCREF(Py_None);
400     return Py_None;
401 }
402
403 /* make a header with _all_ the tags we need */
404 /** \ingroup python
405  */
406 static void mungeFilelist(Header h)
407 {
408     const char ** fileNames = NULL;
409     int count = 0;
410
411     if (!headerIsEntry (h, RPMTAG_BASENAMES)
412         || !headerIsEntry (h, RPMTAG_DIRNAMES)
413         || !headerIsEntry (h, RPMTAG_DIRINDEXES))
414         compressFilelist(h);
415     
416     rpmBuildFileList(h, &fileNames, &count);
417
418     if (fileNames == NULL || count <= 0)
419         return;
420
421     headerAddEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
422                         fileNames, count);
423
424     free((void *)fileNames);
425 }
426
427 /** \ingroup python
428  */
429 static PyObject * hdrFullFilelist(hdrObject * s, PyObject * args) {
430     mungeFilelist (s->h);
431
432     Py_INCREF(Py_None);
433     return Py_None;
434 }
435
436 /** \ingroup python
437  */
438 static struct PyMethodDef hdrMethods[] = {
439         {"keys",        (PyCFunction) hdrKeyList,       1 },
440         {"unload",      (PyCFunction) hdrUnload,        METH_VARARGS|METH_KEYWORDS },
441         {"verifyFile",  (PyCFunction) hdrVerifyFile,    1 },
442         {"expandFilelist",      (PyCFunction) hdrExpandFilelist,        1 },
443         {"compressFilelist",    (PyCFunction) hdrCompressFilelist,      1 },
444         {"fullFilelist",        (PyCFunction) hdrFullFilelist,  1 },
445         {NULL,          NULL}           /* sentinel */
446 };
447
448 /** \ingroup python
449  */
450 static PyObject * hdrGetAttr(hdrObject * s, char * name) {
451     return Py_FindMethod(hdrMethods, (PyObject * ) s, name);
452 }
453
454 /** \ingroup python
455  */
456 static void hdrDealloc(hdrObject * s) {
457     if (s->h) headerFree(s->h);
458     if (s->sigs) headerFree(s->sigs);
459     if (s->md5list) free(s->md5list);
460     if (s->fileList) free(s->fileList);
461     if (s->linkList) free(s->linkList);
462     PyMem_DEL(s);
463 }
464
465 /** \ingroup python
466  */
467 static long tagNumFromPyObject (PyObject *item)
468 {
469     char * str;
470     int i;
471
472     if (PyInt_Check(item)) {
473         return PyInt_AsLong(item);
474     } else if (PyString_Check(item)) {
475         str = PyString_AsString(item);
476         for (i = 0; i < rpmTagTableSize; i++)
477             if (!xstrcasecmp(rpmTagTable[i].name + 7, str)) break;
478         if (i < rpmTagTableSize) return rpmTagTable[i].val;
479     }
480     return -1;
481 }
482
483 /** \ingroup python
484  */
485 static PyObject * hdrSubscript(hdrObject * s, PyObject * item) {
486     int type, count, i, tag = -1;
487     void * data;
488     PyObject * o, * metao;
489     char ** stringArray;
490     int forceArray = 0;
491     int freeData = 0;
492     char * str;
493     struct headerSprintfExtension * ext = NULL;
494     const struct headerSprintfExtension * extensions = rpmHeaderFormats;
495
496     if (PyCObject_Check (item))
497         ext = PyCObject_AsVoidPtr(item);
498     else
499         tag = tagNumFromPyObject (item);
500     if (tag == -1 && PyString_Check(item)) {
501         /* if we still don't have the tag, go looking for the header
502            extensions */
503         str = PyString_AsString(item);
504         while (extensions->name) {
505             if (extensions->type == HEADER_EXT_TAG
506                 && !xstrcasecmp(extensions->name + 7, str)) {
507                 (const struct headerSprintfExtension *) ext = extensions;
508             }
509             extensions++;
510         }
511     }
512
513     if (ext) {
514         ext->u.tagFunction(s->h, &type, (const void **) &data, &count, &freeData);
515     } else {
516         if (tag == -1) {
517             PyErr_SetString(PyExc_KeyError, "unknown header tag");
518             return NULL;
519         }
520         
521         if (!rpmPackageGetEntry(NULL, s->sigs, s->h, tag, &type, &data, &count))
522         {
523             Py_INCREF(Py_None);
524             return Py_None;
525         }
526     }
527
528     switch (tag) {
529       case RPMTAG_OLDFILENAMES:
530       case RPMTAG_FILESIZES:
531       case RPMTAG_FILESTATES:
532       case RPMTAG_FILEMODES:
533       case RPMTAG_FILEUIDS:
534       case RPMTAG_FILEGIDS:
535       case RPMTAG_FILERDEVS:
536       case RPMTAG_FILEMTIMES:
537       case RPMTAG_FILEMD5S:
538       case RPMTAG_FILELINKTOS:
539       case RPMTAG_FILEFLAGS:
540       case RPMTAG_ROOT:
541       case RPMTAG_FILEUSERNAME:
542       case RPMTAG_FILEGROUPNAME:
543         forceArray = 1;
544         break;
545       case RPMTAG_SUMMARY:
546       case RPMTAG_GROUP:
547       case RPMTAG_DESCRIPTION:
548         freeData = 1;
549         break;
550       default:
551         break;
552     }
553
554     switch (type) {
555       case RPM_BIN_TYPE:
556         o = PyString_FromStringAndSize(data, count);
557         break;
558
559       case RPM_INT32_TYPE:
560         if (count != 1 || forceArray) {
561             metao = PyList_New(0);
562             for (i = 0; i < count; i++) {
563                 o = PyInt_FromLong(((int *) data)[i]);
564                 PyList_Append(metao, o);
565                 Py_DECREF(o);
566             }
567             o = metao;
568         } else {
569             o = PyInt_FromLong(*((int *) data));
570         }
571         break;
572
573       case RPM_CHAR_TYPE:
574       case RPM_INT8_TYPE:
575         if (count != 1 || forceArray) {
576             metao = PyList_New(0);
577             for (i = 0; i < count; i++) {
578                 o = PyInt_FromLong(((char *) data)[i]);
579                 PyList_Append(metao, o);
580                 Py_DECREF(o);
581             }
582             o = metao;
583         } else {
584             o = PyInt_FromLong(*((char *) data));
585         }
586         break;
587
588       case RPM_INT16_TYPE:
589         if (count != 1 || forceArray) {
590             metao = PyList_New(0);
591             for (i = 0; i < count; i++) {
592                 o = PyInt_FromLong(((short *) data)[i]);
593                 PyList_Append(metao, o);
594                 Py_DECREF(o);
595             }
596             o = metao;
597         } else {
598             o = PyInt_FromLong(*((short *) data));
599         }
600         break;
601
602       case RPM_STRING_ARRAY_TYPE:
603         stringArray = data;
604
605         metao = PyList_New(0);
606         for (i = 0; i < count; i++) {
607             o = PyString_FromString(stringArray[i]);
608             PyList_Append(metao, o);
609             Py_DECREF(o);
610         }
611         free (stringArray);
612         o = metao;
613         break;
614
615       case RPM_STRING_TYPE:
616         if (count != 1 || forceArray) {
617             stringArray = data;
618
619             metao = PyList_New(0);
620             for (i=0; i < count; i++) {
621                 o = PyString_FromString(stringArray[i]);
622                 PyList_Append(metao, o);
623                 Py_DECREF(o);
624             }
625             o = metao;
626         } else {
627             o = PyString_FromString(data);
628             if (freeData)
629                 free (data);
630         }
631         break;
632
633       default:
634         PyErr_SetString(PyExc_TypeError, "unsupported type in header");
635         return NULL;
636     }
637
638     return o;
639 }
640
641 /** \ingroup python
642  */
643 static PyMappingMethods hdrAsMapping = {
644         (inquiry) 0,                    /* mp_length */
645         (binaryfunc) hdrSubscript,      /* mp_subscript */
646         (objobjargproc)0,               /* mp_ass_subscript */
647 };
648
649 /** \ingroup python
650  */
651 static PyTypeObject hdrType = {
652         PyObject_HEAD_INIT(&PyType_Type)
653         0,                              /* ob_size */
654         "header",                       /* tp_name */
655         sizeof(hdrObject),              /* tp_size */
656         0,                              /* tp_itemsize */
657         (destructor) hdrDealloc,        /* tp_dealloc */
658         0,                              /* tp_print */
659         (getattrfunc) hdrGetAttr,       /* tp_getattr */
660         0,                              /* tp_setattr */
661         0,                              /* tp_compare */
662         0,                              /* tp_repr */
663         0,                              /* tp_as_number */
664         0,                              /* tp_as_sequence */
665         &hdrAsMapping,                  /* tp_as_mapping */
666 };
667
668 /*@}*/
669
670 /** \ingroup python
671  * \class rpmdbMatchIterator
672  * \brief A python rpmdbMatchIterator object represents the result of an RPM
673  *      database query.
674  */
675
676 /** \ingroup python
677  * \name Class: rpmdbMatchIterator
678  */
679 /*@{*/
680
681 /** \ingroup python
682  */
683 struct rpmdbObject_s {
684     PyObject_HEAD;
685     rpmdb db;
686     int offx;
687     int noffs;
688     int *offsets;
689 } ;
690
691 /** \ingroup python
692  */
693 struct rpmdbMIObject_s {
694     PyObject_HEAD;
695     rpmdbObject *db;
696     rpmdbMatchIterator mi;
697 } ;
698
699 /** \ingroup python
700  */
701 static PyObject *
702 rpmdbMINext(rpmdbMIObject * s, PyObject * args) {
703     /* XXX assume header? */
704     Header h;
705     hdrObject * ho;
706     
707
708     h = rpmdbNextIterator(s->mi);
709     if (!h) {
710         Py_INCREF(Py_None);
711         return Py_None;
712     }
713
714     ho = PyObject_NEW(hdrObject, &hdrType);
715     ho->h = headerLink(h);
716     ho->sigs = NULL;
717     ho->fileList = ho->linkList = ho->md5list = NULL;
718     ho->uids = ho->gids = ho->mtimes = ho->fileSizes = NULL;
719     ho->modes = ho->rdevs = NULL;
720     
721     return (PyObject *) ho;
722 }
723
724 /** \ingroup python
725  */
726 static struct PyMethodDef rpmdbMIMethods[] = {
727         {"next",            (PyCFunction) rpmdbMINext,  1 },
728         {NULL,          NULL}           /* sentinel */
729 };
730
731 /** \ingroup python
732  */
733 static PyObject * rpmdbMIGetAttr (rpmdbObject *s, char *name) {
734     return Py_FindMethod (rpmdbMIMethods, (PyObject *) s, name);
735 }
736
737 /** \ingroup python
738  */
739 static void rpmdbMIDealloc(rpmdbMIObject * s) {
740     if (s && s->mi) {
741         rpmdbFreeIterator(s->mi);
742     }
743     Py_DECREF (s->db);
744     PyMem_DEL(s);
745 }
746
747 /** \ingroup python
748  */
749 static PyTypeObject rpmdbMIType = {
750         PyObject_HEAD_INIT(&PyType_Type)
751         0,                              /* ob_size */
752         "rpmdbMatchIterator",           /* tp_name */
753         sizeof(rpmdbMIObject),  /* tp_size */
754         0,                              /* tp_itemsize */
755         (destructor) rpmdbMIDealloc,    /* tp_dealloc */
756         0,                              /* tp_print */
757         (getattrfunc) rpmdbMIGetAttr,   /* tp_getattr */
758         0,                              /* tp_setattr */
759         0,                              /* tp_compare */
760         0,                              /* tp_repr */
761         0,                              /* tp_as_number */
762         0,                              /* tp_as_sequence */
763         0,                              /* tp_as_mapping */
764 };
765
766 /*@}*/
767
768 /** \ingroup python
769  * \class rpmdb
770  * \brief A python rpmdb object represents an RPM database.
771  * 
772  * Instances of the rpmdb object provide access to the records of a
773  * RPM database.  The records are accessed by index number.  To
774  * retrieve the header data in the RPM database, the rpmdb object is
775  * subscripted as you would access members of a list.
776  * 
777  * The rpmdb class contains the following methods:
778  * 
779  * - firstkey() Returns the index of the first record in the database.
780  * @deprecated  Legacy, use rpmdbMatchIterator instead.
781  * 
782  * - nextkey(index) Returns the index of the next record after "index" in the
783  *              database.
784  * @param index current rpmdb location
785  * @deprecated  Legacy, use rpmdbMatchIterator instead.
786  * 
787  * - findbyfile(file) Returns a list of the indexes to records that own file
788  *              "file".
789  * @param file  absolute path to file
790  * 
791  * - findbyname(name) Returns a list of the indexes to records for packages
792  *              named "name".
793  * @param name  package name
794  * 
795  * - findbyprovides(dep) Returns a list of the indexes to records for packages
796  *              that provide "dep".
797  * @param dep   provided dependency string
798  * 
799  * To obtain a rpmdb object, the opendb function in the rpm module
800  * must be called.  The opendb function takes two optional arguments.
801  * The first optional argument is a boolean flag that specifies if the
802  * database is to be opened for read/write access or read-only access.
803  * The second argument specifies an alternate root directory for RPM
804  * to use.
805  * 
806  * An example of opening a database and retrieving the first header in
807  * the database, then printing the name of the package that the header
808  * represents:
809  * \code
810  *      import rpm
811  *      rpmdb = rpm.opendb()
812  *      index = rpmdb.firstkey()
813  *      header = rpmdb[index]
814  *      print header[rpm.RPMTAG_NAME]
815  * \endcode
816  * To print all of the packages in the database that match a package
817  * name, the code will look like this:
818  * \code
819  *      import rpm
820  *      rpmdb = rpm.opendb()
821  *      indexes = rpmdb.findbyname("foo")
822  *      for index in indexes:
823  *          header = rpmdb[index]
824  *          print "%s-%s-%s" % (header[rpm.RPMTAG_NAME],
825  *                              header[rpm.RPMTAG_VERSION],
826  *                              header[rpm.RPMTAG_RELEASE])
827  * \endcode
828  */
829
830 /** \ingroup python
831  * \name Class: rpmdb
832  */
833 /*@{*/
834
835 /** \ingroup python
836  */
837 static PyObject * rpmdbFirst(rpmdbObject * s, PyObject * args) {
838     int first;
839
840     if (!PyArg_ParseTuple (args, "")) return NULL;
841
842     /* Acquire all offsets in one fell swoop. */
843     if (s->offsets == NULL || s->noffs <= 0) {
844         rpmdbMatchIterator mi;
845         Header h;
846
847         if (s->offsets)
848             free(s->offsets);
849         s->offsets = NULL;
850         s->noffs = 0;
851         mi = rpmdbInitIterator(s->db, RPMDBI_PACKAGES, NULL, 0);
852         while ((h = rpmdbNextIterator(mi)) != NULL) {
853             s->noffs++;
854             s->offsets = realloc(s->offsets, s->noffs * sizeof(s->offsets[0]));
855             s->offsets[s->noffs-1] = rpmdbGetIteratorOffset(mi);
856         }
857         rpmdbFreeIterator(mi);
858     }
859
860     s->offx = 0;
861     if (s->offsets != NULL && s->offx < s->noffs)
862         first = s->offsets[s->offx++];
863     else
864         first = 0;
865
866     if (!first) {
867         PyErr_SetString(pyrpmError, "cannot find first entry in database\n");
868         return NULL;
869     }
870
871     return Py_BuildValue("i", first);
872 }
873
874 /** \ingroup python
875  */
876 static PyObject * rpmdbNext(rpmdbObject * s, PyObject * args) {
877     int where;
878
879     if (!PyArg_ParseTuple (args, "i", &where)) return NULL;
880
881     if (s->offsets == NULL || s->offx >= s->noffs) {
882         Py_INCREF(Py_None);
883         return Py_None;
884     }
885
886     where = s->offsets[s->offx++];
887
888     if (!where) {
889         Py_INCREF(Py_None);
890         return Py_None;
891     }
892
893     return Py_BuildValue("i", where);
894 }
895
896 /** \ingroup python
897  */
898 static PyObject * handleDbResult(rpmdbMatchIterator mi) {
899     PyObject * list, *o;
900
901     list = PyList_New(0);
902
903     /* XXX FIXME: unnecessary header mallocs are side effect here */
904     if (mi != NULL) {
905         while (rpmdbNextIterator(mi)) {
906             PyList_Append(list, o=PyInt_FromLong(rpmdbGetIteratorOffset(mi)));
907             Py_DECREF(o);
908         }
909         rpmdbFreeIterator(mi);
910     }
911
912     return list;
913 }
914
915 /** \ingroup python
916  */
917 static PyObject * rpmdbByFile(rpmdbObject * s, PyObject * args) {
918     char * str;
919
920     if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
921
922     return handleDbResult(rpmdbInitIterator(s->db, RPMTAG_BASENAMES, str, 0));
923 }
924
925 /** \ingroup python
926  */
927 static PyObject * rpmdbByName(rpmdbObject * s, PyObject * args) {
928     char * str;
929
930     if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
931
932     return handleDbResult(rpmdbInitIterator(s->db, RPMTAG_NAME, str, 0));
933 }
934
935 /** \ingroup python
936  */
937 static PyObject * rpmdbByProvides(rpmdbObject * s, PyObject * args) {
938     char * str;
939
940     if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
941
942     return handleDbResult(rpmdbInitIterator(s->db, RPMTAG_PROVIDENAME, str, 0));
943 }
944
945 /** \ingroup python
946  */
947 static rpmdbMIObject *
948 py_rpmdbInitIterator (rpmdbObject * s, PyObject * args) {
949     PyObject *index = NULL;
950     char *key = NULL;
951     int len = 0, tag = -1;
952     rpmdbMIObject * mio;
953     
954     if (!PyArg_ParseTuple(args, "|Ozi", &index, &key, &len))
955         return NULL;
956
957     if (index == NULL)
958         tag = 0;
959     else if ((tag = tagNumFromPyObject (index)) == -1) {
960         PyErr_SetString(PyExc_TypeError, "unknown tag type");
961         return NULL;
962     }
963     
964     mio = (rpmdbMIObject *) PyObject_NEW(rpmdbMIObject, &rpmdbMIType);
965     if (mio == NULL) {
966         PyErr_SetString(pyrpmError, "out of memory creating rpmdbMIObject");
967         return NULL;
968     }
969     
970     mio->mi = rpmdbInitIterator(s->db, tag, key, len);
971     mio->db = s;
972     Py_INCREF (mio->db);
973     
974     return mio;
975 }
976
977 /** \ingroup python
978  */
979 static struct PyMethodDef rpmdbMethods[] = {
980         {"firstkey",        (PyCFunction) rpmdbFirst,   1 },
981         {"nextkey",         (PyCFunction) rpmdbNext,    1 },
982         {"findbyfile",      (PyCFunction) rpmdbByFile, 1 },
983         {"findbyname",      (PyCFunction) rpmdbByName, 1 },
984         {"findbyprovides",  (PyCFunction) rpmdbByProvides, 1 },
985         {"match",           (PyCFunction) py_rpmdbInitIterator, 1 },
986         {NULL,          NULL}           /* sentinel */
987 };
988
989 /** \ingroup python
990  */
991 static PyObject * rpmdbGetAttr(rpmdbObject * s, char * name) {
992     return Py_FindMethod(rpmdbMethods, (PyObject * ) s, name);
993 }
994
995 /** \ingroup python
996  */
997 static void rpmdbDealloc(rpmdbObject * s) {
998     if (s->offsets) {
999         free(s->offsets);
1000     }
1001     if (s->db) {
1002         rpmdbClose(s->db);
1003     }
1004     PyMem_DEL(s);
1005 }
1006
1007 #ifndef DYINGSOON
1008 /** \ingroup python
1009  */
1010 static int
1011 rpmdbLength(rpmdbObject * s) {
1012     int count = 0;
1013
1014     {   rpmdbMatchIterator mi;
1015
1016         /* RPMDBI_PACKAGES */
1017         mi = rpmdbInitIterator(s->db, RPMDBI_PACKAGES, NULL, 0);
1018         /* XXX FIXME: unnecessary header mallocs are side effect here */
1019         while (rpmdbNextIterator(mi) != NULL)
1020             count++;
1021         rpmdbFreeIterator(mi);
1022     }
1023
1024     return count;
1025 }
1026
1027 /** \ingroup python
1028  */
1029 static hdrObject *
1030 rpmdbSubscript(rpmdbObject * s, PyObject * key) {
1031     int offset;
1032     hdrObject * h;
1033
1034     if (!PyInt_Check(key)) {
1035         PyErr_SetString(PyExc_TypeError, "integer expected");
1036         return NULL;
1037     }
1038
1039     offset = (int) PyInt_AsLong(key);
1040
1041     h = PyObject_NEW(hdrObject, &hdrType);
1042     h->h = NULL;
1043     h->sigs = NULL;
1044     {   rpmdbMatchIterator mi;
1045         mi = rpmdbInitIterator(s->db, RPMDBI_PACKAGES, &offset, sizeof(offset));
1046         if ((h->h = rpmdbNextIterator(mi)) != NULL)
1047             h->h = headerLink(h->h);
1048         rpmdbFreeIterator(mi);
1049     }
1050     h->fileList = h->linkList = h->md5list = NULL;
1051     h->uids = h->gids = h->mtimes = h->fileSizes = NULL;
1052     h->modes = h->rdevs = NULL;
1053     if (!h->h) {
1054         Py_DECREF(h);
1055         PyErr_SetString(pyrpmError, "cannot read rpmdb entry");
1056         return NULL;
1057     }
1058
1059     return h;
1060 }
1061
1062 /** \ingroup python
1063  */
1064 static PyMappingMethods rpmdbAsMapping = {
1065         (inquiry) rpmdbLength,          /* mp_length */
1066         (binaryfunc) rpmdbSubscript,    /* mp_subscript */
1067         (objobjargproc)0,               /* mp_ass_subscript */
1068 };
1069 #endif
1070
1071 /** \ingroup python
1072  */
1073 static PyTypeObject rpmdbType = {
1074         PyObject_HEAD_INIT(&PyType_Type)
1075         0,                              /* ob_size */
1076         "rpmdb",                        /* tp_name */
1077         sizeof(rpmdbObject),            /* tp_size */
1078         0,                              /* tp_itemsize */
1079         (destructor) rpmdbDealloc,      /* tp_dealloc */
1080         0,                              /* tp_print */
1081         (getattrfunc) rpmdbGetAttr,     /* tp_getattr */
1082         0,                              /* tp_setattr */
1083         0,                              /* tp_compare */
1084         0,                              /* tp_repr */
1085         0,                              /* tp_as_number */
1086         0,                              /* tp_as_sequence */
1087 #ifndef DYINGSOON
1088         &rpmdbAsMapping,                /* tp_as_mapping */
1089 #else
1090         0,
1091 #endif
1092 };
1093
1094 /*@}*/
1095
1096 /** \ingroup python
1097  * \name Class: rpmtrans
1098  * \class rpmtrans
1099  * \brief A python rpmtrans object represents an RPM transaction set.
1100  * 
1101  * The transaction set is the workhorse of RPM.  It performs the
1102  * installation and upgrade of packages.  The rpmtrans object is
1103  * instantiated by the TransactionSet function in the rpm module.
1104  *
1105  * The TransactionSet function takes two optional arguments.  The first
1106  * argument is the root path, the second is an open database to perform
1107  * the transaction set upon.
1108  *
1109  * A rpmtrans object has the following methods:
1110  *
1111  * - add(header,data,mode)      Add a binary package to a transaction set.
1112  * @param header the header to be added
1113  * @param data  user data that will be passed to the transaction callback
1114  *              during transaction execution
1115  * @param mode  optional argument that specifies if this package should
1116  *              be installed ('i'), upgraded ('u'), or if it is just
1117  *              available to the transaction when computing
1118  *              dependencies but no action should be performed with it
1119  *              ('a').
1120  *
1121  * - remove
1122  *
1123  * - depcheck() Perform a dependency and conflict check on the
1124  *              transaction set. After headers have been added to a
1125  *              transaction set, a dependency check can be performed
1126  *              to make sure that all package dependencies are
1127  *              satisfied.
1128  * @return      None If there are no unresolved dependencies
1129  *              Otherwise a list of complex tuples is returned, one tuple per
1130  *              unresolved dependency, with
1131  * The format of the dependency tuple is:
1132  *     ((packageName, packageVersion, packageRelease),
1133  *      (reqName, reqVersion),
1134  *      needsFlags,
1135  *      suggestedPackage,
1136  *      sense)
1137  *     packageName, packageVersion, packageRelease are the name,
1138  *     version, and release of the package that has the unresolved
1139  *     dependency or conflict.
1140  *     The reqName and reqVersion are the name and version of the
1141  *     requirement or conflict.
1142  *     The needsFlags is a bitfield that describes the versioned
1143  *     nature of a requirement or conflict.  The constants
1144  *     rpm.RPMDEP_SENSE_LESS, rpm.RPMDEP_SENSE_GREATER, and
1145  *     rpm.RPMDEP_SENSE_EQUAL can be logical ANDed with the needsFlags
1146  *     to get versioned dependency information.
1147  *     suggestedPackage is a tuple if the dependency check was aware
1148  *     of a package that solves this dependency problem when the
1149  *     dependency check was run.  Packages that are added to the
1150  *     transaction set as "available" are examined during the
1151  *     dependency check as possible dependency solvers. The tuple
1152  *     contains two values, (header, suggestedName).  These are set to
1153  *     the header of the suggested package and its name, respectively.
1154  *     If there is no known package to solve the dependency problem,
1155  *     suggestedPackage is None.
1156  *     The constants rpm.RPMDEP_SENSE_CONFLICTS and
1157  *     rpm.RPMDEP_SENSE_REQUIRES are set to show a dependency as a
1158  *     requirement or a conflict.
1159  *
1160  * - run(flags,problemSetFilter,callback,data) Attempt to execute a
1161  *      transaction set. After the transaction set has been populated
1162  *      with install and upgrade actions, it can be executed by invoking
1163  *      the run() method.
1164  * @param flags - modifies the behavior of the transaction set as it is
1165  *              processed.  The following values can be locical ORed
1166  *              together:
1167  *      - rpm.RPMTRANS_FLAG_TEST - test mode, do not modify the RPM
1168  *              database, change any files, or run any package scripts
1169  *      - rpm.RPMTRANS_FLAG_BUILD_PROBS - only build a list of
1170  *              problems encountered when attempting to run this transaction
1171  *              set
1172  *      - rpm.RPMTRANS_FLAG_NOSCRIPTS - do not execute package scripts
1173  *      - rpm.RPMTRANS_FLAG_JUSTDB - only make changes to the rpm
1174  *              database, do not modify files.
1175  *      - rpm.RPMTRANS_FLAG_NOTRIGGERS - do not run trigger scripts
1176  *      - rpm.RPMTRANS_FLAG_NODOCS - do not install files marked as %doc
1177  *      - rpm.RPMTRANS_FLAG_ALLFILES - create all files, even if a
1178  *              file is marked %config(missingok) and an upgrade is
1179  *              being performed.
1180  *      - rpm.RPMTRANS_FLAG_KEEPOBSOLETE - do not remove obsoleted
1181  *              packages.
1182  * @param problemSetFilter - control bit(s) to ignore classes of problems,
1183  *              any of
1184  *      - rpm.RPMPROB_FILTER_IGNOREOS - 
1185  *      - rpm.RPMPROB_FILTER_IGNOREARCH - 
1186  *      - rpm.RPMPROB_FILTER_REPLACEPKG - 
1187  *      - rpm.RPMPROB_FILTER_FORCERELOCATE - 
1188  *      - rpm.RPMPROB_FILTER_REPLACENEWFILES - 
1189  *      - rpm.RPMPROB_FILTER_REPLACEOLDFILES - 
1190  *      - rpm.RPMPROB_FILTER_OLDPACKAGE - 
1191  *      - rpm.RPMPROB_FILTER_DISKSPACE - 
1192  */
1193
1194 /** \ingroup python
1195  * \name Class: rpmtrans
1196  */
1197 /*@{*/
1198
1199 /** \ingroup python
1200  */
1201 struct rpmtransObject_s {
1202     PyObject_HEAD;
1203     rpmdbObject * dbo;
1204     rpmTransactionSet ts;
1205     PyObject * keyList;                 /* keeps reference counts correct */
1206     FD_t scriptFd;
1207 } ;
1208
1209 /** \ingroup python
1210  */
1211 static PyObject * rpmtransAdd(rpmtransObject * s, PyObject * args) {
1212     hdrObject * h;
1213     PyObject * key;
1214     char * how = NULL;
1215     int isUpgrade = 0;
1216
1217     if (!PyArg_ParseTuple(args, "OO|s", &h, &key, &how)) return NULL;
1218     if (h->ob_type != &hdrType) {
1219         PyErr_SetString(PyExc_TypeError, "bad type for header argument");
1220         return NULL;
1221     }
1222
1223     if (how && strcmp(how, "a") && strcmp(how, "u") && strcmp(how, "i")) {
1224         PyErr_SetString(PyExc_TypeError, "how argument must be \"u\", \"a\", or \"i\"");
1225         return NULL;
1226     } else if (how && !strcmp(how, "u"))
1227         isUpgrade = 1;
1228
1229     if (how && !strcmp(how, "a"))
1230         rpmtransAvailablePackage(s->ts, h->h, key);
1231     else
1232         rpmtransAddPackage(s->ts, h->h, NULL, key, isUpgrade, NULL);
1233
1234     /* This should increment the usage count for me */
1235     if (key) {
1236         PyList_Append(s->keyList, key);
1237     }
1238
1239     Py_INCREF(Py_None);
1240     return Py_None;
1241 }
1242
1243 /** \ingroup python
1244  */
1245 static PyObject * rpmtransRemove(rpmtransObject * s, PyObject * args) {
1246     char * name;
1247     int count;
1248     rpmdbMatchIterator mi;
1249     
1250     if (!PyArg_ParseTuple(args, "s", &name))
1251         return NULL;
1252
1253     /* XXX: Copied hack from ../lib/rpminstall.c, rpmErase() */
1254     mi = rpmdbInitIterator(s->dbo->db, RPMDBI_LABEL, name, 0);
1255     count = rpmdbGetIteratorCount(mi);
1256     if (count <= 0) {
1257         PyErr_SetString(pyrpmError, "package not installed");
1258         return NULL;
1259     } else { /* XXX: Note that we automatically choose to remove all matches */
1260         Header h;
1261         while ((h = rpmdbNextIterator(mi)) != NULL) {
1262             unsigned int recOffset = rpmdbGetIteratorOffset(mi);
1263             if (recOffset) {
1264                 rpmtransRemovePackage(s->ts, recOffset);
1265             }
1266         }
1267     }
1268     rpmdbFreeIterator(mi);
1269
1270     Py_INCREF(Py_None);
1271     return Py_None;
1272 }
1273
1274 /** \ingroup python
1275  */
1276 static PyObject * rpmtransDepCheck(rpmtransObject * s, PyObject * args) {
1277     struct rpmDependencyConflict_s * conflicts;
1278     int numConflicts;
1279     PyObject * list, * cf;
1280     int i;
1281
1282     if (!PyArg_ParseTuple(args, "")) return NULL;
1283
1284     rpmdepCheck(s->ts, &conflicts, &numConflicts);
1285     if (numConflicts) {
1286         list = PyList_New(0);
1287
1288         /* XXX TODO: rpmlib-4.0.3 can return multiple suggested packages. */
1289         for (i = 0; i < numConflicts; i++) {
1290             cf = Py_BuildValue("((sss)(ss)iOi)", conflicts[i].byName,
1291                                conflicts[i].byVersion, conflicts[i].byRelease,
1292
1293                                conflicts[i].needsName,
1294                                conflicts[i].needsVersion,
1295
1296                                conflicts[i].needsFlags,
1297                                conflicts[i].suggestedPackages ?
1298                                    conflicts[i].suggestedPackages[0] : Py_None,
1299                                conflicts[i].sense);
1300             PyList_Append(list, (PyObject *) cf);
1301             Py_DECREF(cf);
1302         }
1303
1304         conflicts = rpmdepFreeConflicts(conflicts, numConflicts);
1305
1306         return list;
1307     }
1308
1309     Py_INCREF(Py_None);
1310     return Py_None;
1311 }
1312
1313 /** \ingroup python
1314  */
1315 static PyObject * rpmtransOrder(rpmtransObject * s, PyObject * args) {
1316     if (!PyArg_ParseTuple(args, "")) return NULL;
1317
1318     rpmdepOrder(s->ts);
1319
1320     Py_INCREF(Py_None);
1321     return Py_None;
1322 }
1323
1324 /** \ingroup python
1325  */
1326 static PyObject * py_rpmtransGetKeys(rpmtransObject * s, PyObject * args) {
1327     const void **data = NULL;
1328     int num, i;
1329     PyObject *tuple;
1330
1331     rpmtransGetKeys(s->ts, &data, &num);
1332     if (data == NULL) {
1333         Py_INCREF(Py_None);
1334         return Py_None;
1335     }
1336
1337     tuple = PyTuple_New(num);
1338
1339     for (i = 0; i < num; i++) {
1340         PyObject *obj = (PyObject *) data[i];
1341         Py_INCREF(obj);
1342         PyTuple_SetItem(tuple, i, obj);
1343     }
1344
1345     free (data);
1346
1347     return tuple;
1348 }
1349
1350 /** \ingroup python
1351  */
1352 struct tsCallbackType {
1353     PyObject * cb;
1354     PyObject * data;
1355     int pythonError;
1356 };
1357
1358 /** \ingroup python
1359  */
1360 static Header transactionSetHeader = NULL;
1361
1362 /** \ingroup python
1363  */
1364 static void * tsCallback(const void * hd, const rpmCallbackType what,
1365                          const unsigned long amount, const unsigned long total,
1366                          const void * pkgKey, rpmCallbackData data) {
1367     struct tsCallbackType * cbInfo = data;
1368     PyObject * args, * result;
1369     int fd;
1370     static FD_t fdt;
1371     const Header h = (Header) hd;
1372
1373     if (cbInfo->pythonError) return NULL;
1374
1375     if (!pkgKey) pkgKey = Py_None;
1376     transactionSetHeader = h;    
1377
1378     args = Py_BuildValue("(illOO)", what, amount, total, pkgKey, cbInfo->data);
1379     result = PyEval_CallObject(cbInfo->cb, args);
1380     Py_DECREF(args);
1381
1382     if (!result) {
1383         cbInfo->pythonError = 1;
1384         return NULL;
1385     }
1386
1387     if (what == RPMCALLBACK_INST_OPEN_FILE) {
1388         if (!PyArg_Parse(result, "i", &fd)) {
1389             cbInfo->pythonError = 1;
1390             return NULL;
1391         }
1392         fdt = fdDup(fd);
1393         
1394         Py_DECREF(result);
1395         return fdt;
1396     }
1397
1398     if (what == RPMCALLBACK_INST_CLOSE_FILE) {
1399         Fclose (fdt);
1400     }
1401
1402     Py_DECREF(result);
1403
1404     return NULL;
1405 }
1406
1407 /** \ingroup python
1408  */
1409 static PyObject * rpmtransRun(rpmtransObject * s, PyObject * args) {
1410     int flags, ignoreSet;
1411     int rc, i;
1412     PyObject * list, * prob;
1413     rpmProblemSet probs;
1414     struct tsCallbackType cbInfo;
1415
1416     if (!PyArg_ParseTuple(args, "iiOO", &flags, &ignoreSet, &cbInfo.cb,
1417                           &cbInfo.data))
1418         return NULL;
1419
1420     cbInfo.pythonError = 0;
1421
1422     rc = rpmRunTransactions(s->ts, tsCallback, &cbInfo, NULL, &probs, flags,
1423                             ignoreSet);
1424
1425     if (cbInfo.pythonError) {
1426         if (rc > 0)
1427             rpmProblemSetFree(probs);
1428         return NULL;
1429     }
1430
1431     if (rc < 0) {
1432         list = PyList_New(0);
1433         return list;
1434     } else if (!rc) {
1435         Py_INCREF(Py_None);
1436         return Py_None;
1437     }
1438
1439     list = PyList_New(0);
1440     for (i = 0; i < probs->numProblems; i++) {
1441         rpmProblem myprob = probs->probs + i;
1442         prob = Py_BuildValue("s(isi)", rpmProblemString(myprob),
1443                              myprob->type,
1444                              myprob->str1,
1445                              myprob->ulong1);
1446         PyList_Append(list, prob);
1447         Py_DECREF(prob);
1448     }
1449
1450     rpmProblemSetFree(probs);
1451
1452     return list;
1453 }
1454
1455 /** \ingroup python
1456  */
1457 static struct PyMethodDef rpmtransMethods[] = {
1458         {"add",         (PyCFunction) rpmtransAdd,      1 },
1459         {"remove",      (PyCFunction) rpmtransRemove,   1 },
1460         {"depcheck",    (PyCFunction) rpmtransDepCheck, 1 },
1461         {"order",       (PyCFunction) rpmtransOrder,    1 },
1462         {"getKeys",     (PyCFunction) py_rpmtransGetKeys, 1 },
1463         {"run",         (PyCFunction) rpmtransRun, 1 },
1464         {NULL,          NULL}           /* sentinel */
1465 };
1466
1467 /** \ingroup python
1468  */
1469 static PyObject * rpmtransGetAttr(rpmtransObject * o, char * name) {
1470     return Py_FindMethod(rpmtransMethods, (PyObject *) o, name);
1471 }
1472
1473 /** \ingroup python
1474  */
1475 static void rpmtransDealloc(PyObject * o) {
1476     rpmtransObject * trans = (void *) o;
1477
1478     rpmtransFree(trans->ts);
1479     if (trans->dbo) {
1480         Py_DECREF(trans->dbo);
1481     }
1482     if (trans->scriptFd) Fclose(trans->scriptFd);
1483     /* this will free the keyList, and decrement the ref count of all
1484        the items on the list as well :-) */
1485     Py_DECREF(trans->keyList);
1486     PyMem_DEL(o);
1487 }
1488
1489 /** \ingroup python
1490  */
1491 static int rpmtransSetAttr(rpmtransObject * o, char * name,
1492                            PyObject * val) {
1493     int i;
1494
1495     if (!strcmp(name, "scriptFd")) {
1496         if (!PyArg_Parse(val, "i", &i)) return 0;
1497         if (i < 0) {
1498             PyErr_SetString(PyExc_TypeError, "bad file descriptor");
1499             return -1;
1500         } else {
1501             o->scriptFd = fdDup(i);
1502             rpmtransSetScriptFd(o->ts, o->scriptFd);
1503         }
1504     } else {
1505         PyErr_SetString(PyExc_AttributeError, name);
1506         return -1;
1507     }
1508
1509     return 0;
1510 }
1511
1512 /** \ingroup python
1513  */
1514 static PyTypeObject rpmtransType = {
1515         PyObject_HEAD_INIT(&PyType_Type)
1516         0,                              /* ob_size */
1517         "rpmtrans",                     /* tp_name */
1518         sizeof(rpmtransObject),         /* tp_size */
1519         0,                              /* tp_itemsize */
1520         (destructor) rpmtransDealloc,   /* tp_dealloc */
1521         0,                              /* tp_print */
1522         (getattrfunc) rpmtransGetAttr,  /* tp_getattr */
1523         (setattrfunc) rpmtransSetAttr,  /* tp_setattr */
1524         0,                              /* tp_compare */
1525         0,                              /* tp_repr */
1526         0,                              /* tp_as_number */
1527         0,                              /* tp_as_sequence */
1528         0,                              /* tp_as_mapping */
1529 };
1530
1531 /*@}*/
1532
1533 /** \ingroup python
1534  * \name Module: rpm
1535  */
1536 /*@{*/
1537
1538 /** \ingroup python
1539  */
1540 static PyObject * rpmtransCreate(PyObject * self, PyObject * args) {
1541     rpmtransObject * o;
1542     rpmdbObject * db = NULL;
1543     char * rootPath = "/";
1544
1545     if (!PyArg_ParseTuple(args, "|sO", &rootPath, &db)) return NULL;
1546     if (db && db->ob_type != &rpmdbType) {
1547         PyErr_SetString(PyExc_TypeError, "bad type for database argument");
1548         return NULL;
1549     }
1550
1551     o = (void *) PyObject_NEW(rpmtransObject, &rpmtransType);
1552
1553     Py_XINCREF(db);
1554     o->dbo = db;
1555     o->scriptFd = NULL;
1556     o->ts = rpmtransCreateSet(db ? db->db : NULL, rootPath);
1557     o->keyList = PyList_New(0);
1558
1559     return (void *) o;
1560 }
1561
1562 /** \ingroup python
1563  */
1564 static PyObject * doAddMacro(PyObject * self, PyObject * args) {
1565     char * name, * val;
1566
1567     if (!PyArg_ParseTuple(args, "ss", &name, &val))
1568         return NULL;
1569
1570     addMacro(NULL, name, NULL, val, RMIL_DEFAULT);
1571
1572     Py_INCREF(Py_None);
1573     return Py_None;
1574 }
1575
1576 /** \ingroup python
1577  */
1578 static PyObject * doDelMacro(PyObject * self, PyObject * args) {
1579     char * name;
1580
1581     if (!PyArg_ParseTuple(args, "s", &name))
1582         return NULL;
1583
1584     delMacro(NULL, name);
1585
1586     Py_INCREF(Py_None);
1587     return Py_None;
1588 }
1589
1590 /** \ingroup python
1591  */
1592 static PyObject * archScore(PyObject * self, PyObject * args) {
1593     char * arch;
1594     int score;
1595
1596     if (!PyArg_ParseTuple(args, "s", &arch))
1597         return NULL;
1598
1599     score = rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch);
1600
1601     return Py_BuildValue("i", score);
1602 }
1603
1604 /** \ingroup python
1605  */
1606 static int psGetArchScore(Header h) {
1607     void * pkgArch;
1608     int type, count;
1609
1610     if (!headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count) ||
1611         type == RPM_INT8_TYPE)
1612        return 150;
1613     else
1614         return rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch);
1615 }
1616
1617 /** \ingroup python
1618  */
1619 static int pkgCompareVer(void * first, void * second) {
1620     struct packageInfo ** a = first;
1621     struct packageInfo ** b = second;
1622     int ret, score1, score2;
1623
1624     /* put packages w/o names at the end */
1625     if (!(*a)->name) return 1;
1626     if (!(*b)->name) return -1;
1627
1628     ret = xstrcasecmp((*a)->name, (*b)->name);
1629     if (ret) return ret;
1630     score1 = psGetArchScore((*a)->h);
1631     if (!score1) return 1;
1632     score2 = psGetArchScore((*b)->h);
1633     if (!score2) return -1;
1634     if (score1 < score2) return -1;
1635     if (score1 > score2) return 1;
1636     return rpmVersionCompare((*b)->h, (*a)->h);
1637 }
1638
1639 /** \ingroup python
1640  */
1641 static void pkgSort(struct pkgSet * psp) {
1642     int i;
1643     char *name;
1644
1645     qsort(psp->packages, psp->numPackages, sizeof(*psp->packages),
1646          (void *) pkgCompareVer);
1647
1648     name = psp->packages[0]->name;
1649     if (!name) {
1650        psp->numPackages = 0;
1651        return;
1652     }
1653     for (i = 1; i < psp->numPackages; i++) {
1654        if (!psp->packages[i]->name) break;
1655        if (!strcmp(psp->packages[i]->name, name))
1656            psp->packages[i]->name = NULL;
1657        else
1658            name = psp->packages[i]->name;
1659     }
1660
1661     qsort(psp->packages, psp->numPackages, sizeof(*psp->packages),
1662          (void *) pkgCompareVer);
1663
1664     for (i = 0; i < psp->numPackages; i++)
1665        if (!psp->packages[i]->name) break;
1666     psp->numPackages = i;
1667 }
1668
1669 /** \ingroup python
1670  */
1671 static PyObject * findUpgradeSet(PyObject * self, PyObject * args) {
1672     PyObject * hdrList, * result;
1673     char * root = "/";
1674     int i;
1675     struct pkgSet list;
1676     hdrObject * hdr;
1677
1678     if (!PyArg_ParseTuple(args, "O|s", &hdrList, &root)) return NULL;
1679
1680     if (!PyList_Check(hdrList)) {
1681         PyErr_SetString(PyExc_TypeError, "list of headers expected");
1682         return NULL;
1683     }
1684
1685     list.numPackages = PyList_Size(hdrList);
1686     list.packages = alloca(sizeof(list.packages) * list.numPackages);
1687     for (i = 0; i < list.numPackages; i++) {
1688         hdr = (hdrObject *) PyList_GetItem(hdrList, i);
1689         if (hdr->ob_type != &hdrType) {
1690             PyErr_SetString(PyExc_TypeError, "list of headers expected");
1691             return NULL;
1692         }
1693         list.packages[i] = alloca(sizeof(struct packageInfo));
1694         list.packages[i]->h = hdr->h;
1695         list.packages[i]->selected = 0;
1696         list.packages[i]->data = hdr;
1697
1698         headerGetEntry(hdr->h, RPMTAG_NAME, NULL,
1699                       (void **) &list.packages[i]->name, NULL);
1700     }
1701
1702     pkgSort (&list);
1703
1704     if (ugFindUpgradePackages(&list, root)) {
1705         PyErr_SetString(pyrpmError, "error during upgrade check");
1706         return NULL;
1707     }
1708
1709     result = PyList_New(0);
1710     for (i = 0; i < list.numPackages; i++) {
1711         if (list.packages[i]->selected) {
1712             PyList_Append(result, list.packages[i]->data);
1713 /*          Py_DECREF(list.packages[i]->data); */
1714         }
1715     }
1716
1717     return result;
1718 }
1719
1720 /** \ingroup python
1721  */
1722 static PyObject * rpmHeaderFromPackage(PyObject * self, PyObject * args) {
1723     hdrObject * h;
1724     Header header;
1725     Header sigs;
1726     FD_t fd;
1727     int rawFd;
1728     int isSource = 0;
1729     rpmRC rc;
1730
1731     if (!PyArg_ParseTuple(args, "i", &rawFd)) return NULL;
1732     fd = fdDup(rawFd);
1733
1734     rc = rpmReadPackageInfo(fd, &sigs, &header);
1735     Fclose(fd);
1736
1737     switch (rc) {
1738     case RPMRC_BADSIZE:
1739     case RPMRC_OK:
1740         h = (hdrObject *) PyObject_NEW(PyObject, &hdrType);
1741         h->h = header;
1742         h->sigs = sigs;
1743         h->fileList = h->linkList = h->md5list = NULL;
1744         h->uids = h->gids = h->mtimes = h->fileSizes = NULL;
1745         h->modes = h->rdevs = NULL;
1746         if (headerIsEntry(header, RPMTAG_SOURCEPACKAGE))
1747             isSource = 1;
1748         break;
1749
1750     case RPMRC_BADMAGIC:
1751         Py_INCREF(Py_None);
1752         h = (hdrObject *) Py_None;
1753         break;
1754
1755     case RPMRC_FAIL:
1756     case RPMRC_SHORTREAD:
1757     default:
1758         PyErr_SetString(pyrpmError, "error reading package");
1759         return NULL;
1760     }
1761
1762     return Py_BuildValue("(Ni)", h, isSource);
1763 }
1764
1765 /** \ingroup python
1766  */
1767 static PyObject * hdrLoad(PyObject * self, PyObject * args) {
1768     char * obj, * copy=NULL;
1769     Header hdr;
1770     hdrObject * h;
1771     int len;
1772
1773     if (!PyArg_ParseTuple(args, "s#", &obj, &len)) return NULL;
1774     
1775     copy = malloc(len);
1776     if (copy == NULL) {
1777         PyErr_SetString(pyrpmError, "out of memory");
1778         return NULL;
1779     }
1780
1781     memcpy (copy, obj, len);
1782
1783     hdr = headerLoad(copy);
1784     if (!hdr) {
1785         PyErr_SetString(pyrpmError, "bad header");
1786         return NULL;
1787     }
1788     compressFilelist (hdr);
1789     providePackageNVR (hdr);
1790
1791     h = (hdrObject *) PyObject_NEW(PyObject, &hdrType);
1792     h->h = hdr;
1793     h->sigs = NULL;
1794     h->fileList = h->linkList = h->md5list = NULL;
1795     h->uids = h->gids = h->mtimes = h->fileSizes = NULL;
1796     h->modes = h->rdevs = NULL;
1797
1798     return (PyObject *) h;
1799 }
1800
1801 /** \ingroup python
1802  */
1803 static PyObject * rpmInitDB(PyObject * self, PyObject * args) {
1804     char *root;
1805     int forWrite = 0;
1806
1807     if (!PyArg_ParseTuple(args, "i|s", &forWrite, &root)) return NULL;
1808
1809     if (rpmdbInit(root, forWrite ? O_RDWR | O_CREAT: O_RDONLY)) {
1810         char * errmsg = "cannot initialize database in %s";
1811         char * errstr = NULL;
1812         int errsize;
1813
1814         errsize = strlen(errmsg) + strlen(root);
1815         errstr = alloca(errsize);
1816         snprintf(errstr, errsize, errmsg, root);
1817         PyErr_SetString(pyrpmError, errstr);
1818         return NULL;
1819     }
1820
1821     Py_INCREF(Py_None);
1822     return(Py_None);
1823 }
1824
1825 /** \ingroup python
1826  */
1827 static rpmdbObject * rpmOpenDB(PyObject * self, PyObject * args) {
1828     rpmdbObject * o;
1829     char * root = "";
1830     int forWrite = 0;
1831
1832     if (!PyArg_ParseTuple(args, "|is", &forWrite, &root)) return NULL;
1833
1834     o = PyObject_NEW(rpmdbObject, &rpmdbType);
1835     o->db = NULL;
1836     o->offx = 0;
1837     o->noffs = 0;
1838     o->offsets = NULL;
1839
1840     if (rpmdbOpen(root, &o->db, forWrite ? O_RDWR | O_CREAT: O_RDONLY, 0644)) {
1841         char * errmsg = "cannot open database in %s";
1842         char * errstr = NULL;
1843         int errsize;
1844
1845         Py_DECREF(o);
1846         /* PyErr_SetString should take varargs... */
1847         errsize = strlen(errmsg) + *root == '\0' ? 15 /* "/var/lib/rpm" */ : strlen(root);
1848         errstr = alloca(errsize);
1849         snprintf(errstr, errsize, errmsg, *root == '\0' ? "/var/lib/rpm" : root);
1850         PyErr_SetString(pyrpmError, errstr);
1851         return NULL;
1852     }
1853
1854     return o;
1855 }
1856
1857 /** \ingroup python
1858  */
1859 static PyObject * rebuildDB (PyObject * self, PyObject * args) {
1860     char * root = "";
1861
1862     if (!PyArg_ParseTuple(args, "s", &root)) return NULL;
1863
1864     return Py_BuildValue("i", rpmdbRebuild(root));
1865 }
1866
1867 /** \ingroup python
1868  */
1869 static PyObject * rpmReadHeaders (FD_t fd) {
1870     PyObject * list;
1871     Header header;
1872     hdrObject * h;
1873
1874     if (!fd) {
1875         PyErr_SetFromErrno(pyrpmError);
1876         return NULL;
1877     }
1878
1879     list = PyList_New(0);
1880     Py_BEGIN_ALLOW_THREADS
1881     header = headerRead(fd, HEADER_MAGIC_YES);
1882
1883     Py_END_ALLOW_THREADS
1884     while (header) {
1885         compressFilelist (header);
1886         providePackageNVR (header);
1887         h = (hdrObject *) PyObject_NEW(PyObject, &hdrType);
1888         h->h = header;
1889         h->sigs = NULL;
1890         h->fileList = h->linkList = h->md5list = NULL;
1891         h->uids = h->gids = h->mtimes = h->fileSizes = NULL;
1892         h->modes = h->rdevs = NULL;
1893         if (PyList_Append(list, (PyObject *) h)) {
1894             Py_DECREF(list);
1895             Py_DECREF(h);
1896             return NULL;
1897         }
1898
1899         Py_DECREF(h);
1900
1901         Py_BEGIN_ALLOW_THREADS
1902         header = headerRead(fd, HEADER_MAGIC_YES);
1903         Py_END_ALLOW_THREADS
1904     }
1905
1906     return list;
1907 }
1908
1909 /** \ingroup python
1910  */
1911 static PyObject * rpmHeaderFromFD(PyObject * self, PyObject * args) {
1912     FD_t fd;
1913     int fileno;
1914     PyObject * list;
1915
1916     if (!PyArg_ParseTuple(args, "i", &fileno)) return NULL;
1917     fd = fdDup(fileno);
1918
1919     list = rpmReadHeaders (fd);
1920     Fclose(fd);
1921
1922     return list;
1923 }
1924
1925 /** \ingroup python
1926  */
1927 static PyObject * rpmHeaderFromFile(PyObject * self, PyObject * args) {
1928     char * filespec;
1929     FD_t fd;
1930     PyObject * list;
1931
1932     if (!PyArg_ParseTuple(args, "s", &filespec)) return NULL;
1933     fd = Fopen(filespec, "r.fdio");
1934
1935     if (!fd) {
1936         PyErr_SetFromErrno(pyrpmError);
1937         return NULL;
1938     }
1939
1940     list = rpmReadHeaders (fd);
1941     Fclose(fd);
1942
1943     return list;
1944 }
1945
1946 /** \ingroup python
1947  * This assumes the order of list matches the order of the new headers, and
1948  * throws an exception if that isn't true.
1949  */
1950 static int rpmMergeHeaders(PyObject * list, FD_t fd, int matchTag) {
1951     Header newH;
1952     HeaderIterator iter;
1953     int_32 * newMatch, * oldMatch;
1954     hdrObject * ho;
1955     int count = 0;
1956     int type, c, tag;
1957     void * p;
1958
1959     Py_BEGIN_ALLOW_THREADS
1960     newH = headerRead(fd, HEADER_MAGIC_YES);
1961
1962     Py_END_ALLOW_THREADS
1963     while (newH) {
1964         if (!headerGetEntry(newH, matchTag, NULL, (void **) &newMatch, NULL)) {
1965             PyErr_SetString(pyrpmError, "match tag missing in new header");
1966             return 1;
1967         }
1968
1969         ho = (hdrObject *) PyList_GetItem(list, count++);
1970         if (!ho) return 1;
1971
1972         if (!headerGetEntry(ho->h, matchTag, NULL, (void **) &oldMatch, NULL)) {
1973             PyErr_SetString(pyrpmError, "match tag missing in new header");
1974             return 1;
1975         }
1976
1977         if (*newMatch != *oldMatch) {
1978             PyErr_SetString(pyrpmError, "match tag mismatch");
1979             return 1;
1980         }
1981
1982         if (ho->sigs) headerFree(ho->sigs);
1983         if (ho->md5list) free(ho->md5list);
1984         if (ho->fileList) free(ho->fileList);
1985         if (ho->linkList) free(ho->linkList);
1986
1987         ho->sigs = NULL;
1988         ho->md5list = NULL;
1989         ho->fileList = NULL;
1990         ho->linkList = NULL;
1991
1992         iter = headerInitIterator(newH);
1993
1994         while (headerNextIterator(iter, &tag, &type, (void *) &p, &c)) {
1995             /* could be dupes */
1996             headerRemoveEntry(ho->h, tag);
1997             headerAddEntry(ho->h, tag, type, p, c);
1998             headerFreeData(p, type);
1999         }
2000
2001         headerFreeIterator(iter);
2002
2003         Py_BEGIN_ALLOW_THREADS
2004         newH = headerRead(fd, HEADER_MAGIC_YES);
2005         Py_END_ALLOW_THREADS
2006     }
2007
2008     return 0;
2009 }
2010
2011 static PyObject * rpmMergeHeadersFromFD(PyObject * self, PyObject * args) {
2012     FD_t fd;
2013     int fileno;
2014     PyObject * list;
2015     int rc;
2016     int matchTag;
2017
2018     if (!PyArg_ParseTuple(args, "Oii", &list, &fileno, &matchTag)) return NULL;
2019
2020     if (!PyList_Check(list)) {
2021         PyErr_SetString(PyExc_TypeError, "first parameter must be a list");
2022         return NULL;
2023     }
2024
2025     fd = fdDup(fileno);
2026
2027     rc = rpmMergeHeaders (list, fd, matchTag);
2028     Fclose(fd);
2029
2030     if (rc) {
2031         return NULL;
2032     }
2033
2034     Py_INCREF(Py_None);
2035     return Py_None;
2036 }
2037
2038 /** \ingroup python
2039  */
2040 static PyObject * errorCB = NULL, * errorData = NULL;
2041
2042 /** \ingroup python
2043  */
2044 static void errorcb (void)
2045 {
2046     PyObject * result, * args = NULL;
2047
2048     if (errorData)
2049         args = Py_BuildValue("(O)", errorData);
2050
2051     result = PyEval_CallObject(errorCB, args);
2052     Py_XDECREF(args);
2053
2054     if (result == NULL) {
2055         PyErr_Print();
2056         PyErr_Clear();
2057     }
2058     Py_DECREF (result);
2059 }
2060
2061 /** \ingroup python
2062  */
2063 static PyObject * errorSetCallback (PyObject * self, PyObject * args) {
2064     if (errorCB != NULL) {
2065         Py_DECREF (errorCB);
2066         errorCB = NULL;
2067     }
2068
2069     if (errorData != NULL) {
2070         Py_DECREF (errorData);
2071         errorData = NULL;
2072     }
2073
2074     if (!PyArg_ParseTuple(args, "O|O", &errorCB, &errorData)) return NULL;
2075
2076     /* if we're getting a void*, set the error callback to this. */
2077     /* also, we can possibly decref any python callbacks we had  */
2078     /* and set them to NULL.                                     */
2079     if (PyCObject_Check (errorCB)) {
2080         rpmErrorSetCallback (PyCObject_AsVoidPtr(errorCB));
2081
2082         Py_XDECREF (errorCB);
2083         Py_XDECREF (errorData);
2084
2085         errorCB   = NULL;
2086         errorData = NULL;
2087         
2088         Py_INCREF(Py_None);
2089         return Py_None;
2090     }
2091     
2092     if (!PyCallable_Check (errorCB)) {
2093         PyErr_SetString(PyExc_TypeError, "parameter must be callable");
2094         return NULL;
2095     }
2096
2097     Py_INCREF (errorCB);
2098     Py_XINCREF (errorData);
2099
2100     return PyCObject_FromVoidPtr(rpmErrorSetCallback (errorcb), NULL);
2101 }
2102
2103 /** \ingroup python
2104  */
2105 static PyObject * errorString (PyObject * self, PyObject * args) {
2106     return PyString_FromString(rpmErrorString ());
2107 }
2108
2109 /** \ingroup python
2110  */
2111 static PyObject * versionCompare (PyObject * self, PyObject * args) {
2112     hdrObject * h1, * h2;
2113
2114     if (!PyArg_ParseTuple(args, "O!O!", &hdrType, &h1, &hdrType, &h2)) return NULL;
2115
2116     return Py_BuildValue("i", rpmVersionCompare(h1->h, h2->h));
2117 }
2118
2119 /**
2120  */
2121 static PyObject * labelCompare (PyObject * self, PyObject * args) {
2122     char *v1, *r1, *e1, *v2, *r2, *e2;
2123     int rc;
2124
2125     if (!PyArg_ParseTuple(args, "(zzz)(zzz)",
2126                           &e1, &v1, &r1,
2127                           &e2, &v2, &r2)) return NULL;
2128
2129     if (e1 && !e2)
2130         return Py_BuildValue("i", 1);
2131     else if (!e1 && e2)
2132         return Py_BuildValue("i", -1);
2133     else if (e1 && e2) {
2134         int ep1, ep2;
2135         ep1 = atoi (e1);
2136         ep2 = atoi (e2);
2137         if (ep1 < ep2)
2138             return Py_BuildValue("i", -1);
2139         else if (ep1 > ep2)
2140             return Py_BuildValue("i", 1);
2141     }
2142
2143     rc = rpmvercmp(v1, v2);
2144     if (rc)
2145         return Py_BuildValue("i", rc);
2146
2147     return Py_BuildValue("i", rpmvercmp(r1, r2));
2148 }
2149
2150 /** \ingroup python
2151  */
2152 static PyObject * checkSig (PyObject * self, PyObject * args) {
2153     char * filename;
2154     int flags;
2155     int rc = 255;
2156
2157     if (PyArg_ParseTuple(args, "si", &filename, &flags)) {
2158         const char *av[2];
2159         av[0] = filename;
2160         av[1] = NULL;
2161         rc = rpmCheckSig(flags, av);
2162     }
2163     return Py_BuildValue("i", rc);
2164 }
2165
2166 /* hack to get the current header that's in the transaction set */
2167 /** \ingroup python
2168  */
2169 static PyObject * getTsHeader (PyObject * self, PyObject * args) {
2170     hdrObject * h;
2171     
2172     if (transactionSetHeader) {
2173         h = (hdrObject *) PyObject_NEW(PyObject, &hdrType);
2174         h->h = headerLink(transactionSetHeader);
2175         h->sigs = NULL;
2176         h->fileList = h->linkList = h->md5list = NULL;
2177         h->uids = h->gids = h->mtimes = h->fileSizes = NULL;
2178         h->modes = h->rdevs = NULL;
2179         return (PyObject *) h;
2180     }
2181     Py_INCREF(Py_None);
2182     return (PyObject *) Py_None;
2183 }
2184
2185 /** \ingroup python
2186  */
2187 typedef struct FDlist_t FDlist;
2188
2189 /** \ingroup python
2190  */
2191 struct FDlist_t {
2192     FILE *f;
2193     FD_t fd;
2194     char *note;
2195     FDlist *next;
2196 } ;
2197
2198 /** \ingroup python
2199  */
2200 static FDlist *fdhead = NULL;
2201
2202 /** \ingroup python
2203  */
2204 static FDlist *fdtail = NULL;
2205
2206 /** \ingroup python
2207  */
2208 static int closeCallback(FILE * f) {
2209     FDlist *node, *last;
2210
2211     printf ("close callback on %p\n", f);
2212     
2213     node = fdhead;
2214     last = NULL;
2215     while (node) {
2216         if (node->f == f)
2217             break;
2218         last = node;
2219         node = node->next;
2220     }
2221     if (node) {
2222         if (last)
2223             last->next = node->next;
2224         else
2225             fdhead = node->next;
2226         printf ("closing %s %p\n", node->note, node->fd);
2227         free (node->note);
2228         node->fd = fdLink(node->fd, "closeCallback");
2229         Fclose (node->fd);
2230         while (node->fd)
2231             node->fd = fdFree(node->fd, "closeCallback");
2232         free (node);
2233     }
2234     return 0; 
2235 }
2236
2237 /** \ingroup python
2238  */
2239 static PyObject * doFopen(PyObject * self, PyObject * args) {
2240     char * path, * mode;
2241     FDlist *node;
2242     
2243     if (!PyArg_ParseTuple(args, "ss", &path, &mode))
2244         return NULL;
2245     
2246     node = malloc (sizeof(FDlist));
2247     
2248     node->fd = Fopen(path, mode);
2249     node->fd = fdLink(node->fd, "doFopen");
2250     node->note = strdup (path);
2251
2252     if (!node->fd) {
2253         PyErr_SetFromErrno(pyrpmError);
2254         free (node);
2255         return NULL;
2256     }
2257     
2258     if (Ferror(node->fd)) {
2259         const char *err = Fstrerror(node->fd);
2260         free(node);
2261         if (err) {
2262             PyErr_SetString(pyrpmError, err);
2263             return NULL;
2264         }
2265     }
2266     node->f = fdGetFp(node->fd);
2267     printf ("opening %s fd = %p f = %p\n", node->note, node->fd, node->f);
2268     if (!node->f) {
2269         PyErr_SetString(pyrpmError, "FD_t has no FILE*");
2270         free(node);
2271         return NULL;
2272     }
2273
2274     node->next = NULL;
2275     if (!fdhead) {
2276         fdhead = fdtail = node;
2277     } else if (fdtail) {
2278         fdtail->next = node;
2279     } else {
2280         fdhead = node;
2281     }
2282     fdtail = node;
2283     
2284     return PyFile_FromFile (node->f, path, mode, closeCallback);
2285 }
2286
2287 /** \ingroup python
2288  */
2289 static PyMethodDef rpmModuleMethods[] = {
2290     { "TransactionSet", (PyCFunction) rpmtransCreate, METH_VARARGS, NULL },
2291     { "addMacro", (PyCFunction) doAddMacro, METH_VARARGS, NULL },
2292     { "delMacro", (PyCFunction) doDelMacro, METH_VARARGS, NULL },
2293     { "archscore", (PyCFunction) archScore, METH_VARARGS, NULL },
2294     { "findUpgradeSet", (PyCFunction) findUpgradeSet, METH_VARARGS, NULL },
2295     { "headerFromPackage", (PyCFunction) rpmHeaderFromPackage, METH_VARARGS, NULL },
2296     { "headerLoad", (PyCFunction) hdrLoad, METH_VARARGS, NULL },
2297     { "initdb", (PyCFunction) rpmInitDB, METH_VARARGS, NULL },
2298     { "opendb", (PyCFunction) rpmOpenDB, METH_VARARGS, NULL },
2299     { "rebuilddb", (PyCFunction) rebuildDB, METH_VARARGS, NULL },
2300     { "mergeHeaderListFromFD", (PyCFunction) rpmMergeHeadersFromFD, METH_VARARGS, NULL },
2301     { "readHeaderListFromFD", (PyCFunction) rpmHeaderFromFD, METH_VARARGS, NULL },
2302     { "readHeaderListFromFile", (PyCFunction) rpmHeaderFromFile, METH_VARARGS, NULL },
2303     { "errorSetCallback", (PyCFunction) errorSetCallback, METH_VARARGS, NULL },
2304     { "errorString", (PyCFunction) errorString, METH_VARARGS, NULL },
2305     { "versionCompare", (PyCFunction) versionCompare, METH_VARARGS, NULL },
2306     { "labelCompare", (PyCFunction) labelCompare, METH_VARARGS, NULL },
2307     { "checksig", (PyCFunction) checkSig, METH_VARARGS, NULL },
2308     { "getTransactionCallbackHeader", (PyCFunction) getTsHeader, METH_VARARGS, NULL },
2309 /*      { "Fopen", (PyCFunction) doFopen, METH_VARARGS, NULL }, */
2310     { NULL }
2311 } ;
2312
2313 /** \ingroup python
2314  */
2315 void initrpm(void) {
2316     PyObject * m, * d, *o, * tag = NULL, * dict;
2317     int i;
2318     const struct headerSprintfExtension * extensions = rpmHeaderFormats;
2319     struct headerSprintfExtension * ext;
2320
2321 /*      _rpmio_debug = -1; */
2322     rpmReadConfigFiles(NULL, NULL);
2323
2324     m = Py_InitModule("rpm", rpmModuleMethods);
2325     d = PyModule_GetDict(m);
2326
2327     pyrpmError = PyString_FromString("rpm.error");
2328     PyDict_SetItemString(d, "error", pyrpmError);
2329     Py_DECREF(pyrpmError);
2330
2331     dict = PyDict_New();
2332
2333     for (i = 0; i < rpmTagTableSize; i++) {
2334         tag = PyInt_FromLong(rpmTagTable[i].val);
2335         PyDict_SetItemString(d, (char *) rpmTagTable[i].name, tag);
2336         Py_DECREF(tag);
2337         PyDict_SetItem(dict, tag, o=PyString_FromString(rpmTagTable[i].name + 7));
2338         Py_DECREF(o);
2339     }
2340
2341     while (extensions->name) {
2342         if (extensions->type == HEADER_EXT_TAG) {
2343             (const struct headerSprintfExtension *) ext = extensions;
2344             PyDict_SetItemString(d, extensions->name, o=PyCObject_FromVoidPtr(ext, NULL));
2345             Py_DECREF(o);
2346             PyDict_SetItem(dict, tag, o=PyString_FromString(ext->name + 7));
2347             Py_DECREF(o);    
2348         }
2349         extensions++;
2350     }
2351
2352     PyDict_SetItemString(d, "tagnames", dict);
2353     Py_DECREF(dict);
2354
2355
2356 #define REGISTER_ENUM(val) \
2357     PyDict_SetItemString(d, #val, o=PyInt_FromLong( val )); \
2358     Py_DECREF(o);
2359     
2360     REGISTER_ENUM(RPMFILE_STATE_NORMAL);
2361     REGISTER_ENUM(RPMFILE_STATE_REPLACED);
2362     REGISTER_ENUM(RPMFILE_STATE_NOTINSTALLED);
2363     REGISTER_ENUM(RPMFILE_STATE_NETSHARED);
2364
2365     REGISTER_ENUM(RPMFILE_CONFIG);
2366     REGISTER_ENUM(RPMFILE_DOC);
2367     REGISTER_ENUM(RPMFILE_MISSINGOK);
2368     REGISTER_ENUM(RPMFILE_NOREPLACE);
2369     REGISTER_ENUM(RPMFILE_GHOST);
2370     REGISTER_ENUM(RPMFILE_LICENSE);
2371     REGISTER_ENUM(RPMFILE_README);
2372
2373     REGISTER_ENUM(RPMDEP_SENSE_REQUIRES);
2374     REGISTER_ENUM(RPMDEP_SENSE_CONFLICTS);
2375
2376     REGISTER_ENUM(RPMSENSE_SERIAL);
2377     REGISTER_ENUM(RPMSENSE_LESS);
2378     REGISTER_ENUM(RPMSENSE_GREATER);
2379     REGISTER_ENUM(RPMSENSE_EQUAL);
2380     REGISTER_ENUM(RPMSENSE_PREREQ);
2381     REGISTER_ENUM(RPMSENSE_INTERP);
2382     REGISTER_ENUM(RPMSENSE_SCRIPT_PRE);
2383     REGISTER_ENUM(RPMSENSE_SCRIPT_POST);
2384     REGISTER_ENUM(RPMSENSE_SCRIPT_PREUN);
2385     REGISTER_ENUM(RPMSENSE_SCRIPT_POSTUN);
2386     REGISTER_ENUM(RPMSENSE_SCRIPT_VERIFY);
2387     REGISTER_ENUM(RPMSENSE_FIND_REQUIRES);
2388     REGISTER_ENUM(RPMSENSE_FIND_PROVIDES);
2389     REGISTER_ENUM(RPMSENSE_TRIGGERIN);
2390     REGISTER_ENUM(RPMSENSE_TRIGGERUN);
2391     REGISTER_ENUM(RPMSENSE_TRIGGERPOSTUN);
2392     REGISTER_ENUM(RPMSENSE_MULTILIB);
2393     REGISTER_ENUM(RPMSENSE_SCRIPT_PREP);
2394     REGISTER_ENUM(RPMSENSE_SCRIPT_BUILD);
2395     REGISTER_ENUM(RPMSENSE_SCRIPT_INSTALL);
2396     REGISTER_ENUM(RPMSENSE_SCRIPT_CLEAN);
2397     REGISTER_ENUM(RPMSENSE_RPMLIB);
2398     REGISTER_ENUM(RPMSENSE_TRIGGERPREIN);
2399
2400     REGISTER_ENUM(RPMTRANS_FLAG_TEST);
2401     REGISTER_ENUM(RPMTRANS_FLAG_BUILD_PROBS);
2402     REGISTER_ENUM(RPMTRANS_FLAG_NOSCRIPTS);
2403     REGISTER_ENUM(RPMTRANS_FLAG_JUSTDB);
2404     REGISTER_ENUM(RPMTRANS_FLAG_NOTRIGGERS);
2405     REGISTER_ENUM(RPMTRANS_FLAG_NODOCS);
2406     REGISTER_ENUM(RPMTRANS_FLAG_ALLFILES);
2407     REGISTER_ENUM(RPMTRANS_FLAG_KEEPOBSOLETE);
2408     REGISTER_ENUM(RPMTRANS_FLAG_MULTILIB);
2409
2410     REGISTER_ENUM(RPMPROB_FILTER_IGNOREOS);
2411     REGISTER_ENUM(RPMPROB_FILTER_IGNOREARCH);
2412     REGISTER_ENUM(RPMPROB_FILTER_REPLACEPKG);
2413     REGISTER_ENUM(RPMPROB_FILTER_FORCERELOCATE);
2414     REGISTER_ENUM(RPMPROB_FILTER_REPLACENEWFILES);
2415     REGISTER_ENUM(RPMPROB_FILTER_REPLACEOLDFILES);
2416     REGISTER_ENUM(RPMPROB_FILTER_OLDPACKAGE);
2417     REGISTER_ENUM(RPMPROB_FILTER_DISKSPACE);
2418     REGISTER_ENUM(RPMPROB_FILTER_DISKNODES);
2419
2420     REGISTER_ENUM(RPMCALLBACK_INST_PROGRESS);
2421     REGISTER_ENUM(RPMCALLBACK_INST_START);
2422     REGISTER_ENUM(RPMCALLBACK_INST_OPEN_FILE);
2423     REGISTER_ENUM(RPMCALLBACK_INST_CLOSE_FILE);
2424     REGISTER_ENUM(RPMCALLBACK_TRANS_PROGRESS);
2425     REGISTER_ENUM(RPMCALLBACK_TRANS_START);
2426     REGISTER_ENUM(RPMCALLBACK_TRANS_STOP);
2427     REGISTER_ENUM(RPMCALLBACK_UNINST_PROGRESS);
2428     REGISTER_ENUM(RPMCALLBACK_UNINST_START);
2429     REGISTER_ENUM(RPMCALLBACK_UNINST_STOP);
2430
2431     REGISTER_ENUM(RPMPROB_BADARCH);
2432     REGISTER_ENUM(RPMPROB_BADOS);
2433     REGISTER_ENUM(RPMPROB_PKG_INSTALLED);
2434     REGISTER_ENUM(RPMPROB_BADRELOCATE);
2435     REGISTER_ENUM(RPMPROB_REQUIRES);
2436     REGISTER_ENUM(RPMPROB_CONFLICT);
2437     REGISTER_ENUM(RPMPROB_NEW_FILE_CONFLICT);
2438     REGISTER_ENUM(RPMPROB_FILE_CONFLICT);
2439     REGISTER_ENUM(RPMPROB_OLDPACKAGE);
2440     REGISTER_ENUM(RPMPROB_DISKSPACE);
2441     REGISTER_ENUM(RPMPROB_DISKNODES);
2442     REGISTER_ENUM(RPMPROB_BADPRETRANS);
2443
2444     REGISTER_ENUM(CHECKSIG_PGP);
2445     REGISTER_ENUM(CHECKSIG_GPG);
2446     REGISTER_ENUM(CHECKSIG_MD5);
2447 }
2448
2449 /*@}*/