- change solv format in an incompatible way, breaking compatibility
[platform/upstream/libsolv.git] / tools / repo_rpmdb.c
index 2697a64..3001f5e 100644 (file)
@@ -1,4 +1,11 @@
 /*
+ * Copyright (c) 2007, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
  * repo_rpmdb
  * 
  * convert rpm db to repo
  */
 
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <limits.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
-#include <db43/db.h>
+#include <rpm/db.h>
 
 #include "pool.h"
+#include "repo.h"
 #include "hash.h"
+#include "util.h"
 #include "repo_rpmdb.h"
 
+
 #define TAG_NAME               1000
 #define TAG_VERSION            1001
 #define TAG_RELEASE            1002
 #define TAG_SUMMARY            1004
 #define TAG_DESCRIPTION                1005
 #define TAG_BUILDTIME          1006
+#define TAG_SIZE                1009
 #define TAG_VENDOR             1011
+#define TAG_GROUP              1016
 #define TAG_ARCH               1022
+#define TAG_FILESIZES          1028
+#define TAG_FILEMODES          1030
+#define TAG_SOURCERPM          1044
 #define TAG_PROVIDENAME                1047
 #define TAG_REQUIREFLAGS       1048
 #define TAG_REQUIRENAME                1049
 #define TAG_REQUIREVERSION     1050
+#define TAG_NOSOURCE           1051
+#define TAG_NOPATCH            1052
 #define TAG_CONFLICTFLAGS      1053
 #define TAG_CONFLICTNAME       1054
 #define TAG_CONFLICTVERSION    1055
@@ -71,8 +90,25 @@ typedef struct rpmhead {
   unsigned char data[1];
 } RpmHead;
 
+static int
+headexists(RpmHead *h, int tag)
+{
+  unsigned int i;
+  unsigned char *d, taga[4];
+
+  d = h->dp - 16;
+  taga[0] = tag >> 24;
+  taga[1] = tag >> 16;
+  taga[2] = tag >> 8;
+  taga[3] = tag;
+  for (i = 0; i < h->cnt; i++, d -= 16)
+    if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
+      return 1;
+  return 0;
+}
+
 static unsigned int *
-headint32(RpmHead *h, int tag, int *cnt)
+headint32array(RpmHead *h, int tag, int *cnt)
 {
   unsigned int i, o, *r;
   unsigned char *d, taga[4];
@@ -94,7 +130,7 @@ headint32(RpmHead *h, int tag, int *cnt)
   if (o + 4 * i > h->dcnt)
     return 0;
   d = h->dp + o;
-  r = calloc(i ? i : 1, sizeof(unsigned int));
+  r = sat_calloc(i ? i : 1, sizeof(unsigned int));
   if (cnt)
     *cnt = i;
   for (o = 0; o < i; o++, d += 4)
@@ -102,6 +138,63 @@ headint32(RpmHead *h, int tag, int *cnt)
   return r;
 }
 
