update python bindings from anaconda.
authorjbj <devnull@localhost>
Tue, 24 Aug 1999 21:10:30 +0000 (21:10 +0000)
committerjbj <devnull@localhost>
Tue, 24 Aug 1999 21:10:30 +0000 (21:10 +0000)
CVS patchset: 3251
CVS date: 1999/08/24 21:10:30

CHANGES
python/hash.c [new file with mode: 0644]
python/hash.h [new file with mode: 0644]
python/rpmmodule.c
python/upgrade.c [new file with mode: 0644]
python/upgrade.h [new file with mode: 0644]
rpm.spec

diff --git a/CHANGES b/CHANGES
index 52c7ce9..0b60236 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -35,6 +35,7 @@
        - add LC_ALL/LC_MESSAGES to query locale search.
        - fix: segfault with "--sign" w/o supplying files (#4651).
        - add headerWrite return code and check for errors.
+       - update python bindings from anaconda.
 
 3.0.1 -> 3.0.2
        - eliminate armv4 entries from rpmrc (Andrew E. Mileski).
diff --git a/python/hash.c b/python/hash.c
new file mode 100644 (file)
index 0000000..c3fad33
--- /dev/null
@@ -0,0 +1,171 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "hash.h"
+
+#define CHUNK 4
+
+struct bucket {
+    char **data;
+    int allocated;
+    int firstFree; /* as in data[firstFree] */
+};
+
+struct hash_table {
+    int size;
+    int entries;
+    int totalData;
+    int overHead;
+    struct bucket *bucket;
+};
+
+struct hash_table *htNewTable(int size)
+{
+    struct hash_table *res;
+    int i = 0;
+
+    res = malloc(sizeof(struct hash_table));
+    res->bucket = malloc(sizeof(struct bucket) * size);
+    res->size = size;
+    res->totalData = 0;
+    res->entries = 0;
+    res->overHead = sizeof(struct bucket) * size + CHUNK * sizeof(char *);
+
+    while (i < size) {
+       res->bucket[i].data = malloc(CHUNK * sizeof(char *));
+       res->bucket[i].allocated = CHUNK;
+       res->bucket[i].firstFree = 0;
+       i++;
+    }
+    
+    return res;
+}
+
+void htFreeHashTable(struct hash_table *ht)
+{
+    struct bucket * b;
+
+    b = ht->bucket;
+    while (ht->size--) {
+       while (b->firstFree) {
+           b->firstFree--;
+           free(b->data[b->firstFree]);
+       }
+       
+       free(b->data);
+       b++;
+    }
+    free(ht->bucket);
+    free(ht);
+}
+
+void htHashStats(struct hash_table *t)
+{
+    int i = 0;
+    int empty = 0;
+
+    while (i < t->size) {
+       if (t->bucket[i].firstFree != 0) {
+           /*printf("Bucket %d used %d\n", i, t->bucket[i].firstFree);*/
+       } else {
+           empty++;
+       }
+       i++;
+    }
+
+    printf("Total Buckets : %d\n", t->size);
+    printf("Empty Buckets : %d\n", empty);
+    printf("Total Entries : %d\n", t->entries);
+    printf("Total Data    : %d\n", t->totalData);
+    printf("Total Overhead: %d\n", t->overHead);
+    printf("Avergage Depth: %f\n", (double)t->entries / (double)t->size);
+}
+
+static unsigned int htHashString(char *s)
+{
+    unsigned int res = 0;
+
+    while (*s)
+       res = ((res<<1) + (int)(*(s++)));
+
+    return res;
+}
+
+static char *in_table_aux(struct hash_table *t, int hash, char *s)
+{
+    int x;
+
+    x = 0;
+    while (x < t->bucket[hash].firstFree) {
+       if (! strcmp(t->bucket[hash].data[x], s)) {
+           return t->bucket[hash].data[x];
+       }
+       x++;
+    }
+    
+    return NULL;
+}
+
+char *htInTable(struct hash_table *t, char *s)
+{
+    int hash;
+
+    hash = htHashString(s) % t->size;
+    return in_table_aux(t, hash, s);
+}
+
+void htAddToTable(struct hash_table *t, char *s)
+{
+    int hash;
+
+    if (s == NULL)
+       return;
+    
+    hash = htHashString(s) % t->size;
+    if (in_table_aux(t, hash, s)) {
+       return;
+    }
+
+    if (t->bucket[hash].firstFree == t->bucket[hash].allocated) {
+       t->bucket[hash].allocated += CHUNK;
+       t->bucket[hash].data =
+           realloc(t->bucket[hash].data,
+                   t->bucket[hash].allocated * sizeof(char *));
+       /*printf("Bucket %d grew to %d\n", hash, t->bucket[hash].allocated);*/
+       t->overHead += sizeof(char *) * CHUNK;
+    }
+    /*printf("In bucket %d, item %d\n", hash, t->bucket[hash].firstFree);*/
+    t->bucket[hash].data[t->bucket[hash].firstFree++] = strdup(s);
+    t->totalData += strlen(s) + 1;
+    t->entries++;
+}
+
+int htNumEntries(struct hash_table *t) {
+    return t->entries;
+}
+
+void htIterStart(htIterator * iter) {
+    iter->bucket = 0;
+    iter->item = -1;
+}
+
+int htIterGetNext(struct hash_table * t, htIterator * iter, char ** s) {
+    iter->item++;
+    
+    while (iter->bucket < t->size) {
+       if (iter->item < t->bucket[iter->bucket].firstFree) {
+           *s = t->bucket[iter->bucket].data[iter->item];
+           return 1;
+       }
+
+       iter->item++;
+       if (iter->item >= t->bucket[iter->bucket].firstFree) {
+           iter->bucket++;
+           iter->item = 0;
+       }
+    }
+
+    return 0;
+}
diff --git a/python/hash.h b/python/hash.h
new file mode 100644 (file)
index 0000000..26b6387
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef H_HASH
+#define H_HASH
+
+struct hash_table;
+typedef struct hash_table * hashTable;
+
+struct ht_iterator {
+    int bucket;
+    int item;
+};
+
+typedef struct ht_iterator htIterator;
+
+struct hash_table *htNewTable(int size);
+void htFreeHashTable(struct hash_table *ht);
+char *htInTable(struct hash_table *t, char *s);
+void htAddToTable(struct hash_table *t, char *s);
+void htPrintHashStats(struct hash_table *t);
+int htNumEntries(struct hash_table *t);
+
+/* these use static storage */
+void htIterStart(htIterator * iter);
+int htIterGetNext(struct hash_table * t, htIterator * iter, char ** s);
+
+#endif
index f4e72aa..8cf30b6 100644 (file)
@@ -1,3 +1,4 @@
+#include <alloca.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -6,6 +7,7 @@
 
 #include "Python.h"
 #include "rpmlib.h"
+#include "upgrade.h"
 
 /* Forward types */
 
@@ -19,19 +21,29 @@ static void rpmdbDealloc(rpmdbObject * s);
 static PyObject * rpmdbGetAttr(rpmdbObject * s, char * name);
 static PyObject * rpmdbFirst(rpmdbObject * s, PyObject * args);
 static PyObject * rpmdbNext(rpmdbObject * s, PyObject * args);
+static PyObject * rpmdbByName(rpmdbObject * s, PyObject * args);
+static PyObject * rpmdbByProvides(rpmdbObject * s, PyObject * args);
+static PyObject * rpmdbByFile(rpmdbObject * s, PyObject * args);
 static int rpmdbLength(rpmdbObject * s);
 static hdrObject * rpmdbSubscript(rpmdbObject * s, PyObject * key);
 
 static void hdrDealloc(hdrObject * s);
 static PyObject * hdrGetAttr(hdrObject * s, char * name);
-static PyObject * hdrSubscript(hdrObject * s, int item);
+static PyObject * hdrSubscript(hdrObject * s, PyObject * item);
+static PyObject * hdrKeyList(hdrObject * s, PyObject * args);
+static PyObject * hdrUnload(hdrObject * s, PyObject * args);
 static PyObject * hdrVerifyFile(hdrObject * s, PyObject * args);
 
 void initrpm(void);
 static rpmdbObject * rpmOpenDB(PyObject * self, PyObject * args);
-static PyObject * archScore(PyObject * self, PyObject * args);
+static PyObject * hdrLoad(PyObject * self, PyObject * args);
 static PyObject * rpmHeaderFromPackage(PyObject * self, PyObject * args);
-static PyObject * rpmHeaderFromList(PyObject * self, PyObject * args);
+static PyObject * rpmHeaderFromFile(PyObject * self, PyObject * args);
+static PyObject * archScore(PyObject * self, PyObject * args);
+static PyObject * rpmHeaderFromFD(PyObject * self, PyObject * args);
+static PyObject * findUpgradeSet(PyObject * self, PyObject * args);
+static PyObject * errorSetCallback (PyObject * self, PyObject * args);
+static PyObject * errorString (PyObject * self, PyObject * args);
 
 static PyObject * rpmtransCreate(PyObject * self, PyObject * args);
 static PyObject * rpmtransAdd(rpmtransObject * s, PyObject * args);
@@ -46,11 +58,16 @@ static int rpmtransSetAttr(rpmtransObject * o, char * name,
 /* Types */
 
 static PyMethodDef rpmModuleMethods[] = {
-    { "opendb", (PyCFunction) rpmOpenDB, METH_VARARGS, NULL },
+    { "TransactionSet", (PyCFunction) rpmtransCreate, METH_VARARGS, NULL },
     { "archscore", (PyCFunction) archScore, METH_VARARGS, NULL },
+    { "findUpgradeSet", (PyCFunction) findUpgradeSet, METH_VARARGS, NULL },
     { "headerFromPackage", (PyCFunction) rpmHeaderFromPackage, METH_VARARGS, NULL },
-    { "readHeaderList", (PyCFunction) rpmHeaderFromList, METH_VARARGS, NULL },
-    { "TransactionSet", (PyCFunction) rpmtransCreate, METH_VARARGS, NULL },
+    { "headerLoad", (PyCFunction) hdrLoad, METH_VARARGS, NULL },
+    { "opendb", (PyCFunction) rpmOpenDB, METH_VARARGS, NULL },
+    { "readHeaderListFromFD", (PyCFunction) rpmHeaderFromFD, METH_VARARGS, NULL },    
+    { "readHeaderListFromFile", (PyCFunction) rpmHeaderFromFile, METH_VARARGS, NULL },
+    { "errorSetCallback", (PyCFunction) errorSetCallback, METH_VARARGS, NULL },
+    { "errorString", (PyCFunction) errorString, METH_VARARGS, NULL },
     { NULL }
 } ;
 
@@ -84,14 +101,10 @@ struct hdrObject_s {
 
 static PyObject * pyrpmError;
 
-static PySequenceMethods hdrAsSequence = {
-       0,                              /* length */
-       0,                              /* concat */
-       0,                              /* repeat */
-       (intargfunc) hdrSubscript,      /* item */
-       0,                              /* slice */
-       0,                              /* assign item */
-       0,                              /* assign slice */
+static PyMappingMethods hdrAsMapping = {
+       (inquiry) 0,                    /* mp_length */
+       (binaryfunc) hdrSubscript,      /* mp_subscript */
+       (objobjargproc)0,               /* mp_ass_subscript */
 };
 
 static PyTypeObject hdrType = {
@@ -107,8 +120,8 @@ static PyTypeObject hdrType = {
        0,                              /* tp_compare */
        0,                              /* tp_repr */
        0,                              /* tp_as_number */
-       &hdrAsSequence,                 /* tp_as_sequence */
-       0,                              /* tp_as_mapping */
+       0,                              /* tp_as_sequence */
+       &hdrAsMapping,                  /* tp_as_mapping */
 };
 
 static PyMappingMethods rpmdbAsMapping = {
@@ -152,8 +165,11 @@ static PyTypeObject rpmtransType = {
 };
 
 static struct PyMethodDef rpmdbMethods[] = {
-       {"firstkey",    (PyCFunction) rpmdbFirst,       1 },
-       {"nextkey",     (PyCFunction) rpmdbNext,        1 },
+       {"firstkey",        (PyCFunction) rpmdbFirst,   1 },
+       {"nextkey",         (PyCFunction) rpmdbNext,    1 },
+       {"findbyfile",      (PyCFunction) rpmdbByFile, 1 },
+       {"findbyname",      (PyCFunction) rpmdbByName, 1 },
+       {"findbyprovides",  (PyCFunction) rpmdbByProvides, 1 },
        {NULL,          NULL}           /* sentinel */
 };
 
@@ -166,14 +182,19 @@ static struct PyMethodDef rpmtransMethods[] = {
 };
 
 static struct PyMethodDef hdrMethods[] = {
+       {"keys",        (PyCFunction) hdrKeyList,       1 },
+       {"unload",      (PyCFunction) hdrUnload,        1 },
        {"verifyFile",  (PyCFunction) hdrVerifyFile,    1 },
        {NULL,          NULL}           /* sentinel */
 };
 
+/* External functions */
+int mdfile(const char *fn, unsigned char *digest);
+    
 /* Code */
 
 void initrpm(void) {
-    PyObject * m, * d, * tag;
+    PyObject * m, * d, * tag, * dict;
     int i;
 
     rpmReadConfigFiles(NULL, NULL);
@@ -184,11 +205,17 @@ void initrpm(void) {
     pyrpmError = PyString_FromString("rpm.error");
     PyDict_SetItemString(d, "error", pyrpmError);
 
+    dict = PyDict_New();
+
     for (i = 0; i < rpmTagTableSize; i++) {
        tag = PyInt_FromLong(rpmTagTable[i].val);
        PyDict_SetItemString(d, rpmTagTable[i].name, tag);
+
+        PyDict_SetItem(dict, tag, PyString_FromString(rpmTagTable[i].name + 7));
     }
 
+    PyDict_SetItemString(d, "tagnames", dict);
+
     PyDict_SetItemString(d, "RPMFILE_STATE_NORMAL", 
                         PyInt_FromLong(RPMFILE_STATE_NORMAL));
     PyDict_SetItemString(d, "RPMFILE_STATE_REPLACED", 
@@ -272,6 +299,113 @@ void initrpm(void) {
                         PyInt_FromLong(RPMCALLBACK_UNINST_STOP));
 }
 
+
+static int psGetArchScore(Header h) {
+    void * pkgArch;
+    int type, count;
+
+    if (!headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count) ||
+        type == RPM_INT8_TYPE)
+       return 150;
+    else
+        return rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch);
+}
+
+static int pkgCompareVer(void * first, void * second) {
+    struct packageInfo ** a = first;
+    struct packageInfo ** b = second;
+    int ret, score1, score2;
+
+    /* put packages w/o names at the end */
+    if (!(*a)->name) return 1;
+    if (!(*b)->name) return -1;
+
+    ret = strcasecmp((*a)->name, (*b)->name);
+    if (ret) return ret;
+    score1 = psGetArchScore((*a)->h);
+    if (!score1) return 1;
+    score2 = psGetArchScore((*b)->h);
+    if (!score2) return -1;
+    if (score1 < score2) return -1;
+    if (score1 > score2) return 1;
+    return rpmVersionCompare((*b)->h, (*a)->h);
+}
+
+static void pkgSort(struct pkgSet * psp) {
+    int i;
+    char *name;
+
+    qsort(psp->packages, psp->numPackages, sizeof(*psp->packages),
+        (void *) pkgCompareVer);
+
+    name = psp->packages[0]->name;
+    if (!name) {
+       psp->numPackages = 0;
+       return;
+    }
+    for (i = 1; i < psp->numPackages; i++) {
+       if (!psp->packages[i]->name) break;
+       if (!strcmp(psp->packages[i]->name, name))
+          psp->packages[i]->name = NULL;
+       else
+          name = psp->packages[i]->name;
+    }
+
+    qsort(psp->packages, psp->numPackages, sizeof(*psp->packages),
+        (void *) pkgCompareVer);
+
+    for (i = 0; i < psp->numPackages; i++)
+       if (!psp->packages[i]->name) break;
+    psp->numPackages = i;
+}
+
+static PyObject * findUpgradeSet(PyObject * self, PyObject * args) {
+    PyObject * hdrList, * result;
+    char * root = "/";
+    int i;
+    struct pkgSet list;
+    hdrObject * hdr;
+
+    if (!PyArg_ParseTuple(args, "O|s", &hdrList, &root)) return NULL;
+
+    if (!PyList_Check(hdrList)) {
+       PyErr_SetString(PyExc_TypeError, "list of headers expected");
+       return NULL;
+    }
+
+    list.numPackages = PyList_Size(hdrList);
+    list.packages = alloca(sizeof(list.packages) * list.numPackages);
+    for (i = 0; i < list.numPackages; i++) {
+       hdr = (hdrObject *) PyList_GetItem(hdrList, i);
+       if (hdr->ob_type != &hdrType) {
+           PyErr_SetString(PyExc_TypeError, "list of headers expected");
+           return NULL;
+       }
+       list.packages[i] = alloca(sizeof(struct packageInfo));
+       list.packages[i]->h = hdr->h;
+       list.packages[i]->selected = 0;
+       list.packages[i]->data = hdr;
+
+       headerGetEntry(hdr->h, RPMTAG_NAME, NULL, 
+                     (void **) &list.packages[i]->name, NULL);
+    }
+
+    pkgSort (&list);
+    
+    if (ugFindUpgradePackages(&list, root)) {
+       PyErr_SetString(pyrpmError, "error during upgrade check");
+       return NULL;
+    }
+
+    result = PyList_New(0);
+    for (i = 0; i < list.numPackages; i++) {
+       if (list.packages[i]->selected)
+           PyList_Append(result, list.packages[i]->data);
+    }
+
+    return result;
+}
+
 static rpmdbObject * rpmOpenDB(PyObject * self, PyObject * args) {
     rpmdbObject * o;
     char * root = "";
@@ -290,24 +424,20 @@ static rpmdbObject * rpmOpenDB(PyObject * self, PyObject * args) {
     return o;
 }
 
-static PyObject * rpmHeaderFromList(PyObject * self, PyObject * args) {
-    char * filespec;
-    FD_t fd;
-    Header header;
+static PyObject * rpmReadHeaders (FD_t fd) {
     PyObject * list;
+    Header header;
     hdrObject * h;
 
-    if (!PyArg_ParseTuple(args, "s", &filespec)) return NULL;
-    fd = fdOpen(filespec, O_RDONLY, 0);
-
     if (!fd) {
        PyErr_SetFromErrno(pyrpmError);
        return NULL;
     }
 
     list = PyList_New(0);
-
+    Py_BEGIN_ALLOW_THREADS
     header = headerRead(fd, HEADER_MAGIC_YES);
+    Py_END_ALLOW_THREADS
     while (header) {
        h = (hdrObject *) PyObject_NEW(PyObject, &hdrType);
        h->h = header;
@@ -322,16 +452,122 @@ static PyObject * rpmHeaderFromList(PyObject * self, PyObject * args) {
 
        Py_DECREF(h);
 
+       Py_BEGIN_ALLOW_THREADS
        header = headerRead(fd, HEADER_MAGIC_YES);
+       Py_END_ALLOW_THREADS
     }
 
+    return list;
+}
+
+static PyObject * rpmHeaderFromFD(PyObject * self, PyObject * args) {
+    FD_t fd;
+    int fileno;
+    PyObject * list;
+    
+    if (!PyArg_ParseTuple(args, "i", &fileno)) return NULL;
+    fd = fdDup(fileno);
+    list = rpmReadHeaders (fd);
     fdClose(fd);
 
     return list;
 }
 
+
+static PyObject * hdrLoad(PyObject * self, PyObject * args) {
+    char * obj;
+    Header hdr;
+    hdrObject * h;
+    int len;
+
+    if (!PyArg_ParseTuple(args, "s#", &obj, &len)) return NULL;
+
+    hdr = headerLoad(obj);
+    if (!hdr) {
+       PyErr_SetString(pyrpmError, "bad header");
+       return NULL;
+    }
+
+    h = (hdrObject *) PyObject_NEW(PyObject, &hdrType);
+    h->h = hdr;
+    h->fileList = h->linkList = h->md5list = NULL;
+    h->uids = h->gids = h->mtimes = h->fileSizes = NULL;
+    h->modes = h->rdevs = NULL;
+    
+    return (PyObject *) h;
+}
+
+static PyObject * rpmHeaderFromFile(PyObject * self, PyObject * args) {
+    char * filespec;
+    FD_t fd;
+    PyObject * list;
+
+    if (!PyArg_ParseTuple(args, "s", &filespec)) return NULL;
+    fd = fdOpen(filespec, O_RDONLY, 0);
+
+    if (!fd) {
+       PyErr_SetFromErrno(pyrpmError);
+       return NULL;
+    }
+
+    list = rpmReadHeaders (fd);
+    fdClose(fd);
+    
+    return list;
+}
+
+static PyObject * errorCB = NULL, * errorData = NULL;
+
+static void errorcb (void)
+{
+    PyObject * result, * args = NULL;
+
+    if (errorData) 
+       args = Py_BuildValue("(O)", errorData);
+
+    result = PyEval_CallObject(errorCB, args);
+    Py_XDECREF(args);
+
+    if (result == NULL) {
+       PyErr_Print();
+       PyErr_Clear();
+    }
+    Py_DECREF (result);
+}
+
+static PyObject * errorSetCallback (PyObject * self, PyObject * args) {
+    if (errorCB != NULL) {
+       Py_DECREF (errorCB);
+       errorCB = NULL;
+    }
+
+    if (errorData != NULL) {
+       Py_DECREF (errorData);
+       errorData = NULL;
+    }
+
+    if (!PyArg_ParseTuple(args, "O|O", &errorCB, &errorData)) return NULL;
+    
+    if (!PyCallable_Check (errorCB)) {
+       PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+       return NULL;
+    }   
+
+    Py_INCREF (errorCB);
+    Py_XINCREF (errorData);
+    
+    rpmErrorSetCallback (errorcb);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject * errorString (PyObject * self, PyObject * args) {
+    return PyString_FromString(rpmErrorString ());
+}
+
 static PyObject * rpmHeaderFromPackage(PyObject * self, PyObject * args) {
-    PyObject * fileObj;
     hdrObject * h;
     Header header;
     int rc;
@@ -382,7 +618,7 @@ static void rpmdbDealloc(rpmdbObject * s) {
 static PyObject * rpmdbFirst(rpmdbObject * s, PyObject * args) {
     int first;
 
-    if (!PyArg_Parse(args, "")) return NULL;
+    if (!PyArg_ParseTuple (args, "")) return NULL;
 
     first = rpmdbFirstRecNum(s->db);
 
@@ -397,7 +633,7 @@ static PyObject * rpmdbFirst(rpmdbObject * s, PyObject * args) {
 static PyObject * rpmdbNext(rpmdbObject * s, PyObject * args) {
     int where;
 
-    if (!PyArg_Parse(args, "i", &where)) return NULL;
+    if (!PyArg_ParseTuple (args, "i", &where)) return NULL;
 
     where = rpmdbNextRecNum(s->db, where);
 
@@ -409,6 +645,60 @@ static PyObject * rpmdbNext(rpmdbObject * s, PyObject * args) {
     return Py_BuildValue("i", where);
 }
 
+static PyObject * handleDbResult(int rc, dbiIndexSet matches) {
+    PyObject * list;
+    int i;
+
+    if (rc == -1) {
+       PyErr_SetString(pyrpmError, "error reading from database");
+       return NULL;
+    }
+
+    list = PyList_New(0);
+
+    if (!rc) {
+       for (i = 0; i < matches.count; i++)
+           PyList_Append(list, PyInt_FromLong(matches.recs[i].recOffset));
+
+       dbiFreeIndexRecord(matches);
+    }
+
+    return list;
+}
+
+static PyObject * rpmdbByName(rpmdbObject * s, PyObject * args) {
+    char * str;
+    dbiIndexSet matches;
+    int rc;
+
+    if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
+
+    rc = rpmdbFindPackage(s->db, str, &matches);
+    return handleDbResult(rc, matches);
+}
+
+static PyObject * rpmdbByFile(rpmdbObject * s, PyObject * args) {
+    char * str;
+    dbiIndexSet matches;
+    int rc;
+
+    if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
+
+    rc = rpmdbFindByFile(s->db, str, &matches);
+    return handleDbResult(rc, matches);
+}
+
+static PyObject * rpmdbByProvides(rpmdbObject * s, PyObject * args) {
+    char * str;
+    dbiIndexSet matches;
+    int rc;
+
+    if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
+
+    rc = rpmdbFindByProvides(s->db, str, &matches);
+    return handleDbResult(rc, matches);
+}
+
 static int rpmdbLength(rpmdbObject * s) {
     int first;
     int count = 0;
@@ -463,13 +753,27 @@ static PyObject * hdrGetAttr(hdrObject * s, char * name) {
     return Py_FindMethod(hdrMethods, (PyObject * ) s, name);
 }
 
-static PyObject * hdrSubscript(hdrObject * s, int tag) {
-    int type, count;
+static PyObject * hdrSubscript(hdrObject * s, PyObject * item) {
+    int type, count, i, tag = -1;
     void * data;
     PyObject * o, * metao;
-    int i;
     char ** stringArray;
     int forceArray = 0;
+    char * str;
+
+    if (PyInt_Check(item)) {
+       tag = PyInt_AsLong(item);
+    } else if (PyString_Check(item)) {
+       str = PyString_AsString(item);
+       for (i = 0; i < rpmTagTableSize; i++)
+           if (!strcasecmp(rpmTagTable[i].name + 7, str)) break;
+       if (i < rpmTagTableSize) tag = rpmTagTable[i].val;
+    }
+
+    if (tag == -1) {
+       PyErr_SetString(PyExc_KeyError, "unknown header tag");
+       return NULL;
+    }
 
     if (!headerGetEntry(s->h, tag, &type, &data, &count)) {
        Py_INCREF(Py_None);
@@ -577,6 +881,50 @@ static PyObject * hdrSubscript(hdrObject * s, int tag) {
     return o;
 }
 
+static PyObject * hdrKeyList(hdrObject * s, PyObject * args) {
+    PyObject * list;
+    HeaderIterator iter;
+    int tag, type;
+
+    if (!PyArg_ParseTuple(args, "")) return NULL;
+
+    list = PyList_New(0);
+
+    iter = headerInitIterator(s->h);
+    while (headerNextIterator(iter, &tag, &type, NULL, NULL)) {
+        if (tag == HEADER_I18NTABLE) continue;
+
+       switch (type) {
+         case RPM_BIN_TYPE:
+         case RPM_INT32_TYPE:
+         case RPM_CHAR_TYPE:
+         case RPM_INT8_TYPE:
+         case RPM_INT16_TYPE:
+         case RPM_STRING_ARRAY_TYPE:
+         case RPM_STRING_TYPE:
+           PyList_Append(list, PyInt_FromLong(tag));
+       }
+    }
+
+    headerFreeIterator(iter);
+
+    return list;
+}
+
+static PyObject * hdrUnload(hdrObject * s, PyObject * args) {
+    char * buf;
+    int len;
+    PyObject * rc;
+
+    len = headerSizeof(s->h, 0);
+    buf = headerUnload(s->h);
+    
+    rc = PyString_FromStringAndSize(buf, len);
+    free(buf);
+
+    return rc;
+}
+
 /* Returns a list of these tuple for each part which failed:
 
        (attr_name, correctValue, currentValue)
@@ -709,7 +1057,7 @@ static PyObject * hdrVerifyFile(hdrObject * s, PyObject * args) {
        PyTuple_SetItem(tuple, 0, attrName);
        sprintf(buf, "0x%-4x", s->rdevs[fileNumber]);
        PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
-       sprintf(buf, "0x%-4x", sb.st_rdev);
+       sprintf(buf, "0x%-4x", (unsigned int) sb.st_rdev);
        PyTuple_SetItem(tuple, 2, PyString_FromString(buf));
        PyList_Append(list, tuple);
     }
@@ -780,8 +1128,9 @@ static PyObject * rpmtransCreate(PyObject * self, PyObject * args) {
 
     o = (void *) PyObject_NEW(rpmtransObject, &rpmtransType);
 
-    Py_INCREF(db);
+    Py_XINCREF(db);
     o->dbo = db;
+    o->scriptFd = NULL;
     o->ts = rpmtransCreateSet(db ? db->db : NULL, rootPath);
     o->keyList = PyList_New(0);
 
@@ -792,8 +1141,12 @@ static void rpmtransDealloc(PyObject * o) {
     rpmtransObject * trans = (void *) o;
 
     rpmtransFree(trans->ts);
-    if (trans->dbo) Py_DECREF(trans->dbo);
+    if (trans->dbo) {
+       Py_DECREF(trans->dbo);
+    }
     if (trans->scriptFd) fdClose(trans->scriptFd);
+    /* this will free the keyList, and decrement the ref count of all
+       the items on the list as well :-) */
     Py_DECREF(trans->keyList);
 }
 
@@ -826,23 +1179,26 @@ static PyObject * rpmtransAdd(rpmtransObject * s, PyObject * args) {
     hdrObject * h;
     PyObject * key;
     char * how = NULL;
+    int isUpgrade = 0;
 
-    if (!PyArg_ParseTuple(args, "OO|s", &h, &key, &s)) return NULL;
+    if (!PyArg_ParseTuple(args, "OO|s", &h, &key, &how)) return NULL;
     if (h->ob_type != &hdrType) {
        PyErr_SetString(PyExc_TypeError, "bad type for header argument");
        return NULL;
     }
 
-    if (how && strcmp(how, "a") && strcmp(how, "u")) {
-       PyErr_SetString(PyExc_TypeError, "how argument must be \"u\" or \"a\"");
+    if (how && strcmp(how, "a") && strcmp(how, "u") && strcmp(how, "i")) {
+       PyErr_SetString(PyExc_TypeError, "how argument must be \"u\", \"a\", or \"i\"");
        return NULL;
-    }
+    } else if (how && !strcmp(how, "u"))
+       isUpgrade = 1;
 
-    if (how && strcmp(how, "a"))
+    if (how && !strcmp(how, "a"))
        rpmtransAvailablePackage(s->ts, h->h, key);
     else
-       rpmtransAddPackage(s->ts, h->h, NULL, key, how ? 1 : 0, NULL);
+       rpmtransAddPackage(s->ts, h->h, NULL, key, isUpgrade, NULL);
 
+    /* This should increment the usage count for me */
     if (key) PyList_Append(s->keyList, key);
 
     Py_INCREF(Py_None);
@@ -904,7 +1260,7 @@ static void * tsCallback(const Header h, const rpmCallbackType what,
     struct tsCallbackType * cbInfo = data;
     PyObject * args, * result;
     int fd;
-    FD_t fdt;
+    static FD_t fdt;
 
     if (cbInfo->pythonError) return NULL;
 
@@ -925,11 +1281,14 @@ static void * tsCallback(const Header h, const rpmCallbackType what,
            return NULL;
        }
        fdt = fdDup(fd);
-       close(fd);
        Py_DECREF(result);
        return fdt;
     }
 
+    if (what == RPMCALLBACK_INST_CLOSE_FILE) {
+       fdClose (fdt);
+    }
+
     Py_DECREF(result);
 
     return NULL;
diff --git a/python/upgrade.c b/python/upgrade.c
new file mode 100644 (file)
index 0000000..1bf863c
--- /dev/null
@@ -0,0 +1,485 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <rpm/rpmlib.h>
+#include <rpm/header.h>
+#include <string.h>
+
+#include "hash.h"
+#include "upgrade.h"
+
+#define MAXPKGS 1024
+
+#define USEDEBUG 0
+
+#define DEBUG(x) {   \
+     if (USEDEBUG)   \
+         printf x; \
+     }
+
+#if 0
+static void printMemStats(char *mess)
+{
+    char buf[1024];
+    printf("%s\n", mess);
+    sprintf(buf, "cat /proc/%d/status | grep VmSize", getpid());
+    system(buf);
+}
+#endif
+
+int pkgCompare(void * first, void * second) {
+    struct packageInfo ** a = first;
+    struct packageInfo ** b = second;
+
+    /* put packages w/o names at the end */
+    if (!(*a)->name) return 1;
+    if (!(*b)->name) return -1;
+
+    return strcasecmp((*a)->name, (*b)->name);
+}
+
+
+static void compareFileList(int availFileCount, char **availFiles,
+                           int installedFileCount, char **installedFiles,
+                           struct hash_table *ht)
+{
+    int installedX, availX, rc;
+    
+    availX = 0;
+    installedX = 0;
+    while (installedX < installedFileCount) {
+       if (availX == availFileCount) {
+           /* All the rest have moved */
+           DEBUG(("=> %s\n", installedFiles[installedX]));
+           if (strncmp(installedFiles[installedX], "/etc/rc.d/", 10))
+               htAddToTable(ht, installedFiles[installedX]);
+           installedX++;
+       } else {
+           rc = strcmp(availFiles[availX], installedFiles[installedX]);
+           if (rc > 0) {
+               /* Avail > Installed -- file has moved */
+               DEBUG (("=> %s\n", installedFiles[installedX]));
+               if (strncmp(installedFiles[installedX], "/etc/rc.d/", 10))
+                   htAddToTable(ht, installedFiles[installedX]);
+               installedX++;
+           } else if (rc < 0) {
+               /* Avail < Installed -- avail has some new files */
+               availX++;
+           } else {
+               /* Files are equal -- file not moved */
+               availX++;
+               installedX++;
+           }
+       }
+    }
+}
+
+static void addLostFiles(rpmdb db, struct pkgSet *psp, struct hash_table *ht)
+{
+    int num;
+    Header h;
+    char *name;
+    struct packageInfo **pack;
+    struct packageInfo key;
+    struct packageInfo *keyaddr = &key;
+    char **installedFiles;
+    int installedFileCount;
+
+    num = rpmdbFirstRecNum(db);
+    while (num) {
+       h = rpmdbGetRecord(db, num);
+       headerGetEntry(h, RPMTAG_NAME, NULL, (void **) &name, NULL);
+       if (name && !strcmp(name, "metroess")) {
+           /* metro was removed from 5.1, but leave it if it's already
+              installed */
+           headerFree(h);
+           num = rpmdbNextRecNum(db, num);
+           continue;
+       }
+       key.name = name;
+       
+       pack = bsearch(&keyaddr, psp->packages, psp->numPackages,
+                      sizeof(*psp->packages), (void *)pkgCompare);
+       if (!pack) {
+           if (headerGetEntry(h, RPMTAG_FILENAMES, NULL,
+                         (void **) &installedFiles, &installedFileCount)) {
+               compareFileList(0, NULL, installedFileCount,
+                               installedFiles, ht);
+               free(installedFiles);
+           }
+       }
+       
+       headerFree(h);
+       num = rpmdbNextRecNum(db, num);
+    }
+}
+
+static int findPackagesWithObsoletes(rpmdb db, struct pkgSet *psp)
+{
+    dbiIndexSet matches;
+    int rc, count, obsoletesCount;
+    struct packageInfo **pip;
+    char **obsoletes;
+
+    count = psp->numPackages;
+    pip = psp->packages;
+    while (count--) {
+       if ((*pip)->selected) {
+           pip++;
+           continue;
+       }
+
+       if (headerGetEntry((*pip)->h, RPMTAG_OBSOLETES, NULL,
+                      (void **) &obsoletes, &obsoletesCount)) {
+           while (obsoletesCount--) {
+               rc = rpmdbFindPackage(db, obsoletes[obsoletesCount], &matches);
+               if (!rc) {
+                   if (matches.count) {
+                       (*pip)->selected = 1;
+                       dbiFreeIndexRecord(matches);
+                       break;
+                   }
+
+                   dbiFreeIndexRecord(matches);
+               }
+           }
+
+           free(obsoletes);
+       }
+
+       pip++;
+    }
+
+    return 0;
+}
+
+static void errorFunction(void)
+{
+}
+
+static int findUpgradePackages(rpmdb db, struct pkgSet *psp,
+                              struct hash_table *ht)
+{
+    int skipThis;
+    Header h, installedHeader;
+    char *name, *version, *release;
+    dbiIndexSet matches;
+    int rc, i, count;
+    char **installedFiles, **availFiles;
+    int installedFileCount, availFileCount;
+    struct packageInfo **pip;
+
+    count = psp->numPackages;
+    pip = psp->packages;
+    while (count--) {
+       h = (*pip)->h;
+       name = version = release = NULL;
+       headerGetEntry(h, RPMTAG_NAME, NULL, (void **) &name, NULL);
+       headerGetEntry(h, RPMTAG_VERSION, NULL, (void **) &version, NULL);
+       headerGetEntry(h, RPMTAG_RELEASE, NULL, (void **) &release, NULL);
+       if (! (name && version && release)) {
+           /* bum header */
+           /*logMessage("Failed with bad header");*/
+           return(-1);
+       }
+       
+       DEBUG (("Avail: %s-%s-%s\n", name, version, release));
+       rc = rpmdbFindPackage(db, name, &matches);
+
+       if (rc == 0) {
+           skipThis = 0;
+           rpmErrorSetCallback(errorFunction);
+           for (i = 0; i < matches.count; i++) {
+               installedHeader =
+                   rpmdbGetRecord(db, matches.recs[i].recOffset);
+               if (rpmVersionCompare(installedHeader, h) >= 0) {
+                   /* already have a newer version installed */
+                   DEBUG (("Already have newer version\n"))
+                   skipThis = 1;
+                   headerFree(installedHeader);
+                   break;
+               }
+               headerFree(installedHeader);
+           }
+           rpmErrorSetCallback(NULL);
+           if (! skipThis) {
+               DEBUG (("No newer version installed\n"))
+           }
+       } else {
+           skipThis = 1;
+           DEBUG (("Not installed\n"))
+       }
+       
+       if (skipThis) {
+           DEBUG (("DO NOT INSTALL\n"))
+       } else {
+           DEBUG (("UPGRADE\n"))
+           (*pip)->selected = 1;
+
+           if (!headerGetEntry(h, RPMTAG_FILENAMES, NULL,
+                         (void **) &availFiles, &availFileCount)) {
+               availFiles = NULL;
+               availFileCount = 0;
+           }
+
+           for (i = 0; i < matches.count; i++) {
+               /* Compare the file lists */
+               installedHeader =
+                   rpmdbGetRecord(db, matches.recs[i].recOffset);
+               if (!headerGetEntry(installedHeader, RPMTAG_FILENAMES, NULL,
+                             (void **) &installedFiles,
+                             &installedFileCount)) {
+                   installedFiles = NULL;
+                   installedFileCount = 0;
+               }
+
+               compareFileList(availFileCount, availFiles,
+                               installedFileCount, installedFiles, ht);
+
+               if (installedFiles) {
+                   free(installedFiles);
+               }
+               headerFree(installedHeader);
+           }
+
+           if (availFiles) {
+               free(availFiles);
+           }
+       }
+
+       if (rc == 0) {
+           dbiFreeIndexRecord(matches);
+       }
+
+       DEBUG (("\n\n"))
+
+       pip++;
+    }
+
+    return 0;
+}
+
+static int removeMovedFilesAlreadyHandled(struct pkgSet *psp,
+                                         struct hash_table *ht)
+{
+    char *name;
+    int i, count;
+    Header h;
+    char **availFiles;
+    int availFileCount;
+    char *file;
+    struct packageInfo **pip;
+
+    count = psp->numPackages;
+    pip = psp->packages;
+    while (count--) {
+       h = (*pip)->h;
+       if ((*pip)->selected) {
+           name = NULL;
+           headerGetEntry(h, RPMTAG_NAME, NULL, (void **) &name, NULL);
+
+           if (!headerGetEntry(h, RPMTAG_FILENAMES, NULL,
+                         (void **) &availFiles, &availFileCount)) {
+               availFiles = NULL;
+               availFileCount = 0;
+           }
+
+           for (i = 0; i < availFileCount; i++) {
+               if ((file = htInTable(ht, availFiles[i]))) {
+                   *file = '\0';
+                   DEBUG (("File already in %s: %s\n", name, availFiles[i]))
+                   break;
+               }
+           }
+           if (availFiles) {
+               free(availFiles);
+           }
+       }
+
+       pip++;
+    }
+
+    return 0;
+}
+
+static int findPackagesWithRelocatedFiles(struct pkgSet *psp,
+                                         struct hash_table *ht)
+{
+    char *name;
+    int i, count;
+    Header h;
+    char **availFiles;
+    int availFileCount;
+    char *file;
+    struct packageInfo **pip;
+
+    count = psp->numPackages;
+    pip = psp->packages;
+    while (count--) {
+       h = (*pip)->h;
+       if (! (*pip)->selected) {
+           name = NULL;
+           headerGetEntry(h, RPMTAG_NAME, NULL, (void **) &name, NULL);
+
+           availFiles = NULL;
+           availFileCount = 0;
+           if (headerGetEntry(h, RPMTAG_FILENAMES, NULL,
+                        (void **) &availFiles, &availFileCount)) {
+               for (i = 0; i < availFileCount; i++) {
+                   if ((file = htInTable(ht, availFiles[i]))) {
+                       *file = '\0';
+                       DEBUG (("Found file in %s: %s\n", name,
+                               availFiles[i]))
+                       (*pip)->selected = 1;
+                       break;
+                   }
+               }
+               free(availFiles);
+           }
+       }
+
+       pip++;
+    }
+
+    return 0;
+}
+
+/*
+static void printCount(struct pkgSet *psp)
+{
+    int i, upgradeCount;
+    struct packageInfo *pip;
+    
+    upgradeCount = 0;
+    pip = psp->packages;
+    i = psp->numPackages;
+    while (i--) {
+       if (pip->selected) {
+           upgradeCount++;
+       }
+       pip++;
+    }
+    logMessage("marked %d packages for upgrade", upgradeCount);
+}
+*/
+
+static int unmarkPackagesAlreadyInstalled(rpmdb db, struct pkgSet *psp)
+{
+    dbiIndexSet matches;
+    Header h, installedHeader;
+    char *name, *version, *release;
+    struct packageInfo **pip;
+    int count, rc, i;
+
+    count = psp->numPackages;
+    pip = psp->packages;
+    while (count--) {
+       if ((*pip)->selected) {
+           h = (*pip)->h;
+           /* If this package is already installed, don't bother */
+           name = version = release = NULL;
+           headerGetEntry(h, RPMTAG_NAME, NULL, (void **) &name, NULL);
+           headerGetEntry(h, RPMTAG_VERSION, NULL, (void **) &version, NULL);
+           headerGetEntry(h, RPMTAG_RELEASE, NULL, (void **) &release, NULL);
+           if (! (name && version && release)) {
+               /* bum header */
+               /*logMessage("Failed with bad header");*/
+               return(-1);
+           }
+           rc = rpmdbFindPackage(db, name, &matches);
+           if (rc == 0) {
+               rpmErrorSetCallback(errorFunction);
+               for (i = 0; i < matches.count; i++) {
+                   installedHeader =
+                       rpmdbGetRecord(db, matches.recs[i].recOffset);
+                   if (rpmVersionCompare(installedHeader, h) >= 0) {
+                       /* already have a newer version installed */
+                       DEBUG (("Already have newer version\n"))
+                       (*pip)->selected = 0;
+                       headerFree(installedHeader);
+                       break;
+                   }
+                   headerFree(installedHeader);
+               }
+               rpmErrorSetCallback(NULL);
+               dbiFreeIndexRecord(matches);
+           }
+       }
+
+       pip++;
+    }
+
+    return 0;
+}
+           
+static void emptyErrorCallback(void) {
+}
+
+int ugFindUpgradePackages(struct pkgSet *psp, char *installRoot)
+{
+    rpmdb db;
+    struct hash_table *hashTable;
+    rpmErrorCallBackType old;    
+
+    /*logDebugMessage(("ugFindUpgradePackages() ..."));*/
+
+    rpmReadConfigFiles(NULL, NULL);
+
+    rpmSetVerbosity(RPMMESS_FATALERROR);
+    old = rpmErrorSetCallback(emptyErrorCallback);
+
+    if (rpmdbOpenForTraversal(installRoot, &db)) {
+       /*logMessage("failed opening %s/var/lib/rpm/packages.rpm",
+                    installRoot);*/
+       return(-1);
+    }
+
+    rpmErrorSetCallback(old);
+    rpmSetVerbosity(RPMMESS_NORMAL);
+    
+    hashTable = htNewTable(1103);
+
+    /* For all packages that are installed, if there is no package       */
+    /* available by that name, add the package's files to the hash table */
+    addLostFiles(db, psp, hashTable);
+    /*logDebugMessage(("added lost files"));
+    printCount(psp);*/
+    
+    /* Find packges that are new, and mark them in installThisPackage,  */
+    /* updating availPkgs with the count.  Also add files to the hash   */
+    /* table that do not exist in the new package - they may have moved */
+    if (findUpgradePackages(db, psp, hashTable)) {
+       rpmdbClose(db);
+       return(-1);
+    }
+    /*logDebugMessage(("found basic packages to upgrade"));
+    printCount(psp);
+    hash_stats(hashTable);*/
+
+    /* Remove any files that were added to the hash table that are in */
+    /* some other package marked for upgrade.                         */
+    removeMovedFilesAlreadyHandled(psp, hashTable);
+    /*logDebugMessage(("removed extra files which have moved"));
+    printCount(psp);*/
+
+    findPackagesWithRelocatedFiles(psp, hashTable);
+    /*logDebugMessage(("found packages with relocated files"));
+    printCount(psp);*/
+
+    findPackagesWithObsoletes(db, psp);
+    /*logDebugMessage(("found packages that obsolete installed packages"));
+    printCount(psp);*/
+    
+    unmarkPackagesAlreadyInstalled(db, psp);
+    /*logDebugMessage(("unmarked packages already installed"));
+    printCount(psp);*/
+    
+    htFreeHashTable(hashTable);
+    
+    /*printMemStats("Done");*/
+
+    rpmdbClose(db);
+
+    return 0;
+}
diff --git a/python/upgrade.h b/python/upgrade.h
new file mode 100644 (file)
index 0000000..d971b7f
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef H_UPGRADE
+#define H_UPGRADE
+
+struct packageInfo {
+    Header h;
+    char selected;
+    char * name;
+    void * data;
+} ;
+
+struct pkgSet {
+    struct packageInfo ** packages;
+    int numPackages;
+};
+
+int ugFindUpgradePackages(struct pkgSet *psp, char *installRoot);
+
+#endif
index b109e28..41d5ea8 100644 (file)
--- a/rpm.spec
+++ b/rpm.spec
@@ -2,7 +2,7 @@ Summary: The Red Hat package management system.
 Name: rpm
 %define version 3.0.3
 Version: %{version}
-Release: 0.17
+Release: 0.18
 Group: System Environment/Base
 Source: ftp://ftp.rpm.org/pub/rpm/dist/rpm-3.0.x/rpm-%{version}.tar.gz
 Copyright: GPL