+static unsigned int
+headint32(RpmHead *h, int tag)
+{
+  unsigned int i, o;
+  unsigned char *d, taga[4];
+
+  d = h->dp - 16; 
+  taga[0] = tag >> 24; 
+  taga[1] = tag >> 16; 
+  taga[2] = tag >> 8;
+  taga[3] = tag;
+  for (i = 0; i < h->cnt; i++, d -= 16) 
+    if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
+      break;
+  if (i >= h->cnt)
+    return 0;
+  if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
+    return 0;
+  o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
+  i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
+  if (i == 0 || o + 4 * i > h->dcnt)
+    return 0;
+  d = h->dp + o;
+  return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+}
+
+static unsigned int *
+headint16array(RpmHead *h, int tag, int *cnt)
+{
+  unsigned int i, o, *r;
+  unsigned char *d, taga[4];
+
+  d = h->dp - 16;
+  taga[0] = tag >> 24;
+  taga[1] = tag >> 16;
+  taga[2] = tag >> 8;
+  taga[3] = tag;
+  for (i = 0; i < h->cnt; i++, d -= 16)
+    if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
+      break;
+  if (i >= h->cnt)
+    return 0;
+  if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
+    return 0;
+  o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
+  i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
+  if (o + 4 * i > h->dcnt)
+    return 0;
+  d = h->dp + o;
+  r = sat_calloc(i ? i : 1, sizeof(unsigned int));
+  if (cnt)
+    *cnt = i;
+  for (o = 0; o < i; o++, d += 2)
+    r[o] = d[0] << 8 | d[1];
+  return r;
+}
+
 static char *
 headstring(RpmHead *h, int tag)
 {
@@ -117,7 +210,8 @@ headstring(RpmHead *h, int tag)
       break;
   if (i >= h->cnt)
     return 0;
-  if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 6)
+  /* 6: STRING, 9: I18NSTRING */
+  if (d[4] != 0 || d[5] != 0 || d[6] != 0 || (d[7] != 6 && d[7] != 9))
     return 0;
   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
   return (char *)h->dp + o;
@@ -144,7 +238,7 @@ headstringarray(RpmHead *h, int tag, int *cnt)
     return 0;
   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
-  r = calloc(i ? i : 1, sizeof(char *));
+  r = sat_calloc(i ? i : 1, sizeof(char *));
   if (cnt)
     *cnt = i;
   d = h->dp + o;
@@ -155,7 +249,7 @@ headstringarray(RpmHead *h, int tag, int *cnt)
         d += strlen((char *)d) + 1;
       if (d >= h->dp + h->dcnt)
         {
-          free(r);
+          sat_free(r);
           return 0;
         }
     }
@@ -164,15 +258,14 @@ headstringarray(RpmHead *h, int tag, int *cnt)
 
 static char *headtoevr(RpmHead *h)
 {
-  unsigned int epoch, *epochp; 
+  unsigned int epoch;
   char *version, *v;
   char *release;
   char *evr;
-  int epochcnt = 0;
 
   version  = headstring(h, TAG_VERSION);
   release  = headstring(h, TAG_RELEASE);
-  epochp = headint32(h, TAG_EPOCH, &epochcnt);
+  epoch = headint32(h, TAG_EPOCH);
   if (!version || !release)
     {
       fprintf(stderr, "headtoevr: bad rpm header\n");
@@ -180,21 +273,18 @@ static char *headtoevr(RpmHead *h)
     }
   for (v = version; *v >= 0 && *v <= '9'; v++)
     ;
-  epoch = epochp && epochcnt ? *epochp : 0;
   if (epoch || (v != version && *v == ':'))
     {
       char epochbuf[11];        /* 32bit decimal will fit in */
       sprintf(epochbuf, "%u", epoch);
-      evr = malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
+      evr = sat_malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
       sprintf(evr, "%s:%s-%s", epochbuf, version, release);
     }
   else
     {
-      evr = malloc(strlen(version) + 1 + strlen(release) + 1);
+      evr = sat_malloc(strlen(version) + 1 + strlen(release) + 1);
       sprintf(evr, "%s-%s", version, release);
     }
-  if (epochp)
-    free(epochp);
   return evr;
 }
 
@@ -214,13 +304,13 @@ makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf,
   v = headstringarray(rpmhead, tagv, &vc);
   if (!v)
     {
-      free(n);
+      sat_free(n);
       return 0;
     }
-  f = headint32(rpmhead, tagf, &fc);
+  f = headint32array(rpmhead, tagf, &fc);
   if (!f)
     {
-      free(n);
+      sat_free(n);
       free(v);
       return 0;
     }
@@ -255,9 +345,9 @@ makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf,
      haspre = 0;
   if (cc == 0)
     {
-      free(n);
-      free(v);
-      free(f);
+      sat_free(n);
+      sat_free(v);
+      sat_free(f);
       return 0;
     }
   cc += haspre;
@@ -301,9 +391,9 @@ makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf,
     }
   *ida++ = 0;
   repo->idarraysize += cc + 1;
-  free(n);
-  free(v);
-  free(f);
+  sat_free(n);
+  sat_free(v);
+  sat_free(f);
   return olddeps;
 }
 
@@ -371,9 +461,61 @@ static struct filefilter filefilters[] = {
   { FILEFILTER_STARTS, "/opt/gnome/games/", 0},
 };
 
+static void
+adddudata(Pool *pool, Repo *repo, Repodata *repodata, Solvable *s, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dic)
+{
+  Id entry, did;
+  int i, fszc;
+  unsigned int *fkb, *fn, *fsz, *fm;
+
+  fsz = headint32array(rpmhead, TAG_FILESIZES, &fszc);
+  if (!fsz || fc != fszc)
+    {
+      sat_free(fsz);
+      return;
+    }
+  /* stupid rpm recodrs sizes of directories, so we have to check the mode */
+  fm = headint16array(rpmhead, TAG_FILEMODES, &fszc);
+  if (!fm || fc != fszc)
+    {
+      sat_free(fsz);
+      sat_free(fm);
+      return;
+    }
+  fn = sat_calloc(dic, sizeof(unsigned int));
+  fkb = sat_calloc(dic, sizeof(unsigned int));
+  for (i = 0; i < fc; i++)
+    {
+      if (fsz[i] == 0 || !S_ISREG(fm[i]))
+       continue;
+      if (di[i] >= dic)
+       continue;
+      fn[di[i]]++;
+      /* does not consider hard links. tough luck. */
+      fkb[di[i]] += fsz[i] / 1024 + 1;
+    }
+  sat_free(fsz);
+  sat_free(fm);
+  /* commit */
+  repodata_extend(repodata, s - pool->solvables);
+  entry = (s - pool->solvables) - repodata->start;
+  for (i = 0; i < fc; i++)
+    {
+      if (!fn[i])
+       continue;
+      if (!*dn[i] && (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC))
+        did = repodata_str2dir(repodata, "/usr/src", 1);
+      else
+        did = repodata_str2dir(repodata, dn[i], 1);
+      repodata_add_dirnumnum(repodata, entry, SOLVABLE_DISKUSAGE, did, fkb[i], fn[i]);
+    }
+  sat_free(fn);
+  sat_free(fkb);
+}
+
 /* assumes last processed array is provides! */
 static unsigned int
-addfileprovides(Pool *pool, Repo *repo, RpmHead *rpmhead, unsigned int olddeps)
+addfileprovides(Pool *pool, Repo *repo, Repodata *repodata, Solvable *s, RpmHead *rpmhead, unsigned int olddeps)
 {
   char **bn;
   char **dn;
@@ -384,20 +526,22 @@ addfileprovides(Pool *pool, Repo *repo, RpmHead *rpmhead, unsigned int olddeps)
   char *fn = 0;
   int fna = 0;
 
+  if (!repodata)
+    return olddeps;
   bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
   if (!bn)
     return olddeps;
   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
   if (!dn)
     {
-      free(bn);
+      sat_free(bn);
       return olddeps;
     }
-  di = headint32(rpmhead, TAG_DIRINDEXES, &dic);
+  di = headint32array(rpmhead, TAG_DIRINDEXES, &dic);
   if (!di)
     {
-      free(bn);
-      free(dn);
+      sat_free(bn);
+      sat_free(dn);
       return olddeps;
     }
   if (bnc != dic)
@@ -405,6 +549,10 @@ addfileprovides(Pool *pool, Repo *repo, RpmHead *rpmhead, unsigned int olddeps)
       fprintf(stderr, "bad filelist\n");
       exit(1);
     }
+
+  if (repodata)
+    adddudata(pool, repo, repodata, s, rpmhead, dn, di, bnc, dic);
+
   for (i = 0; i < bnc; i++)
     {
       ff = filefilters;
@@ -441,26 +589,33 @@ addfileprovides(Pool *pool, Repo *repo, RpmHead *rpmhead, unsigned int olddeps)
       j = strlen(bn[i]) + strlen(dn[di[i]]) + 1;
       if (j > fna)
        {
-         if (fn)
-           fn = realloc(fn, j + 256);
-         else
-           fn = malloc(j + 256);
          fna = j + 256;
+         fn = sat_realloc(fn, fna);
        }
       strcpy(fn, dn[di[i]]);
       strcat(fn, bn[i]);
-      olddeps = repo_addid(repo, olddeps, str2id(pool, fn, 1));
+#if 0
+      olddeps = repo_addid_dep(repo, olddeps, str2id(pool, fn, 1), SOLVABLE_FILEMARKER);
+#endif
+      if (repodata)
+       {
+         Id entry, did;
+         repodata_extend(repodata, s - pool->solvables);
+         entry = (s - pool->solvables) - repodata->start;
+         did = repodata_str2dir(repodata, dn[di[i]], 1);
+         repodata_add_dirstr(repodata, entry, SOLVABLE_FILELIST, did, bn[i]);
+       }
     }
   if (fn)
-    free(fn);
-  free(bn);
-  free(dn);
-  free(di);
+    sat_free(fn);
+  sat_free(bn);
+  sat_free(dn);
+  sat_free(di);
   return olddeps;
 }
 
 static int
-rpm2solv(Pool *pool, Repo *repo, Solvable *s, RpmHead *rpmhead)
+rpm2solv(Pool *pool, Repo *repo, Repodata *repodata, Solvable *s, RpmHead *rpmhead)
 {
   char *name;
   char *evr;
@@ -474,18 +629,24 @@ rpm2solv(Pool *pool, Repo *repo, Solvable *s, RpmHead *rpmhead)
       fprintf(stderr, "package has no name\n");
       exit(1);
     }
-  s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
-  if (!s->arch)
+  if (headstring(rpmhead, TAG_SOURCERPM))
+    s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
+  else
     {
-      fprintf(stderr, "package %s has no arch\n", id2str(pool, s->name));
-      exit(1);
+      if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
+        s->arch = ARCH_NOSRC;
+      else
+        s->arch = ARCH_SRC;
     }
+  if (!s->arch)
+    s->arch = ARCH_NOARCH;
   evr = headtoevr(rpmhead);
   s->evr = str2id(pool, evr, 1);
-  free(evr);
+  sat_free(evr);
+  s->vendor = str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
 
   s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
-  s->provides = addfileprovides(pool, repo, rpmhead, s->provides);
+  s->provides = addfileprovides(pool, repo, repodata, s, rpmhead, s->provides);
   s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
   s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, 0);
   s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
@@ -497,6 +658,71 @@ rpm2solv(Pool *pool, Repo *repo, Solvable *s, RpmHead *rpmhead)
   s->enhances  = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 1);
   s->freshens = 0;
   s->supplements = repo_fix_legacy(repo, s->provides, s->supplements);
+
+  if (repodata)
+    {
+      Id entry;
+      char *str;
+      unsigned int u32;
+
+      repodata_extend(repodata, s - pool->solvables);
+      entry = (s - pool->solvables) - repodata->start;
+      str = headstring(rpmhead, TAG_SUMMARY);
+      if (str)
+        repodata_set_str(repodata, entry, SOLVABLE_SUMMARY, str);
+      str = headstring(rpmhead, TAG_DESCRIPTION);
+      if (str)
+       {
+         char *aut, *p;
+         for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
+           if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
+             break;
+         if (aut)
+           {
+             /* oh my, found SUSE special author section */
+             int l = aut - str;
+             str = strdup(str);
+             aut = str + l;
+             str[l] = 0;
+             while (l > 0 && str[l - 1] == '\n')
+               str[--l] = 0;
+             if (l)
+                repodata_set_str(repodata, entry, SOLVABLE_DESCRIPTION, str);
+             p = aut + 19;
+             aut = str;        /* copy over */
+             while (*p == ' ' || *p == '\n')
+               p++;
+             while (*p)
+               {
+                 if (*p == '\n')
+                   {
+                     *aut++ = *p++;
+                     while (*p == ' ')
+                       p++;
+                     continue;
+                   }
+                 *aut++ = *p++;
+               }
+             while (aut != str && aut[-1] == '\n')
+               aut--;
+             *aut = 0;
+             if (*str)
+               repodata_set_str(repodata, entry, SOLVABLE_AUTHORS, str);
+             free(str);
+           }
+         else if (*str)
+           repodata_set_str(repodata, entry, SOLVABLE_DESCRIPTION, str);
+       }
+      str = headstring(rpmhead, TAG_GROUP);
+      if (str)
+        repodata_set_poolstr(repodata, entry, SOLVABLE_GROUP, str);
+      u32 = headint32(rpmhead, TAG_BUILDTIME);
+      if (u32)
+        repodata_set_num(repodata, entry, SOLVABLE_BUILDTIME, u32);
+      u32 = headint32(rpmhead, TAG_SIZE);
+      if (u32)
+        repodata_set_num(repodata, entry, SOLVABLE_INSTALLSIZE, (u32 + 1023) / 1024);
+    }
   return 1;
 }
 
@@ -506,9 +732,10 @@ rpm2solv(Pool *pool, Repo *repo, Solvable *s, RpmHead *rpmhead)
  * 
  */
 
-Repo *
-pool_addrepo_rpmdb(Pool *pool, Repo *ref)
+void
+repo_add_rpmdb(Repo *repo, Repo *ref, const char *rootdir)
 {
+  Pool *pool = repo->pool;
   unsigned char buf[16];
   DB *db = 0;
   DBC *dbc = 0;
@@ -520,13 +747,17 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
   int i;
   int rpmheadsize;
   RpmHead *rpmhead;
-  Repo *repo;
   Solvable *s;
   Id id, *refhash;
   unsigned int refmask, h;
   int asolv;
+  Repodata *repodata;
+  char dbpath[PATH_MAX];
+
+  if (repo->start != repo->end)
+    abort();           /* FIXME: rpmdbid */
 
-  repo = pool_addrepo_empty(pool);
+  repodata = repo_add_repodata(repo);
 
   if (ref && !(ref->nsolvables && ref->rpmdbid))
     ref = 0;
@@ -539,9 +770,10 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
 
   if (!ref)
     {
-      if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
+      snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
+      if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
        {
-         perror("db->open /var/lib/rpm/Packages");
+         perror("db->open var/lib/rpm/Packages");
          exit(1);
        }
       if (db->get_byteswapped(db, &byteswapped))
@@ -555,24 +787,22 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
          exit(1);
        }
       dbidp = (unsigned char *)&dbid;
-      pool->solvables = realloc(pool->solvables, (pool->nsolvables + 256) * sizeof(Solvable));
-      memset(pool->solvables + repo->start, 0, 256 * sizeof(Solvable));
-      repo->rpmdbid = calloc(256, sizeof(unsigned int));
+      repo->rpmdbid = sat_calloc(256, sizeof(unsigned int));
       asolv = 256;
       rpmheadsize = 0;
       rpmhead = 0;
       i = 0;
+      s = 0;
       while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
        {
+         if (!s)
+           s = pool_id2solvable(pool, repo_add_solvable(repo));
          if (i >= asolv)
            {
-             pool->solvables = realloc(pool->solvables, (pool->nsolvables + asolv + 256) * sizeof(Solvable));
-             memset(pool->solvables + repo->start + asolv, 0, 256 * sizeof(Solvable));
-             repo->rpmdbid = realloc(repo->rpmdbid, (asolv + 256) * sizeof(unsigned int));
+             repo->rpmdbid = sat_realloc(repo->rpmdbid, (asolv + 256) * sizeof(unsigned int));
              memset(repo->rpmdbid + asolv, 0, 256 * sizeof(unsigned int));
              asolv += 256;
            }
-         pool->solvables[repo->start + i].repo = repo;
           if (key.size != 4)
            {
              fprintf(stderr, "corrupt Packages database (key size)\n");
@@ -595,10 +825,8 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
              fprintf(stderr, "corrupt rpm database (size %u)\n", data.size);
              exit(1);
            }
-         if (!rpmhead)
-           rpmhead = malloc(sizeof(*rpmhead) + data.size);
-         else if (data.size > rpmheadsize)
-           rpmhead = realloc(rpmhead, sizeof(*rpmhead) + data.size);
+         if (data.size > rpmheadsize)
+           rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + data.size);
          memcpy(buf, data.data, 8);
          rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
          rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
@@ -610,19 +838,35 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
          memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
          rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
          repo->rpmdbid[i] = dbid;
-         if (rpm2solv(pool, repo, pool->solvables + repo->start + i, rpmhead))
-           i++;
+         if (rpm2solv(pool, repo, repodata, s, rpmhead))
+           {
+             i++;
+             s = 0;
+           }
+         else
+           {
+             /* We can reuse this solvable, but make sure it's still
+                associated with this repo.  */
+             memset(s, 0, sizeof(*s));
+             s->repo = repo;
+           }
+       }
+      if (s)
+       {
+         /* oops, could not reuse. free it instead */
+          repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
+         s = 0;
        }
-      nrpmids = i;
       dbc->c_close(dbc);
       db->close(db, 0);
       db = 0;
     }
   else
     {
-      if (db->open(db, 0, "/var/lib/rpm/Name", 0, DB_HASH, DB_RDONLY, 0664))
+      snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Name", rootdir);
+      if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
        {
-         perror("db->open /var/lib/rpm/Name");
+         perror("db->open var/lib/rpm/Name");
          exit(1);
        }
       if (db->get_byteswapped(db, &byteswapped))
@@ -656,21 +900,15 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
              else
                memcpy(dbidp, dp, 4);
              if ((nrpmids & 255) == 0)
-               {
-                 if (rpmids)
-                   rpmids = realloc(rpmids, sizeof(*rpmids) * (nrpmids + 256));
-                 else
-                   rpmids = malloc(sizeof(*rpmids) * 256);
-               }
+               rpmids = sat_realloc(rpmids, sizeof(*rpmids) * (nrpmids + 256));
              rpmids[nrpmids].dbid = dbid;
-             rpmids[nrpmids].name = malloc((int)key.size + 1);
+             rpmids[nrpmids].name = sat_malloc((int)key.size + 1);
              memcpy(rpmids[nrpmids].name, key.data, (int)key.size);
              rpmids[nrpmids].name[(int)key.size] = 0;
              nrpmids++;
              dp += 8;
              dl -= 8;
            }
-         
        }
       dbc->c_close(dbc);
       db->close(db, 0);
@@ -681,16 +919,12 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
       rpmheadsize = 0;
       rpmhead = 0;
 
-      pool->solvables = realloc(pool->solvables, (pool->nsolvables + nrpmids) * sizeof(Solvable));
-      memset(pool->solvables + repo->start, 0, nrpmids * sizeof(Solvable));
-      repo->rpmdbid = calloc(nrpmids, sizeof(unsigned int));
-
       refhash = 0;
       refmask = 0;
       if (ref)
        {
          refmask = mkmask(ref->nsolvables);
-         refhash = calloc(refmask + 1, sizeof(Id));
+         refhash = sat_calloc(refmask + 1, sizeof(Id));
          for (i = 0; i < ref->nsolvables; i++)
            {
              h = ref->rpmdbid[i] & refmask;
@@ -699,10 +933,13 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
              refhash[h] = i + 1;       /* make it non-zero */
            }
        }
-      s = pool->solvables + repo->start;
+
+      repo->rpmdbid = sat_calloc(nrpmids, sizeof(unsigned int));
+
+      s = pool_id2solvable(pool, repo_add_solvable_block(repo, nrpmids));
+
       for (i = 0; i < nrpmids; i++, rp++, s++)
        {
-         s->repo = repo;
          dbid = rp->dbid;
          repo->rpmdbid[i] = dbid;
          if (refhash)
@@ -722,6 +959,7 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
                      s->name = r->name;
                      s->evr = r->evr;
                      s->arch = r->arch;
+                     s->vendor = r->vendor;
                    }
                  else
                    {
@@ -731,6 +969,8 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
                        s->evr = str2id(pool, id2str(ref->pool, r->evr), 1);
                      if (r->arch)
                        s->arch = str2id(pool, id2str(ref->pool, r->arch), 1);
+                     if (r->vendor)
+                       s->vendor = str2id(pool, id2str(ref->pool, r->vendor), 1);
                    }
                  s->provides = copydeps(pool, repo, r->provides, ref);
                  s->requires = copydeps(pool, repo, r->requires, ref);
@@ -751,9 +991,10 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
                  perror("db_create");
                  exit(1);
                }
-             if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
+             snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
+             if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
                {
-                 perror("db->open /var/lib/rpm/Packages");
+                 perror("db->open var/lib/rpm/Packages");
                  exit(1);
                }
              if (db->get_byteswapped(db, &byteswapped))
@@ -786,10 +1027,8 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
              fprintf(stderr, "corrupt rpm database (size)\n");
              exit(1);
            }
-         if (!rpmhead)
-           rpmhead = malloc(sizeof(*rpmhead) + data.size);
-         else if (data.size > rpmheadsize)
-           rpmhead = realloc(rpmhead, sizeof(*rpmhead) + data.size);
+         if (data.size > rpmheadsize)
+           rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + data.size);
          memcpy(buf, data.data, 8);
          rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
          rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
@@ -801,24 +1040,193 @@ pool_addrepo_rpmdb(Pool *pool, Repo *ref)
          memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
          rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
 
-         rpm2solv(pool, repo, s, rpmhead);
+         rpm2solv(pool, repo, repodata, s, rpmhead);
        }
 
       if (refhash)
-       free(refhash);
+       sat_free(refhash);
       if (rpmids)
        {
          for (i = 0; i < nrpmids; i++)
-           free(rpmids[i].name);
-         free(rpmids);
+           sat_free(rpmids[i].name);
+         sat_free(rpmids);
        }
     }
   if (rpmhead)
-    free(rpmhead);
-  pool->nsolvables += nrpmids;
-  repo->nsolvables = nrpmids;
-
+    sat_free(rpmhead);
   if (db)
     db->close(db, 0);
-  return repo;
+  if (repodata)
+    repodata_internalize(repodata);
+}
+
+static inline unsigned int
+getu32(unsigned char *dp)
+{
+  return dp[0] << 24 | dp[1] << 16 | dp[2] << 8 | dp[3];
+}
+
+static void
+add_location(Repodata *data, Solvable *s, const char *location)
+{
+  Pool *pool = s->repo->pool;
+  const char *name, *n1, *n2;
+  int l;
+  Id entry;
+
+  repodata_extend(data, s - pool->solvables);
+  entry = (s - pool->solvables) - data->start;
+
+  /* skip ./ prefix */
+  if (location[0] == '.' && location[1] == '/' && location[2] != '/')
+    location += 2;
+
+  name = strrchr(location, '/');
+  if (!name)
+    name = location;
+  else
+    {
+      name++;
+      n2 = id2str(pool, s->arch);
+      l = strlen(n2);
+      if (strncmp(location, n2, l) != 0 || location + l + 1 != name)
+       {
+         /* too bad, need to store directory */
+         char *dir = strdup(location);
+         dir[name - location - 1] = 0;
+         repodata_set_str(data, entry, SOLVABLE_MEDIADIR, dir);
+         free(dir);
+       }
+      else
+        repodata_set_void(data, entry, SOLVABLE_MEDIADIR);
+    }
+  n1 = name;
+  for (n2 = id2str(pool, s->name); *n2; n1++, n2++)
+    if (*n1 != *n2)
+      break;
+  if (*n2 || *n1 != '-')
+    goto nontrivial;
+  n1++;
+  for (n2 = id2str (pool, s->evr); *n2; n1++, n2++)
+    if (*n1 != *n2)
+      break;
+  if (*n2 || *n1 != '.')
+    goto nontrivial;
+  n1++;
+  for (n2 = id2str (pool, s->arch); *n2; n1++, n2++)
+    if (*n1 != *n2)
+      break;
+  if (*n2 || strcmp (n1, ".rpm"))
+    goto nontrivial;
+  repodata_set_void(data, entry, SOLVABLE_MEDIAFILE);
+  return;
+
+nontrivial:
+  repodata_set_str(data, entry, SOLVABLE_MEDIAFILE, name);
+  return;
+}
+
+
+void
+repo_add_rpms(Repo *repo, const char **rpms, int nrpms)
+{
+  int i, sigdsize, sigcnt, l;
+  Pool *pool = repo->pool;
+  Solvable *s;
+  Repodata *repodata;
+  RpmHead *rpmhead = 0;
+  int rpmheadsize = 0;
+  FILE *fp;
+  unsigned char lead[4096];
+
+  if (nrpms <= 0)
+    return;
+  repodata = repo_add_repodata(repo);
+  for (i = 0; i < nrpms; i++)
+    {
+      if ((fp = fopen(rpms[i], "r")) == 0)
+       {
+         perror(rpms[i]);
+         continue;
+       }
+      if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
+       {
+         fprintf(stderr, "%s: not a rpm\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      if (lead[78] != 0 || lead[79] != 5)
+       {
+         fprintf(stderr, "%s: not a V5 header\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      if (getu32(lead + 96) != 0x8eade801)
+       {
+         fprintf(stderr, "%s: bad signature header\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      sigcnt = getu32(lead + 96 + 8);
+      sigdsize = getu32(lead + 96 + 12);
+      if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
+       {
+         fprintf(stderr, "%s: bad signature header\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      sigdsize += sigcnt * 16;
+      sigdsize = (sigdsize + 7) & ~7;
+      while (sigdsize)
+       {
+         l = sigdsize > 4096 ? 4096 : sigdsize;
+         if (fread(lead, l, 1, fp) != 1)
+           {
+             fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
+             fclose(fp);
+             continue;
+           }
+         sigdsize -= l;
+       }
+      if (fread(lead, 16, 1, fp) != 1)
+       {
+         fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      if (getu32(lead) != 0x8eade801)
+       {
+         fprintf(stderr, "%s: bad header\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      sigcnt = getu32(lead + 8);
+      sigdsize = getu32(lead + 12);
+      if (sigcnt >= 0x4000000 || sigdsize >= 0x40000000)
+       {
+         fprintf(stderr, "%s: bad header\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      l = sigdsize + sigcnt * 16;
+      if (l > rpmheadsize)
+       rpmhead = sat_realloc(rpmhead, sizeof(*rpmhead) + l);
+      if (fread(rpmhead->data, l, 1, fp) != 1)
+       {
+         fprintf(stderr, "%s: unexpected EOF\n", rpms[i]);
+         fclose(fp);
+         continue;
+       }
+      rpmhead->cnt = sigcnt;
+      rpmhead->dcnt = sigdsize;
+      rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
+      fclose(fp);
+      s = pool_id2solvable(pool, repo_add_solvable(repo));
+      rpm2solv(pool, repo, repodata, s, rpmhead);
+      add_location(repodata, s, rpms[i]);
+    }
+  if (rpmhead)
+    sat_free(rpmhead);
+  if (repodata)
+    repodata_internalize(repodata);
 }