- fix di->repoid to be consistent with repo->repoid
[platform/upstream/libsolv.git] / src / repodata.c
index f269fd7..55517c5 100644 (file)
@@ -9,7 +9,9 @@
  * repodata.c
  *
  * Manage data coming from one repository
- * 
+ *
+ * a repository can contain multiple repodata entries, consisting of
+ * different sets of keys and different sets of solvables
  */
 
 #define _GNU_SOURCE
 #include <stdlib.h>
 #include <unistd.h>
 #include <assert.h>
+#include <regex.h>
 
 #include "repo.h"
 #include "pool.h"
 #include "poolid_private.h"
 #include "util.h"
+#include "hash.h"
+#include "chksum.h"
 
 #include "repopack.h"
 #include "repopage.h"
 
-extern unsigned int compress_buf (const unsigned char *in, unsigned int in_len,
-                                 unsigned char *out, unsigned int out_len);
-extern unsigned int unchecked_decompress_buf (const unsigned char *in,
-                                             unsigned int in_len,
-                                             unsigned char *out,
-                                             unsigned int out_len);
-
 #define REPODATA_BLOCK 255
 
+static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
 
 void
-repodata_init(Repodata *data, Repo *repo, int localpool)
+repodata_initdata(Repodata *data, Repo *repo, int localpool)
 {
   memset(data, 0, sizeof (*data));
   data->repo = repo;
   data->localpool = localpool;
   if (localpool)
     stringpool_init_empty(&data->spool);
+  /* dirpool_init(&data->dirpool);     just zeros out again */
   data->keys = sat_calloc(1, sizeof(Repokey));
   data->nkeys = 1;
   data->schemata = sat_calloc(1, sizeof(Id));
   data->schemadata = sat_calloc(1, sizeof(Id));
   data->nschemata = 1;
   data->schemadatalen = 1;
-  data->pagefd = -1;
+  repopagestore_init(&data->store);
 }
 
 void
-repodata_free(Repodata *data)
+repodata_freedata(Repodata *data)
 {
   int i;
 
@@ -75,9 +75,7 @@ repodata_free(Repodata *data)
   sat_free(data->incoreoffset);
   sat_free(data->verticaloffset);
 
-  sat_free(data->blob_store);
-  sat_free(data->pages);
-  sat_free(data->mapped);
+  repopagestore_free(&data->store);
 
   sat_free(data->vincore);
 
@@ -92,9 +90,40 @@ repodata_free(Repodata *data)
 
   sat_free(data->attrdata);
   sat_free(data->attriddata);
-  
-  if (data->pagefd != -1)
-    close(data->pagefd);
+}
+
+Repodata *
+repodata_create(Repo *repo, int localpool)
+{
+  Repodata *data;
+
+  repo->nrepodata++;
+  repo->repodata = sat_realloc2(repo->repodata, repo->nrepodata, sizeof(*data));
+  data = repo->repodata + repo->nrepodata - 1;
+  repodata_initdata(data, repo, localpool);
+  return data;
+}
+
+void
+repodata_free(Repodata *data)
+{
+  Repo *repo = data->repo;
+  int i = data - repo->repodata;
+  repodata_freedata(data);
+  if (i < repo->nrepodata - 1)
+    memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
+  repo->nrepodata--;
+}
+
+void
+repodata_empty(Repodata *data, int localpool)
+{
+  void (*loadcallback)(Repodata *) = data->loadcallback;
+  int state = data->state;
+  repodata_freedata(data);
+  repodata_initdata(data, data->repo, localpool);
+  data->state = state;
+  data->loadcallback = loadcallback;
 }
 
 
@@ -111,13 +140,13 @@ repodata_key2id(Repodata *data, Repokey *key, int create)
 
   for (keyid = 1; keyid < data->nkeys; keyid++)
     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
-      {    
+      {
         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
           continue;
         break;
       }
   if (keyid == data->nkeys)
-    {    
+    {
       if (!create)
        return 0;
       /* allocate new key */
@@ -126,7 +155,7 @@ repodata_key2id(Repodata *data, Repokey *key, int create)
       if (data->verticaloffset)
         {
           data->verticaloffset = sat_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
-          data->verticaloffset[data->nkeys - 1] = 0; 
+          data->verticaloffset[data->nkeys - 1] = 0;
         }
       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
     }
@@ -144,54 +173,55 @@ repodata_key2id(Repodata *data, Repokey *key, int create)
 Id
 repodata_schema2id(Repodata *data, Id *schema, int create)
 {
-  int h, len, i; 
-  Id *sp, cid; 
+  int h, len, i;
+  Id *sp, cid;
   Id *schematahash;
 
+  if (!*schema)
+    return 0;  /* XXX: allow empty schema? */
   if ((schematahash = data->schematahash) == 0)
     {
       data->schematahash = schematahash = sat_calloc(256, sizeof(Id));
-      for (i = 0; i < data->nschemata; i++)
+      for (i = 1; i < data->nschemata; i++)
        {
          for (sp = data->schemadata + data->schemata[i], h = 0; *sp; len++)
            h = h * 7 + *sp++;
          h &= 255;
-         schematahash[h] = i + 1;
+         schematahash[h] = i;
        }
-      data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK); 
+      data->schemadata = sat_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
       data->schemata = sat_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
     }
 
   for (sp = schema, len = 0, h = 0; *sp; len++)
     h = h * 7 + *sp++;
-  h &= 255; 
+  h &= 255;
   len++;
 
   cid = schematahash[h];
   if (cid)
-    {    
-      cid--;
+    {
       if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
         return cid;
-      /* cache conflict */
-      for (cid = 0; cid < data->nschemata; cid++)
+      /* cache conflict, do a slow search */
+      for (cid = 1; cid < data->nschemata; cid++)
         if (!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
           return cid;
     }
   /* a new one */
   if (!create)
     return 0;
-  data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK); 
+  data->schemadata = sat_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
   data->schemata = sat_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
   /* add schema */
   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
   data->schemata[data->nschemata] = data->schemadatalen;
   data->schemadatalen += len;
-  schematahash[h] = data->nschemata + 1;
+  schematahash[h] = data->nschemata;
 #if 0
 fprintf(stderr, "schema2id: new schema\n");
 #endif
-  return data->nschemata++; 
+  return data->nschemata++;
 }
 
 void
@@ -208,6 +238,14 @@ repodata_free_schemahash(Repodata *data)
  * dir pool management
  */
 
+#ifndef HAVE_STRCHRNUL
+static inline const char *strchrnul(const char *str, char x)
+{
+  const char *p = strchr(str, x);
+  return p ? p : str + strlen(str);
+}
+#endif
+
 Id
 repodata_str2dir(Repodata *data, const char *dir, int create)
 {
@@ -218,14 +256,18 @@ repodata_str2dir(Repodata *data, const char *dir, int create)
   while (*dir == '/' && dir[1] == '/')
     dir++;
   if (*dir == '/' && !dir[1])
-    return 1;
+    {
+      if (data->dirpool.ndirs)
+        return 1;
+      return dirpool_add_dir(&data->dirpool, 0, 1, create);
+    }
   while (*dir)
     {
       dire = strchrnul(dir, '/');
       if (data->localpool)
         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
       else
-       id = strn2id(data->repo->pool, dir, dire - dir, create);
+       id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
       if (!id)
        return 0;
       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
@@ -300,7 +342,7 @@ data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
   return dp;
 }
 
-unsigned char *
+static unsigned char *
 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
 {
   int nentries, schema;
@@ -341,6 +383,14 @@ forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
 
   if (!keyid)
     return 0;
+  if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
+    {
+      int i;
+      for (i = 0; (k = *keyp++) != 0; i++)
+        if (k == keyid)
+         return data->incoredata + data->mainschemaoffsets[i];
+      return 0;
+    }
   while ((k = *keyp++) != 0)
     {
       if (k == keyid)
@@ -375,15 +425,15 @@ get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
     return 0;
   /* we now have the offset, go into vertical */
   off += data->verticaloffset[key - data->keys];
-  /* fprintf(stderr, "key %d page %d\n", key->name, off / BLOB_PAGESIZE); */
-  dp = repodata_load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
+  /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
+  dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
   if (dp)
-    dp += off % BLOB_PAGESIZE;
+    dp += off % REPOPAGE_BLOBSIZE;
   return dp;
 }
 
 static inline unsigned char *
-get_data(Repodata *data, Repokey *key, unsigned char **dpp)
+get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
 {
   unsigned char *dp = *dpp;
 
@@ -391,8 +441,8 @@ get_data(Repodata *data, Repokey *key, unsigned char **dpp)
     return 0;
   if (key->storage == KEY_STORAGE_INCORE)
     {
-      /* hmm, this is a bit expensive */
-      *dpp = data_skip_key(data, dp, key);
+      if (advance)
+        *dpp = data_skip_key(data, dp, key);
       return dp;
     }
   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
@@ -400,7 +450,8 @@ get_data(Repodata *data, Repokey *key, unsigned char **dpp)
       Id off, len;
       dp = data_read_id(dp, &off);
       dp = data_read_id(dp, &len);
-      *dpp = dp;
+      if (advance)
+        *dpp = dp;
       return get_vertical_data(data, key, off, len);
     }
   return 0;
@@ -440,6 +491,7 @@ maybe_load_repodata(Repodata *data, Id keyname)
     case REPODATA_ERROR:
       return 0;
     case REPODATA_AVAILABLE:
+    case REPODATA_LOADING:
       return 1;
     default:
       data->state = REPODATA_ERROR;
@@ -447,15 +499,15 @@ maybe_load_repodata(Repodata *data, Id keyname)
     }
 }
 
-static inline unsigned char*
-entry2data(Repodata *data, Id entry, Id *schemap)
+static inline unsigned char *
+solvid2data(Repodata *data, Id solvid, Id *schemap)
 {
   unsigned char *dp = data->incoredata;
   if (!dp)
     return 0;
-  if (entry == REPOENTRY_META) /* META */
+  if (solvid == SOLVID_META)   /* META */
     dp += 1;
-  else if (entry == REPOENTRY_POS)     /* META */
+  else if (solvid == SOLVID_POS)       /* META */
     {
       Pool *pool = data->repo->pool;
       if (data->repo != pool->pos.repo)
@@ -467,9 +519,9 @@ entry2data(Repodata *data, Id entry, Id *schemap)
     }
   else
     {
-      if (entry < data->start || entry >= data->end)
+      if (solvid < data->start || solvid >= data->end)
        return 0;
-      dp += data->incoreoffset[entry - data->start];
+      dp += data->incoreoffset[solvid - data->start];
     }
   return data_read_id(dp, schemap);
 }
@@ -478,51 +530,58 @@ entry2data(Repodata *data, Id entry, Id *schemap)
  * data lookup
  */
 
-static inline Id
-find_schema_key(Repodata *data, Id schema, Id keyname)
-{
-  Id *keyp;
-  for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
-    if (data->keys[*keyp].name == keyname)
-      return *keyp;
-  return 0;
-}
-
 static inline unsigned char *
-find_key_data(Repodata *data, Id entry, Id keyname, Repokey **keyp)
+find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
 {
-  unsigned char *dp, *ddp;
-  Id keyid, schema;
+  unsigned char *dp;
+  Id schema, *keyp, *kp;
   Repokey *key;
 
   if (!maybe_load_repodata(data, keyname))
     return 0;
-  dp = entry2data(data, entry, &schema);
+  dp = solvid2data(data, solvid, &schema);
   if (!dp)
     return 0;
-  keyid = find_schema_key(data, schema, keyname);
-  if (!keyid)
+  keyp = data->schemadata + data->schemata[schema];
+  for (kp = keyp; *kp; kp++)
+    if (data->keys[*kp].name == keyname)
+      break;
+  if (!*kp)
+    return 0;
+  *keypp = key = data->keys + *kp;
+  if (key->type == REPOKEY_TYPE_DELETED)
     return 0;
-  key = data->keys + keyid;
-  *keyp = key;
   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
-    return dp;
-  dp = forward_to_key(data, keyid, data->schemadata + data->schemata[schema], dp);
+    return dp; /* no need to forward... */
+  dp = forward_to_key(data, *kp, keyp, dp);
   if (!dp)
     return 0;
-  ddp = get_data(data, key, &dp);
-  return ddp;
+  return get_data(data, key, &dp, 0);
 }
 
+Id
+repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
+{
+  Id schema, *keyp, *kp;
+  if (!maybe_load_repodata(data, keyname))
+    return 0;
+  if (!solvid2data(data, solvid, &schema))
+    return 0;
+  keyp = data->schemadata + data->schemata[schema];
+  for (kp = keyp; *kp; kp++)
+    if (data->keys[*kp].name == keyname)
+      return data->keys[*kp].type;
+  return 0;
+}
 
 Id
-repodata_lookup_id(Repodata *data, Id entry, Id keyname)
+repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
 {
   unsigned char *dp;
   Repokey *key;
   Id id;
 
-  dp = find_key_data(data, entry, keyname, &key);
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
   if (key->type == REPOKEY_TYPE_CONSTANTID)
@@ -534,43 +593,44 @@ repodata_lookup_id(Repodata *data, Id entry, Id keyname)
 }
 
 const char *
-repodata_lookup_str(Repodata *data, Id entry, Id keyname)
+repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
 {
   unsigned char *dp;
   Repokey *key;
   Id id;
 
-  dp = find_key_data(data, entry, keyname, &key);
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
   if (key->type == REPOKEY_TYPE_STR)
     return (const char *)dp;
   if (key->type == REPOKEY_TYPE_CONSTANTID)
-    return id2str(data->repo->pool, key->size);
-  if (key->type == REPOKEY_TYPE_ID)
+    id = key->size;
+  else if (key->type == REPOKEY_TYPE_ID)
     dp = data_read_id(dp, &id);
   else
     return 0;
   if (data->localpool)
-    return data->spool.stringspace + data->spool.strings[id];
-  return id2str(data->repo->pool, id);
+    return stringpool_id2str(&data->spool, id);
+  return pool_id2str(data->repo->pool, id);
 }
 
 int
-repodata_lookup_num(Repodata *data, Id entry, Id keyname, unsigned int *value)
+repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned int *value)
 {
   unsigned char *dp;
   Repokey *key;
   KeyValue kv;
 
   *value = 0;
-  dp = find_key_data(data, entry, keyname, &key);
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
   if (key->type == REPOKEY_TYPE_NUM
       || key->type == REPOKEY_TYPE_U32
       || key->type == REPOKEY_TYPE_CONSTANT)
     {
+      kv.num = 0;
       dp = data_fetch(dp, &kv, key);
       *value = kv.num;
       return 1;
@@ -579,7 +639,7 @@ repodata_lookup_num(Repodata *data, Id entry, Id keyname, unsigned int *value)
 }
 
 int
-repodata_lookup_void(Repodata *data, Id entry, Id keyname)
+repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
 {
   Id schema;
   Id *keyp;
@@ -587,10 +647,10 @@ repodata_lookup_void(Repodata *data, Id entry, Id keyname)
 
   if (!maybe_load_repodata(data, keyname))
     return 0;
-  dp = entry2data(data, entry, &schema);
+  dp = solvid2data(data, solvid, &schema);
   if (!dp)
     return 0;
-  /* can't use find_schema_key as we need to test the type */
+  /* can't use find_key_data as we need to test the type */
   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
       return 1;
@@ -598,36 +658,123 @@ repodata_lookup_void(Repodata *data, Id entry, Id keyname)
 }
 
 const unsigned char *
-repodata_lookup_bin_checksum(Repodata *data, Id entry, Id keyname, Id *typep)
+repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
 {
   unsigned char *dp;
   Repokey *key;
 
-  dp = find_key_data(data, entry, keyname, &key);
+  dp = find_key_data(data, solvid, keyname, &key);
   if (!dp)
     return 0;
   *typep = key->type;
   return dp;
 }
 
+int
+repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
+{
+  unsigned char *dp;
+  Repokey *key;
+  Id id;
+  int eof = 0;
+
+  queue_empty(q);
+  dp = find_key_data(data, solvid, keyname, &key);
+  if (!dp)
+    return 0;
+  if (key->type != REPOKEY_TYPE_IDARRAY && key->type != REPOKEY_TYPE_REL_IDARRAY)
+    return 0;
+  for (;;)
+    {
+      dp = data_read_ideof(dp, &id, &eof);
+      queue_push(q, id);
+      if (eof)
+       break;
+    }
+  return 1;
+}
+
+Id
+repodata_globalize_id(Repodata *data, Id id, int create)
+{
+  if (!id || !data || !data->localpool)
+    return id;
+  return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
+}
+
+Id
+repodata_localize_id(Repodata *data, Id id, int create)
+{
+  if (!id || !data || !data->localpool)
+    return id;
+  return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
+}
+
 
 /************************************************************************
  * data search
  */
 
+
+int
+repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
+{
+  switch (key->type)
+    {
+    case REPOKEY_TYPE_ID:
+    case REPOKEY_TYPE_CONSTANTID:
+    case REPOKEY_TYPE_IDARRAY:
+      if (data && data->localpool)
+       kv->str = stringpool_id2str(&data->spool, kv->id);
+      else
+       kv->str = pool_id2str(pool, kv->id);
+      if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
+       {
+         const char *s;
+         for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
+           ;
+         if (*s == ':' && s > kv->str)
+           kv->str = s + 1;
+       }
+      return 1;
+    case REPOKEY_TYPE_STR:
+      return 1;
+    case REPOKEY_TYPE_DIRSTRARRAY:
+      if (!(flags & SEARCH_FILES))
+       return 1;       /* match just the basename */
+      /* Put the full filename into kv->str.  */
+      kv->str = repodata_dir2str(data, kv->id, kv->str);
+      /* And to compensate for that put the "empty" directory into
+        kv->id, so that later calls to repodata_dir2str on this data
+        come up with the same filename again.  */
+      kv->id = 0;
+      return 1;
+    case REPOKEY_TYPE_MD5:
+    case REPOKEY_TYPE_SHA1:
+    case REPOKEY_TYPE_SHA256:
+      if (!(flags & SEARCH_CHECKSUMS))
+       return 0;       /* skip em */
+      kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+
 struct subschema_data {
   Solvable *s;
   void *cbdata;
   KeyValue *parent;
 };
 
-/* search in a specific entry */
+/* search a specific repodata */
 void
-repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
 {
   Id schema;
   Repokey *key;
-  Id k, keyid, *kp, *keyp;
+  Id keyid, *kp, *keyp;
   unsigned char *dp, *ddp;
   int onekey = 0;
   int stop;
@@ -636,7 +783,7 @@ repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbda
 
   if (!maybe_load_repodata(data, keyname))
     return;
-  if (entry == REPOENTRY_SUBSCHEMA)
+  if (solvid == SOLVID_SUBSCHEMA)
     {
       struct subschema_data *subd = cbdata;
       cbdata = subd->cbdata;
@@ -648,33 +795,35 @@ repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbda
   else
     {
       schema = 0;
-      dp = entry2data(data, entry, &schema);
+      dp = solvid2data(data, solvid, &schema);
       if (!dp)
        return;
-      s = data->repo->pool->solvables + entry;
+      s = data->repo->pool->solvables + solvid;
       kv.parent = 0;
     }
   keyp = data->schemadata + data->schemata[schema];
   if (keyname)
     {
       /* search for a specific key */
-      for (kp = keyp; (k = *kp++) != 0; )
-       if (data->keys[k].name == keyname)
+      for (kp = keyp; *kp; kp++)
+       if (data->keys[*kp].name == keyname)
          break;
-      if (k == 0)
+      if (!*kp)
        return;
-      dp = forward_to_key(data, k, data->schemadata + data->schemata[schema], dp);
+      dp = forward_to_key(data, *kp, keyp, dp);
       if (!dp)
        return;
-      keyp = kp - 1;
+      keyp = kp;
       onekey = 1;
     }
   while ((keyid = *keyp++) != 0)
     {
       stop = 0;
       key = data->keys + keyid;
-      ddp = get_data(data, key, &dp);
+      ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
 
+      if (key->type == REPOKEY_TYPE_DELETED)
+       continue;
       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
        {
          struct subschema_data subd;
@@ -690,6 +839,8 @@ repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbda
          kv.eof = 0;
           while (ddp && nentries > 0)
            {
+             if (!--nentries)
+               kv.eof = 1;
              if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
                ddp = data_read_id(ddp, &schema);
              kv.id = schema;
@@ -697,18 +848,17 @@ repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbda
              stop = callback(cbdata, s, data, key, &kv);
              if (stop > SEARCH_NEXT_KEY)
                return;
-             if (stop)
+             if (stop && stop != SEARCH_ENTERSUB)
                break;
-             if (!keyname)
-               repodata_search(data, REPOENTRY_SUBSCHEMA, 0, callback, &subd);
+             if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
+               repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
              ddp = data_skip_schema(data, ddp, schema);
-             nentries--;
              kv.entry++;
            }
-         if (!nentries)
+         if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
            {
              /* sentinel */
-             kv.eof = 1;
+             kv.eof = 2;
              kv.str = (char *)ddp;
              stop = callback(cbdata, s, data, key, &kv);
              if (stop > SEARCH_NEXT_KEY)
@@ -738,22 +888,19 @@ repodata_setpos_kv(Repodata *data, KeyValue *kv)
 {
   Pool *pool = data->repo->pool;
   if (!kv)
-    {
-      pool->pos.repo = 0;
-      pool->pos.repodataid = 0;
-      pool->pos.dp = 0;
-      pool->pos.schema = 0;
-    }
+    pool_clear_pos(pool);
   else
     {
-      pool->pos.repo = 0;
+      pool->pos.repo = data->repo;
       pool->pos.repodataid = data - data->repo->repodata;
       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
       pool->pos.schema = kv->id;
     }
 }
 
-/************************************************************************/
+/************************************************************************
+ * data iterator functions
+ */
 
 static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
   { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
@@ -771,7 +918,6 @@ static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
   { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
 };
 
-#if 1
 static inline Id *
 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
 {
@@ -822,103 +968,99 @@ solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
     }
 }
 
-void
-datamatcher_init(Datamatcher *ma, Pool *pool, const char *match, int flags)
+int
+datamatcher_init(Datamatcher *ma, const char *match, int flags)
 {
-  ma->pool = pool;
-  ma->match = (void *)match;
+  ma->match = match;
   ma->flags = flags;
   ma->error = 0;
+  ma->matchdata = 0;
   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
     {
-      ma->match = sat_calloc(1, sizeof(regex_t));
-      ma->error = regcomp((regex_t *)ma->match, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
+      ma->matchdata = sat_calloc(1, sizeof(regex_t));
+      ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
       if (ma->error)
        {
-         sat_free(ma->match);
-         ma->match = (void *)match;
+         sat_free(ma->matchdata);
          ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
        }
     }
+  return ma->error;
 }
 
 void
 datamatcher_free(Datamatcher *ma)
 {
-  if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->match)
+  if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
     {
-      regfree(ma->match);
-      ma->match = sat_free(ma->match);
+      regfree(ma->matchdata);
+      ma->matchdata = sat_free(ma->matchdata);
     }
 }
 
 int
-datamatcher_match(Datamatcher *ma, Repodata *data, Repokey *key, KeyValue *kv)
+datamatcher_match(Datamatcher *ma, const char *str)
 {
-  switch (key->type)
-    {
-    case REPOKEY_TYPE_ID:
-    case REPOKEY_TYPE_IDARRAY:
-      if (data && data->localpool)
-       kv->str = stringpool_id2str(&data->spool, kv->id);
-      else
-       kv->str = id2str(ma->pool, kv->id);
-      break;
-    case REPOKEY_TYPE_STR:
-      break;
-    case REPOKEY_TYPE_DIRSTRARRAY:
-      if (!(ma->flags & SEARCH_FILES))
-       return 0;
-      /* Put the full filename into kv->str.  */
-      kv->str = repodata_dir2str(data, kv->id, kv->str);
-      /* And to compensate for that put the "empty" directory into
-        kv->id, so that later calls to repodata_dir2str on this data
-        come up with the same filename again.  */
-      kv->id = 0;
-      break;
-    default:
-      return 0;
-    }
-  /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
-     for the others we can't know if a colon separates a kind or not.  */
-  if ((ma->flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
-    {
-      const char *s = strchr(kv->str, ':');
-      if (s)
-       kv->str = s + 1;
-    }
+  int l;
   switch ((ma->flags & SEARCH_STRINGMASK))
     {
     case SEARCH_SUBSTRING:
       if (ma->flags & SEARCH_NOCASE)
        {
-         if (!strcasestr(kv->str, (const char *)ma->match))
+         if (!strcasestr(str, ma->match))
            return 0;
        }
       else
        {
-         if (!strstr(kv->str, (const char *)ma->match))
+         if (!strstr(str, ma->match))
            return 0;
        }
       break;
     case SEARCH_STRING:
       if (ma->flags & SEARCH_NOCASE)
        {
-         if (strcasecmp((const char *)ma->match, kv->str))
+         if (strcasecmp(ma->match, str))
+           return 0;
+       }
+      else
+       {
+         if (strcmp(ma->match, str))
+           return 0;
+       }
+      break;
+    case SEARCH_STRINGSTART:
+      if (ma->flags & SEARCH_NOCASE)
+       {
+         if (strncasecmp(ma->match, str, strlen(ma->match)))
+           return 0;
+       }
+      else
+       {
+         if (strncmp(ma->match, str, strlen(ma->match)))
+           return 0;
+       }
+      break;
+    case SEARCH_STRINGEND:
+      l = strlen(str) - strlen(ma->match);
+      if (l < 0)
+       return 0;
+      if (ma->flags & SEARCH_NOCASE)
+       {
+         if (strcasecmp(ma->match, str + l))
            return 0;
        }
       else
        {
-         if (strcmp((const char *)ma->match, kv->str))
+         if (strcmp(ma->match, str + l))
            return 0;
        }
       break;
     case SEARCH_GLOB:
-      if (fnmatch((const char *)ma->match, kv->str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
+      if (fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
        return 0;
       break;
     case SEARCH_REGEX:
-      if (regexec((const regex_t *)ma->match, kv->str, 0, NULL, 0))
+      if (regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0))
        return 0;
       break;
     default:
@@ -927,21 +1069,39 @@ datamatcher_match(Datamatcher *ma, Repodata *data, Repokey *key, KeyValue *kv)
   return 1;
 }
 
+int
+repodata_filelistfilter_matches(Repodata *data, const char *str)
+{
+  /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
+  /* for now hardcoded */
+  if (strstr(str, "bin/"))
+    return 1;
+  if (!strncmp(str, "/etc/", 5))
+    return 1;
+  if (!strcmp(str, "/usr/lib/sendmail"))
+    return 1;
+  return 0;
+}
+
+
 enum {
   di_bye,
 
+  di_enterrepo,
+  di_entersolvable,
+  di_enterrepodata,
+  di_enterschema,
+  di_enterkey,
+
   di_nextattr,
   di_nextkey,
   di_nextrepodata,
   di_nextsolvable,
   di_nextrepo,
 
-  di_enterrepo,
-  di_entersolvable,
-  di_enterrepodata,
-  di_enterkey,
-
+  di_enterarray,
   di_nextarrayelement,
+
   di_entersub,
   di_leavesub,
 
@@ -950,176 +1110,302 @@ enum {
   di_entersolvablekey
 };
 
-void
-dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname, const char *match, int flags)
+/* see repo.h for documentation */
+int
+dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
 {
   memset(di, 0, sizeof(*di));
-  di->repo = repo;
-  di->keyname = keyname;
-  di->entry = p;
-  di->pool = repo->pool;
-  if (p)
-    flags |= SEARCH_THISENTRY;
-  di->flags = flags;
-  if (repo)
-    di->repoid = -1;
+  di->pool = pool;
+  di->flags = flags & ~SEARCH_THISSOLVID;
+  if (!pool || (repo && repo->pool != pool))
+    {
+      di->state = di_bye;
+      return -1;
+    }
   if (match)
-    datamatcher_init(&di->matcher, di->pool, match, flags);
-  if (p == REPOENTRY_POS)
     {
-      di->repo = di->pool->pos.repo;
-      di->data = di->repo->repodata + di->pool->pos.repodataid;
-      di->repoid = -1;
-      di->repodataid = -1;
+      int error;
+      if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
+       {
+         di->state = di_bye;
+         return error;
+       }
     }
-  di->state = di_enterrepo;
+  di->keyname = keyname;
+  di->keynames[0] = keyname;
+  dataiterator_set_search(di, repo, p);
+  return 0;
 }
 
 void
-dataiterator_free(Dataiterator *di)
+dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
 {
-  if (di->matcher.match)
-    datamatcher_free(&di->matcher);
+  *di = *from;
+  memset(&di->matcher, 0, sizeof(di->matcher));
+  if (from->matcher.match)
+    datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
+  if (di->nparents)
+    {
+      /* fix pointers */
+      int i;
+      for (i = 1; i < di->nparents; i++)
+       di->parents[i].kv.parent = &di->parents[i - 1].kv;
+      di->kv.parent = &di->parents[di->nparents - 1].kv;
+    }
 }
 
 int
-dataiterator_step(Dataiterator *di)
+dataiterator_set_match(Dataiterator *di, const char *match, int flags)
 {
-  Id schema;
-
-  for (;;)
+  di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
+  datamatcher_free(&di->matcher);
+  memset(&di->matcher, 0, sizeof(di->matcher));
+  if (match)
     {
-      switch (di->state)
+      int error;
+      if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
        {
-       case di_nextattr: di_nextattr:
-          di->kv.entry++;
-         di->ddp = data_fetch(di->ddp, &di->kv, di->key);
-         if (di->kv.eof)
-           di->state = di_nextkey;
-         else
-           di->state = di_nextattr;
-         break;
+         di->state = di_bye;
+         return error;
+       }
+    }
+  return 0;
+}
 
-       case di_nextkey: di_nextkey:
-         if (!di->keyname)
-           {
-             if (*++di->keyp)
-               goto di_enterkey;
-           }
-         else if ((di->flags & SEARCH_SUB) != 0)
-           {
-             Id *keyp = di->keyp;
-             for (keyp++; *keyp; keyp++)
-               if (di->data->keys[*keyp].name == di->keyname || 
-                   di->data->keys[*keyp].type == REPOKEY_TYPE_FIXARRAY || 
-                   di->data->keys[*keyp].type == REPOKEY_TYPE_FLEXARRAY)
-                 break;
-             if (*keyp && (di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp)) != 0)
-               {
-                 di->keyp = keyp;
-                 goto di_enterkey;
-               }
-           }
+void
+dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
+{
+  di->repo = repo;
+  di->repoid = 0;
+  di->flags &= ~SEARCH_THISSOLVID;
+  di->nparents = 0;
+  di->rootlevel = 0;
+  di->repodataid = 0;
+  if (!di->pool->nrepos)
+    {
+      di->state = di_bye;
+      return;
+    }
+  if (!repo)
+    {
+      di->repoid = 1;
+      di->repo = di->pool->repos[0];
+    }
+  di->state = di_enterrepo;
+  if (p)
+    dataiterator_jump_to_solvid(di, p);
+}
 
-         if (di->kv.parent)
-           goto di_leavesub;
-         /* FALLTHROUGH */
+void
+dataiterator_set_keyname(Dataiterator *di, Id keyname)
+{
+  di->nkeynames = 0;
+  di->keyname = keyname;
+  di->keynames[0] = keyname;
+}
 
-       case di_nextrepodata: di_nextrepodata:
-         if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
-             goto di_enterrepodata;
-         /* FALLTHROUGH */
+void
+dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
+{
+  int i;
 
-       case di_nextsolvable:
-         if (!(di->flags & SEARCH_THISENTRY))
-           {
-             if (di->entry < 0)
-               di->entry = di->repo->start;
-             else
-               di->entry++;
-             for (; di->entry < di->repo->end; di->entry++)
-               {
-                 if (di->pool->solvables[di->entry].repo == di->repo)
-                   goto di_entersolvable;
-               }
-           }
-         /* FALLTHROUGH */
+  if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
+    {
+      di->state = di_bye;      /* sorry */
+      return;
+    }
+  for (i = di->nkeynames + 1; i > 0; i--)
+    di->keynames[i] = di->keynames[i - 1];
+  di->keynames[0] = di->keyname = keyname;
+  di->nkeynames++;
+}
 
-       case di_nextrepo:
-         if (di->repoid >= 0)
-           {
-             di->repoid++;
-             if (di->repoid < di->pool->nrepos)
-               {
-                 di->repo = di->pool->repos[di->repoid];
-                 goto di_enterrepo;
-               }
-           }
+void
+dataiterator_free(Dataiterator *di)
+{
+  if (di->matcher.match)
+    datamatcher_free(&di->matcher);
+}
 
-       /* FALLTHROUGH */
-       case di_bye:
-         di->state = di_bye;
-         return 0;
+static inline unsigned char *
+dataiterator_find_keyname(Dataiterator *di, Id keyname)
+{
+  Id *keyp = di->keyp;
+  Repokey *keys = di->data->keys;
+  unsigned char *dp;
+
+  for (keyp = di->keyp; *keyp; keyp++)
+    if (keys[*keyp].name == keyname)
+      break;
+  if (!*keyp)
+    return 0;
+  dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
+  if (!dp)
+    return 0;
+  di->keyp = keyp;
+  return dp;
+}
 
+static int
+dataiterator_filelistcheck(Dataiterator *di)
+{
+  int j;
+  int needcomplete = 0;
+  Repodata *data = di->data;
+
+  if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
+    if (!di->matcher.match
+       || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
+           && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
+       || !repodata_filelistfilter_matches(di->data, di->matcher.match))
+      needcomplete = 1;
+  if (data->state != REPODATA_AVAILABLE)
+    return needcomplete ? 1 : 0;
+  for (j = 1; j < data->nkeys; j++)
+    if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+      break;
+  return j == data->nkeys && !needcomplete ? 0 : 1;
+}
+
+int
+dataiterator_step(Dataiterator *di)
+{
+  Id schema;
+
+  for (;;)
+    {
+      switch (di->state)
+       {
        case di_enterrepo: di_enterrepo:
-         if (!(di->flags & SEARCH_THISENTRY))
-           di->entry = di->repo->start;
+         if (!di->repo)
+           goto di_bye;
+         if (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS))
+           goto di_nextrepo;
+         if (!(di->flags & SEARCH_THISSOLVID))
+           {
+             di->solvid = di->repo->start - 1; /* reset solvid iterator */
+             goto di_nextsolvable;
+           }
          /* FALLTHROUGH */
 
        case di_entersolvable: di_entersolvable:
          if (di->repodataid >= 0)
            {
-             di->repodataid = 0;
-             if (di->entry > 0 && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)))
+             di->repodataid = 0;       /* reset repodata iterator */
+             if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
                {
                  di->key = solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
                  di->data = 0;
                  goto di_entersolvablekey;
                }
            }
+         /* FALLTHROUGH */
 
        case di_enterrepodata: di_enterrepodata:
          if (di->repodataid >= 0)
-           di->data = di->repo->repodata + di->repodataid;
+           {
+             if (di->repodataid >= di->repo->nrepodata)
+               goto di_nextsolvable;
+             di->data = di->repo->repodata + di->repodataid;
+           }
+         if (di->repodataid >= 0 && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
+           goto di_nextrepodata;
          if (!maybe_load_repodata(di->data, di->keyname))
            goto di_nextrepodata;
-         di->dp = entry2data(di->data, di->entry, &schema);
+         di->dp = solvid2data(di->data, di->solvid, &schema);
          if (!di->dp)
            goto di_nextrepodata;
+         if (di->solvid == SOLVID_POS)
+           di->solvid = di->pool->pos.solvid;
+         /* reset key iterator */
          di->keyp = di->data->schemadata + di->data->schemata[schema];
+         /* FALLTHROUGH */
+
+       case di_enterschema: di_enterschema:
          if (di->keyname)
+           di->dp = dataiterator_find_keyname(di, di->keyname);
+         if (!di->dp || !*di->keyp)
            {
-             Id *keyp;
-             if ((di->flags & SEARCH_SUB) != 0)
-               {
-                 di->keyp--;
-                 goto di_nextkey;
-               }
-             for (keyp = di->keyp; *keyp; keyp++)
-               if (di->data->keys[*keyp].name == di->keyname)
-                 break;
-             if (!*keyp)
-               goto di_nextrepodata;
-             di->dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
-             di->keyp = keyp;
-             if (!di->dp)
-               goto di_nextrepodata;
+             if (di->kv.parent)
+               goto di_leavesub;
+             goto di_nextrepodata;
            }
+         /* FALLTHROUGH */
 
        case di_enterkey: di_enterkey:
          di->kv.entry = -1;
          di->key = di->data->keys + *di->keyp;
-         di->ddp = get_data(di->data, di->key, &di->dp);
+         di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
          if (!di->ddp)
            goto di_nextkey;
+          if (di->key->type == REPOKEY_TYPE_DELETED)
+           goto di_nextkey;
          if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
+           goto di_enterarray;
+         if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+           goto di_nextkey;
+         /* FALLTHROUGH */
+
+       case di_nextattr:
+          di->kv.entry++;
+         di->ddp = data_fetch(di->ddp, &di->kv, di->key);
+         if (di->kv.eof)
+           di->state = di_nextkey;
+         else
+           di->state = di_nextattr;
+         break;
+
+       case di_nextkey: di_nextkey:
+         if (!di->keyname && *++di->keyp)
+           goto di_enterkey;
+         if (di->kv.parent)
+           goto di_leavesub;
+         /* FALLTHROUGH */
+
+       case di_nextrepodata: di_nextrepodata:
+         if (di->repodataid >= 0 && ++di->repodataid < di->repo->nrepodata)
+             goto di_enterrepodata;
+         /* FALLTHROUGH */
+
+       case di_nextsolvable: di_nextsolvable:
+         if (!(di->flags & SEARCH_THISSOLVID))
+           {
+             if (di->solvid < 0)
+               di->solvid = di->repo->start;
+             else
+               di->solvid++;
+             for (; di->solvid < di->repo->end; di->solvid++)
+               {
+                 if (di->pool->solvables[di->solvid].repo == di->repo)
+                   goto di_entersolvable;
+               }
+           }
+         /* FALLTHROUGH */
+
+       case di_nextrepo: di_nextrepo:
+         if (di->repoid > 0)
            {
-             di->ddp = data_read_id(di->ddp, &di->kv.num);
-             di->kv.entry = -1;
-             di->kv.eof = 0;
-             goto di_nextarrayelement;
+             di->repoid++;
+             di->repodataid = 0;
+             if (di->repoid - 1 < di->pool->nrepos)
+               {
+                 di->repo = di->pool->repos[di->repoid - 1];
+                 goto di_enterrepo;
+               }
            }
-         goto di_nextattr;
+       /* FALLTHROUGH */
+
+       case di_bye: di_bye:
+         di->state = di_bye;
+         return 0;
+
+       case di_enterarray: di_enterarray:
+         if (di->key->name == REPOSITORY_SOLVABLES)
+           goto di_nextkey;
+         di->ddp = data_read_id(di->ddp, &di->kv.num);
+         di->kv.eof = 0;
+         di->kv.entry = -1;
+         /* FALLTHROUGH */
 
        case di_nextarrayelement: di_nextarrayelement:
          di->kv.entry++;
@@ -1127,17 +1413,21 @@ dataiterator_step(Dataiterator *di)
            di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
          if (di->kv.entry == di->kv.num)
            {
-             if (di->keyname && di->key->name != di->keyname)
+             if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+               goto di_nextkey;
+             if (!(di->flags & SEARCH_ARRAYSENTINEL))
                goto di_nextkey;
              di->kv.str = (char *)di->ddp;
-             di->kv.eof = 1;
+             di->kv.eof = 2;
              di->state = di_nextkey;
              break;
            }
+         if (di->kv.entry == di->kv.num - 1)
+           di->kv.eof = 1;
          if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
            di->ddp = data_read_id(di->ddp, &di->kv.id);
          di->kv.str = (char *)di->ddp;
-         if (di->keyname && di->key->name != di->keyname)
+         if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
            goto di_entersub;
          if ((di->flags & SEARCH_SUB) != 0)
            di->state = di_entersub;
@@ -1156,16 +1446,19 @@ dataiterator_step(Dataiterator *di)
          memset(&di->kv, 0, sizeof(di->kv));
          di->kv.parent = &di->parents[di->nparents].kv;
          di->nparents++;
-         di->keyp--;
-         goto di_nextkey;
-         
+         di->keyname = di->keynames[di->nparents - di->rootlevel];
+         goto di_enterschema;
+
        case di_leavesub: di_leavesub:
+         if (di->nparents - 1 < di->rootlevel)
+           goto di_bye;
          di->nparents--;
          di->dp = di->parents[di->nparents].dp;
          di->kv = di->parents[di->nparents].kv;
          di->keyp = di->parents[di->nparents].keyp;
          di->key = di->data->keys + *di->keyp;
          di->ddp = (unsigned char *)di->kv.str;
+         di->keyname = di->keynames[di->nparents - di->rootlevel];
          goto di_nextarrayelement;
 
         /* special solvable attr handling follows */
@@ -1187,12 +1480,13 @@ dataiterator_step(Dataiterator *di)
          /* FALLTHROUGH */
 
        case di_entersolvablekey: di_entersolvablekey:
-         di->idp = solvabledata_fetch(di->pool->solvables + di->entry, &di->kv, di->key->name);
+         di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
          if (!di->idp || !di->idp[0])
            goto di_nextsolvablekey;
          di->kv.id = di->idp[0];
          di->kv.num = di->idp[0];
-         if (!di->kv.eof && !di->idp[1])
+         di->idp++;
+         if (!di->kv.eof && !di->idp[0])
            di->kv.eof = 1;
          di->kv.entry = 0;
          if (di->kv.eof)
@@ -1203,588 +1497,238 @@ dataiterator_step(Dataiterator *di)
        }
 
       if (di->matcher.match)
-       if (!datamatcher_match(&di->matcher, di->data, di->key, &di->kv))
-         continue;
+       {
+         /* simple pre-check so that we don't need to stringify */
+         if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && di->matcher.match && (di->matcher.flags & (SEARCH_FILES|SEARCH_NOCASE|SEARCH_STRINGMASK)) == (SEARCH_FILES|SEARCH_STRING))
+           {
+             int l = strlen(di->matcher.match) - strlen(di->kv.str);
+             if (l < 0 || strcmp(di->matcher.match + l, di->kv.str))
+               continue;
+           }
+         if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
+           {
+             if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
+               return 1;
+             continue;
+           }
+         if (!datamatcher_match(&di->matcher, di->kv.str))
+           continue;
+       }
       /* found something! */
       return 1;
     }
 }
 
 void
-dataiterator_setpos(Dataiterator *di)
-{
-  di->pool->pos.repo = di->repo;
-  di->pool->pos.repodataid = di->data - di->repo->repodata;
-  di->pool->pos.schema = di->kv.id;
-  di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
-}
-
-void
-dataiterator_skip_attribute(Dataiterator *di)
+dataiterator_entersub(Dataiterator *di)
 {
-  if (di->state == di_nextsolvableattr)
-    di->state = di_nextsolvablekey;
-  else
-    di->state = di_nextkey;
+  if (di->state == di_nextarrayelement)
+    di->state = di_entersub;
 }
 
 void
-dataiterator_skip_solvable(Dataiterator *di)
-{
-  di->state = di_nextsolvable;
-}
-
-void
-dataiterator_skip_repo(Dataiterator *di)
-{
-  di->state = di_nextrepo;
-}
-
-void
-dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
-{
-  di->repo = s->repo;
-  di->repoid = -1;
-  di->entry = s - di->pool->solvables;
-  di->state = di_entersolvable;
-}
-
-void
-dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
-{
-  di->repo = repo;
-  di->repoid = -1;
-  di->state = di_enterrepo;
-}
-
-int
-dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
-{
-  Datamatcher matcher = di->matcher;
-  matcher.flags = flags;
-  matcher.match = (void *)vmatch;
-  return datamatcher_match(&matcher, di->data, di->key, &di->kv);
-}
-
-#else
-
-/************************************************************************
- * data search iterator
- */
-
-static void
-dataiterator_newdata(Dataiterator *di)
+dataiterator_setpos(Dataiterator *di)
 {
-  Id keyname = di->keyname;
-  Repodata *data = di->data;
-  di->nextkeydp = 0;
-
-  if (data->state == REPODATA_STUB)
-    {
-      if (keyname)
-       {
-         int j;
-         for (j = 1; j < data->nkeys; j++)
-           if (keyname == data->keys[j].name)
-             break;
-         if (j == data->nkeys)
-           return;
-       }
-      /* load it */
-      if (data->loadcallback)
-       data->loadcallback(data);
-      else
-       data->state = REPODATA_ERROR;
-    }
-  if (data->state == REPODATA_ERROR)
-    return;
-
-  Id schema;
-  unsigned char *dp = data->incoredata;
-  if (!dp)
-    return;
-  if (di->solvid >= 0)
-    dp += data->incoreoffset[di->solvid - data->start];
-  dp = data_read_id(dp, &schema);
-  Id *keyp = data->schemadata + data->schemata[schema];
-  if (keyname)
+  if (di->kv.eof == 2)
     {
-      Id k, *kp;
-      /* search in a specific key */
-      for (kp = keyp; (k = *kp++) != 0; )
-       if (data->keys[k].name == keyname)
-         break;
-      if (k == 0)
-       return;
-      dp = forward_to_key(data, k, keyp, dp);
-      if (!dp)
-       return;
-      keyp = kp - 1;
+      pool_clear_pos(di->pool);
+      return;
     }
-  Id keyid = *keyp++;
-  if (!keyid)
-    return;
-
-  di->data = data;
-  di->key = di->data->keys + keyid;
-  di->keyp = keyp;
-  di->dp = 0;
-
-  di->nextkeydp = dp;
-  di->dp = get_data(di->data, di->key, &di->nextkeydp);
-  di->kv.eof = 0;
+  di->pool->pos.solvid = di->solvid;
+  di->pool->pos.repo = di->repo;
+  di->pool->pos.repodataid = di->data - di->repo->repodata;
+  di->pool->pos.schema = di->kv.id;
+  di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
 }
 
 void
-dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
-                 const char *match, int flags)
+dataiterator_setpos_parent(Dataiterator *di)
 {
-  di->flags = flags;
-  if (p > 0)
+  if (!di->kv.parent || di->kv.parent->eof == 2)
     {
-      di->solvid = p;
-      di->flags |= __SEARCH_ONESOLVABLE;
-      di->data = repo->repodata - 1;
-      if (flags & SEARCH_NO_STORAGE_SOLVABLE)
-       di->state = 0;
-      else
-       di->state = 1;
-    }
-  else
-    {
-      di->solvid = repo->start - 1;
-      if (di->solvid < 0)
-       {
-         fprintf(stderr, "A repo contains the NULL solvable!\n");
-         exit(1);
-       }
-      di->data = repo->repodata + repo->nrepodata - 1;
-      di->state = 0;
-    }
-
-  di->match = match;
-  if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
-    {
-      if (di->match)
-        {
-          /* We feed multiple lines eventually (e.g. authors or descriptions),
-             so set REG_NEWLINE. */
-          di->regex_err =
-            regcomp(&di->regex, di->match,
-              REG_EXTENDED | REG_NOSUB | REG_NEWLINE
-              | ((di->flags & SEARCH_NOCASE) ? REG_ICASE : 0));
-#if 0
-          if (di->regex_err != 0)
-            {
-              fprintf(stderr, "Given regex failed to compile: %s\n", di->match);
-              fprintf(stderr, "regcomp error code: %d\n", di->regex_err);
-              exit(1);
-            }
-#else
-        }
-      else
-        {
-          di->flags |= (di->flags & SEARCH_STRINGMASK) | SEARCH_STRING;
-          di->regex_err = 0;
-#endif
-        }
+      pool_clear_pos(di->pool);
+      return;
     }
-
-  di->keyname = keyname;
-  static Id zeroid = 0;
-  di->keyp = &zeroid;
-  di->kv.eof = 1;
-  di->repo = repo;
-  di->idp = 0;
-  di->subkeyp = 0;
+  di->pool->pos.solvid = di->solvid;
+  di->pool->pos.repo = di->repo;
+  di->pool->pos.repodataid = di->data - di->repo->repodata;
+  di->pool->pos.schema = di->kv.parent->id;
+  di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
 }
 
-/* FIXME factor and merge with repo_matchvalue */
-static int
-dataiterator_match_int_real(Dataiterator *di, int flags, const void *vmatch)
-{
-  KeyValue *kv = &di->kv;
-  const char *match = vmatch;
-  if ((flags & SEARCH_STRINGMASK) != 0)
+/* clones just the position, not the search keys/matcher */
+void
+dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
+{
+  di->state = from->state;
+  di->flags &= ~SEARCH_THISSOLVID;
+  di->flags |= (from->flags & SEARCH_THISSOLVID);
+  di->repo = from->repo;
+  di->data = from->data;
+  di->dp = from->dp;
+  di->ddp = from->ddp;
+  di->idp = from->idp;
+  di->keyp = from->keyp;
+  di->key = from->key;
+  di->kv = from->kv;
+  di->repodataid = from->repodataid;
+  di->solvid = from->solvid;
+  di->repoid = from->repoid;
+  di->rootlevel = from->rootlevel;
+  memcpy(di->parents, from->parents, sizeof(from->parents));
+  di->nparents = from->nparents;
+  if (di->nparents)
     {
-      switch (di->key->type)
-       {
-       case REPOKEY_TYPE_ID:
-       case REPOKEY_TYPE_IDARRAY:
-         if (di->data && di->data->localpool)
-           kv->str = stringpool_id2str(&di->data->spool, kv->id);
-         else
-           kv->str = id2str(di->repo->pool, kv->id);
-         break;
-       case REPOKEY_TYPE_STR:
-         break;
-       case REPOKEY_TYPE_DIRSTRARRAY:
-         if (!(flags & SEARCH_FILES))
-           return 0;
-         /* Put the full filename into kv->str.  */
-         kv->str = repodata_dir2str(di->data, kv->id, kv->str);
-         /* And to compensate for that put the "empty" directory into
-            kv->id, so that later calls to repodata_dir2str on this data
-            come up with the same filename again.  */
-         kv->id = 0;
-         break;
-       default:
-         return 0;
-       }
-      /* Maybe skip the kind specifier.  Do this only for SOLVABLE attributes,
-         for the others we can't know if a colon separates a kind or not.  */
-      if ((flags & SEARCH_SKIP_KIND)
-         && di->key->storage == KEY_STORAGE_SOLVABLE)
-       {
-         const char *s = strchr(kv->str, ':');
-         if (s)
-           kv->str = s + 1;
-       }
-      switch ((flags & SEARCH_STRINGMASK))
-       {
-         case SEARCH_SUBSTRING:
-           if (flags & SEARCH_NOCASE)
-             {
-               if (!strcasestr(kv->str, match))
-                 return 0;
-             }
-           else
-             {
-               if (!strstr(kv->str, match))
-                 return 0;
-             }
-           break;
-         case SEARCH_STRING:
-           if (flags & SEARCH_NOCASE)
-             {
-               if (strcasecmp(match, kv->str))
-                 return 0;
-             }
-           else
-             {
-               if (strcmp(match, kv->str))
-                 return 0;
-             }
-           break;
-         case SEARCH_GLOB:
-           if (fnmatch(match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
-             return 0;
-           break;
-         case SEARCH_REGEX:
-           if (regexec((const regex_t *)vmatch, kv->str, 0, NULL, 0))
-             return 0;
-           break;
-         default:
-           return 0;
-       }
+      int i;
+      for (i = 1; i < di->nparents; i++)
+       di->parents[i].kv.parent = &di->parents[i - 1].kv;
+      di->kv.parent = &di->parents[di->nparents - 1].kv;
     }
-  return 1;
 }
-
-static int
-dataiterator_match_int(Dataiterator *di)
-{
-  if ((di->flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
-    return dataiterator_match_int_real(di, di->flags, &di->regex);
-  else
-    return dataiterator_match_int_real(di, di->flags, di->match);
-}
-
-int
-dataiterator_match(Dataiterator *di, int flags, const void *vmatch)
-{
-  return dataiterator_match_int_real(di, flags, vmatch);
-}
-
-int
-dataiterator_step(Dataiterator *di)
-{
-restart:
-  while (1)
-    {
-      if (di->state)
-       {
-         /* we're stepping through solvable data, 1 -> SOLVABLE_NAME... */
-         if (di->idp)
-           {
-             /* we're stepping through an id array */
-             Id *idp = di->idp;
-             if (*idp)
-               {
-                 di->kv.id = *idp;
-                 di->idp++;
-                 di->kv.eof = idp[1] ? 0 : 1;
-                 goto weg2;
-               }
-             else
-               di->idp = 0;
-           }
-         Solvable *s = di->repo->pool->solvables + di->solvid;
-         int state = di->state;
-         di->key = solvablekeys + state - 1;
-         if (di->keyname)
-           di->state = RPM_RPMDBID;
-         else
-           di->state++;
-         if (state == 1)
-           {
-             di->data = 0;
-             if (di->keyname)
-               state = di->keyname - 1;
-           }
-         switch (state + 1)
-           {
-             case SOLVABLE_NAME:
-               if (!s->name)
-                 continue;
-               di->kv.id = s->name;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_ARCH:
-               if (!s->arch)
-                 continue;
-               di->kv.id = s->arch;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_EVR:
-               if (!s->evr)
-                 continue;
-               di->kv.id = s->evr;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_VENDOR:
-               if (!s->vendor)
-                 continue;
-               di->kv.id = s->vendor;
-               di->kv.eof = 1;
-               break;
-             case SOLVABLE_PROVIDES:
-               di->idp = s->provides
-                   ? di->repo->idarraydata + s->provides : 0;
-               continue;
-             case SOLVABLE_OBSOLETES:
-               di->idp = s->obsoletes
-                   ? di->repo->idarraydata + s->obsoletes : 0;
-               continue;
-             case SOLVABLE_CONFLICTS:
-               di->idp = s->conflicts
-                   ? di->repo->idarraydata + s->conflicts : 0;
-               continue;
-             case SOLVABLE_REQUIRES:
-               di->idp = s->requires
-                   ? di->repo->idarraydata + s->requires : 0;
-               continue;
-             case SOLVABLE_RECOMMENDS:
-               di->idp = s->recommends
-                   ? di->repo->idarraydata + s->recommends : 0;
-               continue;
-             case SOLVABLE_SUPPLEMENTS:
-               di->idp = s->supplements
-                   ? di->repo->idarraydata + s->supplements : 0;
-               continue;
-             case SOLVABLE_SUGGESTS:
-               di->idp = s->suggests
-                   ? di->repo->idarraydata + s->suggests : 0;
-               continue;
-             case SOLVABLE_ENHANCES:
-               di->idp = s->enhances
-                   ? di->repo->idarraydata + s->enhances : 0;
-               continue;
-             case RPM_RPMDBID:
-               if (!di->repo->rpmdbid)
-                 continue;
-               di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
-               di->kv.eof = 1;
-               break;
-             default:
-               di->data = di->repo->repodata - 1;
-               di->kv.eof = 1;
-               di->state = 0;
-               continue;
-           }
-       }
-      else if (di->subkeyp)
-       {
-         Id keyid;
-         if (!di->subnum)
-           {
-             /* Send end-of-substruct.  We are here only when we saw a
-                _COUNTED key one level up.  Since then we didn't increment
-                ->keyp, so it still can be found at keyp[-1].  */
-             di->kv.eof = 2;
-             di->key = di->data->keys + di->keyp[-1];
-             di->subkeyp = 0;
-           }
-         else if (!(keyid = *di->subkeyp++))
-           {
-             /* Send end-of-element.  See above for keyp[-1].  */
-             di->kv.eof = 1;
-             di->key = di->data->keys + di->keyp[-1];
-             if (di->subschema)
-               di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
-             else
-               {
-                 di->dp = data_read_id(di->dp, &di->subschema);
-                 di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
-                 di->subschema = 0;
-               }
-             di->subnum--;
-           }
-         else
-           {
-             di->key = di->data->keys + keyid;
-             di->dp = data_fetch(di->dp, &di->kv, di->key);
-             if (!di->dp)
-               exit(1);
-           }
+
+void
+dataiterator_seek(Dataiterator *di, int whence)
+{
+  if ((whence & DI_SEEK_STAY) != 0)
+    di->rootlevel = di->nparents;
+  switch (whence & ~DI_SEEK_STAY)
+    {
+    case DI_SEEK_CHILD:
+      if (di->state != di_nextarrayelement)
+       break;
+      if ((whence & DI_SEEK_STAY) != 0)
+       di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
+      di->state = di_entersub;
+      break;
+    case DI_SEEK_PARENT:
+      if (!di->nparents)
+       {
+         di->state = di_bye;
+         break;
        }
-      else
+      di->nparents--;
+      if (di->rootlevel > di->nparents)
+       di->rootlevel = di->nparents;
+      di->dp = di->parents[di->nparents].dp;
+      di->kv = di->parents[di->nparents].kv;
+      di->keyp = di->parents[di->nparents].keyp;
+      di->key = di->data->keys + *di->keyp;
+      di->ddp = (unsigned char *)di->kv.str;
+      di->keyname = di->keynames[di->nparents - di->rootlevel];
+      di->state = di_nextarrayelement;
+      break;
+    case DI_SEEK_REWIND:
+      if (!di->nparents)
        {
-         if (di->kv.eof)
-           di->dp = 0;
-         else
-           di->dp = data_fetch(di->dp, &di->kv, di->key);
-
-         while (!di->dp)
-           {
-             Id keyid;
-             if (di->keyname || !(keyid = *di->keyp++))
-               {
-                 while (1)
-                   {
-                     Repo *repo = di->repo;
-                     Repodata *data = ++di->data;
-                     if (data >= repo->repodata + repo->nrepodata)
-                       {
-                         if (di->flags & __SEARCH_ONESOLVABLE)
-                           return 0;
-                         if (di->solvid >= 0)
-                           {
-                             while (++di->solvid < repo->end)
-                               if (repo->pool->solvables[di->solvid].repo == repo)
-                                 break;
-                             if (di->solvid >= repo->end)
-                               {
-                                 if (!(di->flags & SEARCH_EXTRA))
-                                   goto skiprepo;
-                                 goto skiprepo;
-                               }
-                           }
-                         else
-                           {
-                               {
-skiprepo:;
-                                 Pool *pool = di->repo->pool;
-                                 if (!(di->flags & SEARCH_ALL_REPOS)
-                                     || di->repo == pool->repos[pool->nrepos - 1])
-                                   return 0;
-                                 int i;
-                                 for (i = 0; i < pool->nrepos; i++)
-                                   if (di->repo == pool->repos[i])
-                                     break;
-                                 di->repo = pool->repos[i + 1];
-                                 dataiterator_init(di, di->repo, 0, di->keyname, di->match, di->flags);
-                                 continue;
-                               }
-                           }
-                         di->data = repo->repodata - 1;
-                         if ((di->flags & SEARCH_NO_STORAGE_SOLVABLE))
-                           continue;
-                         static Id zeroid = 0;
-                         di->keyp = &zeroid;
-                         di->state = 1;
-                         goto restart;
-                       }
-                     if ((di->solvid >= 0 && di->solvid >= data->start && di->solvid < data->end))
-                       {
-                         dataiterator_newdata(di);
-                         if (di->nextkeydp)
-                           break;
-                       }
-                   }
-               }
-             else
-               {
-                 di->key = di->data->keys + keyid;
-                 di->dp = get_data(di->data, di->key, &di->nextkeydp);
-               }
-             di->dp = data_fetch(di->dp, &di->kv, di->key);
-           }
-         if (di->key->type == REPOKEY_TYPE_FIXARRAY)
-           {
-             di->subnum = di->kv.num;
-             di->subschema = di->kv.id;
-             di->kv.eof = 0;
-             di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
-           }
-         if (di->key->type == REPOKEY_TYPE_FLEXARRAY)
-           {
-             di->subnum = di->kv.num;
-             di->kv.eof = 0;
-             di->dp = data_read_id(di->dp, &di->subschema);
-             di->subkeyp = di->data->schemadata + di->data->schemata[di->subschema];
-             di->subschema = 0;
-           }
+         di->state = di_bye;
+         break;
        }
-weg2:
-      if (!di->match
-         || dataiterator_match_int(di))
-       break;
+      di->dp = (unsigned char *)di->kv.parent->str;
+      di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
+      di->state = di_enterschema;
+      break;
+    default:
+      break;
     }
-  return 1;
 }
 
 void
 dataiterator_skip_attribute(Dataiterator *di)
 {
-  if (di->state)
-    di->idp = 0;
-  /* This will make the next _step call to retrieve the next field.  */
-  di->kv.eof = 1;
+  if (di->state == di_nextsolvableattr)
+    di->state = di_nextsolvablekey;
+  else
+    di->state = di_nextkey;
 }
 
 void
 dataiterator_skip_solvable(Dataiterator *di)
 {
-  /* We're done with this field.  */
-  di->kv.eof = 1;
-  /* And with solvable data.  */
-  di->state = 0;
-  /* And with all keys for this repodata and thing. */
-  static Id zeroid = 0;
-  di->keyp = &zeroid;
-  /* And with all repodatas for this thing.  */
-  di->data = di->repo->repodata + di->repo->nrepodata - 1;
-  /* Hence the next call to _step will retrieve the next thing.  */
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
+  di->keyname = di->keynames[0];
+  di->state = di_nextsolvable;
 }
 
 void
 dataiterator_skip_repo(Dataiterator *di)
 {
-  dataiterator_skip_solvable(di);
-  /* We're done with all solvables and all extra things for this repo.  */
-  di->solvid = -1;
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
+  di->keyname = di->keynames[0];
+  di->state = di_nextrepo;
 }
 
 void
-dataiterator_jump_to_solvable(Dataiterator *di, Solvable *s)
+dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
 {
-  di->repo = s->repo;
-  /* Simulate us being done with the solvable before the requested one.  */
-  dataiterator_skip_solvable(di);
-  di->solvid = s - s->repo->pool->solvables;
-  di->solvid--;
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
+  di->keyname = di->keynames[0];
+  if (solvid == SOLVID_POS)
+    {
+      di->repo = di->pool->pos.repo;
+      if (!di->repo)
+       {
+         di->state = di_bye;
+         return;
+       }
+      di->repoid = 0;
+      di->data = di->repo->repodata + di->pool->pos.repodataid;
+      di->repodataid = -1;
+      di->solvid = solvid;
+      di->state = di_enterrepo;
+      di->flags |= SEARCH_THISSOLVID;
+      return;
+    }
+  if (solvid > 0)
+    {
+      di->repo = di->pool->solvables[solvid].repo;
+      di->repoid = 0;
+    }
+  else if (di->repoid > 0)
+    {
+      if (!di->pool->nrepos)
+       {
+         di->state = di_bye;
+         return;
+       }
+      di->repoid = 1;
+      di->repo = di->pool->repos[0];
+    }
+  di->repodataid = 0;
+  di->solvid = solvid;
+  if (solvid)
+    di->flags |= SEARCH_THISSOLVID;
+  di->state = di_enterrepo;
 }
 
 void
 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
 {
+  di->nparents = 0;
+  di->kv.parent = 0;
+  di->rootlevel = 0;
   di->repo = repo;
-  dataiterator_skip_solvable(di);
-  di->solvid = repo->start - 1;
+  di->repoid = 0;      /* 0 means stay at repo */
+  di->repodataid = 0;
+  di->solvid = 0;
+  di->flags &= ~SEARCH_THISSOLVID;
+  di->state = di_enterrepo;
 }
 
-#endif
+int
+dataiterator_match(Dataiterator *di, Datamatcher *ma)
+{
+  if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
+    return 0;
+  if (!ma)
+    return 1;
+  return datamatcher_match(ma, di->kv.str);
+}
 
 /************************************************************************
  * data modify functions
@@ -1802,8 +1746,8 @@ repodata_extend(Repodata *data, Id p)
       int new = p - data->end + 1;
       if (data->attrs)
        {
-         data->attrs = sat_extend(data->attrs, old, new, sizeof(Id), REPODATA_BLOCK);
-         memset(data->attrs + old, 0, new * sizeof(Id));
+         data->attrs = sat_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
+         memset(data->attrs + old, 0, new * sizeof(Id *));
        }
       data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
       memset(data->incoreoffset + old, 0, new * sizeof(Id));
@@ -1815,9 +1759,9 @@ repodata_extend(Repodata *data, Id p)
       int new = data->start - p;
       if (data->attrs)
        {
-         data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id), REPODATA_BLOCK);
-         memmove(data->attrs + new, data->attrs, old * sizeof(Id));
-         memset(data->attrs, 0, new * sizeof(Id));
+         data->attrs = sat_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
+         memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
+         memset(data->attrs, 0, new * sizeof(Id *));
        }
       data->incoreoffset = sat_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
@@ -1826,6 +1770,38 @@ repodata_extend(Repodata *data, Id p)
     }
 }
 
+/* shrink end of repodata */
+void
+repodata_shrink(Repodata *data, int end)
+{
+  int i;
+
+  if (data->end <= end)
+    return;
+  if (data->start >= end)
+    {
+      if (data->attrs)
+       {
+         for (i = 0; i < data->end - data->start; i++)
+           sat_free(data->attrs[i]);
+          data->attrs = sat_free(data->attrs);
+       }
+      data->incoreoffset = sat_free(data->incoreoffset);
+      data->start = data->end = 0;
+      return;
+    }
+  if (data->attrs)
+    {
+      for (i = end; i < data->end; i++)
+       sat_free(data->attrs[i - data->start]);
+      data->attrs = sat_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
+    }
+  if (data->incoreoffset)
+    data->incoreoffset = sat_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
+  data->end = end;
+}
+
+/* extend repodata so that it includes solvables from start to start + num - 1 */
 void
 repodata_extend_block(Repodata *data, Id start, Id num)
 {
@@ -1845,7 +1821,8 @@ repodata_extend_block(Repodata *data, Id start, Id num)
 
 /**********************************************************************/
 
-#define REPODATA_ATTRS_BLOCK 63
+
+#define REPODATA_ATTRS_BLOCK 31
 #define REPODATA_ATTRDATA_BLOCK 1023
 #define REPODATA_ATTRIDDATA_BLOCK 63
 
@@ -1856,7 +1833,7 @@ repodata_new_handle(Repodata *data)
   if (!data->nxattrs)
     {
       data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
-      data->nxattrs = 2;
+      data->nxattrs = 2;       /* -1: SOLVID_META */
     }
   data->xattrs = sat_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
   data->xattrs[data->nxattrs] = 0;
@@ -1866,16 +1843,15 @@ repodata_new_handle(Repodata *data)
 static inline Id **
 repodata_get_attrp(Repodata *data, Id handle)
 {
-  if (handle == REPOENTRY_META)
+  if (handle < 0)
     {
-      if (!data->xattrs)
+      if (handle == SOLVID_META && !data->xattrs)
        {
          data->xattrs = sat_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
           data->nxattrs = 2;
        }
+      return data->xattrs - handle;
     }
-  if (handle < 0)
-    return data->xattrs - handle;
   if (handle < data->start || handle >= data->end)
     repodata_extend(data, handle);
   if (!data->attrs)
@@ -1895,14 +1871,14 @@ repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite
   i = 0;
   if (ap)
     {
+      /* Determine equality based on the name only, allows us to change
+         type (when overwrite is set), and makes TYPE_CONSTANT work.  */
       for (pp = ap; *pp; pp += 2)
-       /* Determine equality based on the name only, allows us to change
-          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
         if (data->keys[*pp].name == data->keys[keyid].name)
           break;
       if (*pp)
         {
-         if (overwrite)
+         if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
            {
              pp[0] = keyid;
               pp[1] = val;
@@ -1920,88 +1896,88 @@ repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite
 }
 
 
-void
-repodata_set(Repodata *data, Id handle, Repokey *key, Id val)
+static void
+repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
 {
   Id keyid;
 
   keyid = repodata_key2id(data, key, 1);
-  repodata_insert_keyid(data, handle, keyid, val, 1);
+  repodata_insert_keyid(data, solvid, keyid, val, 1);
 }
 
 void
-repodata_set_id(Repodata *data, Id handle, Id keyname, Id id)
+repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_ID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, id);
+  repodata_set(data, solvid, &key, id);
 }
 
 void
-repodata_set_num(Repodata *data, Id handle, Id keyname, unsigned int num)
+repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_NUM;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, (Id)num);
+  repodata_set(data, solvid, &key, (Id)num);
 }
 
 void
-repodata_set_poolstr(Repodata *data, Id handle, Id keyname, const char *str)
+repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
 {
   Repokey key;
   Id id;
   if (data->localpool)
     id = stringpool_str2id(&data->spool, str, 1);
   else
-    id = str2id(data->repo->pool, str, 1);
+    id = pool_str2id(data->repo->pool, str, 1);
   key.name = keyname;
   key.type = REPOKEY_TYPE_ID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, id);
+  repodata_set(data, solvid, &key, id);
 }
 
 void
-repodata_set_constant(Repodata *data, Id handle, Id keyname, unsigned int constant)
+repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_CONSTANT;
   key.size = constant;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, 0);
+  repodata_set(data, solvid, &key, 0);
 }
 
 void
-repodata_set_constantid(Repodata *data, Id handle, Id keyname, Id id)
+repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_CONSTANTID;
   key.size = id;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, 0);
+  repodata_set(data, solvid, &key, 0);
 }
 
 void
-repodata_set_void(Repodata *data, Id handle, Id keyname)
+repodata_set_void(Repodata *data, Id solvid, Id keyname)
 {
   Repokey key;
   key.name = keyname;
   key.type = REPOKEY_TYPE_VOID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  repodata_set(data, handle, &key, 0);
+  repodata_set(data, solvid, &key, 0);
 }
 
 void
-repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
+repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
 {
   Repokey key;
   int l;
@@ -2013,16 +1989,48 @@ repodata_set_str(Repodata *data, Id handle, Id keyname, const char *str)
   key.storage = KEY_STORAGE_INCORE;
   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
   memcpy(data->attrdata + data->attrdatalen, str, l);
-  repodata_set(data, handle, &key, data->attrdatalen);
+  repodata_set(data, solvid, &key, data->attrdatalen);
   data->attrdatalen += l;
 }
 
+void
+repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
+{
+  Repokey key;
+  unsigned char *dp;
+
+  key.name = keyname;
+  key.type = REPOKEY_TYPE_BINARY;
+  key.size = 0;
+  key.storage = KEY_STORAGE_INCORE;
+  data->attrdata = sat_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
+  dp = data->attrdata + data->attrdatalen;
+  if (len >= (1 << 14))
+    {
+      if (len >= (1 << 28))
+        *dp++ = (len >> 28) | 128;
+      if (len >= (1 << 21))
+        *dp++ = (len >> 21) | 128;
+      *dp++ = (len >> 14) | 128;
+    }
+  if (len >= (1 << 7))
+    *dp++ = (len >> 7) | 128;
+  *dp++ = len & 127;
+  if (len)
+    memcpy(dp, buf, len);
+  repodata_set(data, solvid, &key, data->attrdatalen);
+  data->attrdatalen = dp + len - data->attrdata;
+}
+
+/* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
+ * so that the caller can append entrysize new elements plus the termination zero there */
 static void
 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
 {
   int oldsize;
   Id *ida, *pp, **ppp;
 
+  /* check if it is the same as last time, this speeds things up a lot */
   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
     {
       /* great! just append the new data */
@@ -2031,23 +2039,30 @@ repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrys
       data->lastdatalen += entrysize;
       return;
     }
+
   ppp = repodata_get_attrp(data, handle);
   pp = *ppp;
   if (pp)
-    for (; *pp; pp += 2)
-      if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
-        break;
-  if (!pp || !*pp)
+    {
+      for (; *pp; pp += 2)
+        if (data->keys[*pp].name == keyname)
+          break;
+    }
+  if (!pp || !*pp || data->keys[*pp].type != keytype)
     {
       /* not found. allocate new key */
       Repokey key;
+      Id keyid;
       key.name = keyname;
       key.type = keytype;
       key.size = 0;
       key.storage = KEY_STORAGE_INCORE;
       data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
-      repodata_set(data, handle, &key, data->attriddatalen);
-      data->lasthandle = 0;    /* next time... */
+      keyid = repodata_key2id(data, &key, 1);
+      repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
+      data->lasthandle = handle;
+      data->lastkey = keyid;
+      data->lastdatalen = data->attriddatalen + entrysize + 1;
       return;
     }
   oldsize = 0;
@@ -2072,30 +2087,14 @@ repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrys
   data->lastdatalen = data->attriddatalen + entrysize + 1;
 }
 
-static inline int
-checksumtype2len(Id type)
-{
-  switch (type)
-    {
-    case REPOKEY_TYPE_MD5:
-      return SIZEOF_MD5;
-    case REPOKEY_TYPE_SHA1:
-      return SIZEOF_SHA1;
-    case REPOKEY_TYPE_SHA256:
-      return SIZEOF_SHA256;
-    default:
-      return 0;
-    }
-}
-
 void
-repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
+repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
                      const unsigned char *str)
 {
   Repokey key;
-  int l = checksumtype2len(type);
+  int l;
 
-  if (!l)
+  if (!(l = sat_chksum_len(type)))
     return;
   key.name = keyname;
   key.type = type;
@@ -2103,90 +2102,140 @@ repodata_set_bin_checksum(Repodata *data, Id handle, Id keyname, Id type,
   key.storage = KEY_STORAGE_INCORE;
   data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
   memcpy(data->attrdata + data->attrdatalen, str, l);
-  repodata_set(data, handle, &key, data->attrdatalen);
+  repodata_set(data, solvid, &key, data->attrdatalen);
   data->attrdatalen += l;
 }
 
-static int
-hexstr2bytes(unsigned char *buf, const char *str, int buflen)
-{
-  int i;
-  for (i = 0; i < buflen; i++)
-    {
-#define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')     \
-               : ((c)>='a' && (c)<='f') ? ((c)-'a'+10) \
-               : ((c)>='A' && (c)<='F') ? ((c)-'A'+10) \
-               : -1)
-      int v = c2h(*str);
-      str++;
-      if (v < 0)
-       return 0;
-      buf[i] = v;
-      v = c2h(*str);
-      str++;
-      if (v < 0)
-       return 0;
-      buf[i] = (buf[i] << 4) | v;
-#undef c2h
-    }
-  return buflen;
-}
-
 void
-repodata_set_checksum(Repodata *data, Id handle, Id keyname, Id type,
+repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
                      const char *str)
 {
   unsigned char buf[64];
-  int l = checksumtype2len(type);
+  int l;
 
-  if (!l)
+  if (!(l = sat_chksum_len(type)))
     return;
-  if (hexstr2bytes(buf, str, l) != l)
-    {
-      fprintf(stderr, "Invalid hex character in '%s'\n", str);
-      return;
-    }
-  repodata_set_bin_checksum(data, handle, keyname, type, buf);
+  if (l > sizeof(buf) || sat_hex2bin(&str, buf, l) != l)
+    return;
+  repodata_set_bin_checksum(data, solvid, keyname, type, buf);
 }
 
 const char *
 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
 {
-  int i, l;
-  char *str, *s;
+  int l;
 
-  l = checksumtype2len(type);
-  if (!l)
+  if (!(l = sat_chksum_len(type)))
     return "";
-  s = str = pool_alloctmpspace(data->repo->pool, 2 * l + 1);
-  for (i = 0; i < l; i++)
+  return pool_bin2hex(data->repo->pool, buf, l);
+}
+
+/* rpm filenames don't contain the epoch, so strip it */
+static inline const char *
+evrid2vrstr(Pool *pool, Id evrid)
+{
+  const char *p, *evr = pool_id2str(pool, evrid);
+  if (!evr)
+    return evr;
+  for (p = evr; *p >= '0' && *p <= '9'; p++)
+    ;
+  return p != evr && *p == ':' ? p + 1 : evr;
+}
+
+void
+repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
+{
+  Pool *pool = data->repo->pool;
+  Solvable *s;
+  const char *str, *fp;
+  int l = 0;
+
+  if (medianr)
+    repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
+  if (!dir)
+    {
+      if ((dir = strrchr(file, '/')) != 0)
+       {
+          l = dir - file;
+         dir = file;
+         file = dir + l + 1;
+         if (!l)
+           l++;
+       }
+    }
+  else
+    l = strlen(dir);
+  if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
+    {
+      dir += 2;
+      l -= 2;
+    }
+  if (l == 1 && dir[0] == '.')
+    l = 0;
+  s = pool->solvables + solvid;
+  if (dir && l)
     {
-      unsigned char v = buf[i];
-      unsigned char w = v >> 4;
-      *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
-      w = v & 15;
-      *s++ = w >= 10 ? w + ('a' - 10) : w + '0';
+      str = pool_id2str(pool, s->arch);
+      if (!strncmp(dir, str, l) && !str[l])
+       repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
+      else if (!dir[l])
+       repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
+      else
+       {
+         char *dir2 = strdup(dir);
+         dir2[l] = 0;
+         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
+         free(dir2);
+       }
+    }
+  fp = file;
+  str = pool_id2str(pool, s->name);
+  l = strlen(str);
+  if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
+    {
+      fp += l + 1;
+      str = evrid2vrstr(pool, s->evr);
+      l = strlen(str);
+      if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
+       {
+         fp += l + 1;
+         str = pool_id2str(pool, s->arch);
+         l = strlen(str);
+         if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
+           {
+             repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
+             return;
+           }
+       }
     }
-  *s = 0;
-  return str;
+  repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
 }
 
-Id
-repodata_globalize_id(Repodata *data, Id id)
-{ 
-  if (!data || !data->localpool)
-    return id;
-  return str2id(data->repo->pool, stringpool_id2str(&data->spool, id), 1);
+void
+repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
+{
+  Repokey key;
+  int i;
+
+  key.name = keyname;
+  key.type = REPOKEY_TYPE_IDARRAY;
+  key.size = 0;
+  key.storage = KEY_STORAGE_INCORE;
+  repodata_set(data, solvid, &key, data->attriddatalen);
+  data->attriddata = sat_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+  for (i = 0; i < q->count; i++)
+    data->attriddata[data->attriddatalen++] = q->elements[i];
+  data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
-repodata_add_dirnumnum(Repodata *data, Id handle, Id keyname, Id dir, Id num, Id num2)
+repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
 {
   assert(dir);
 #if 0
-fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, num2, data->attriddatalen);
+fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
 #endif
-  repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
   data->attriddata[data->attriddatalen++] = dir;
   data->attriddata[data->attriddatalen++] = num;
   data->attriddata[data->attriddatalen++] = num2;
@@ -2194,7 +2243,7 @@ fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", handle, dir, num, n
 }
 
 void
-repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *str)
+repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
 {
   Id stroff;
   int l;
@@ -2207,69 +2256,118 @@ repodata_add_dirstr(Repodata *data, Id handle, Id keyname, Id dir, const char *s
   data->attrdatalen += l;
 
 #if 0
-fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", handle, dir, str,  data->attriddatalen);
+fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
 #endif
-  repodata_add_array(data, handle, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
   data->attriddata[data->attriddatalen++] = dir;
   data->attriddata[data->attriddatalen++] = stroff;
   data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
-repodata_add_idarray(Repodata *data, Id handle, Id keyname, Id id)
+repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
 {
 #if 0
-fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", handle, id, data->attriddatalen);
+fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
 #endif
-  repodata_add_array(data, handle, keyname, REPOKEY_TYPE_IDARRAY, 1);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
   data->attriddata[data->attriddatalen++] = id;
   data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
-repodata_add_poolstr_array(Repodata *data, Id handle, Id keyname,
+repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
                           const char *str)
 {
   Id id;
   if (data->localpool)
     id = stringpool_str2id(&data->spool, str, 1);
   else
-    id = str2id(data->repo->pool, str, 1);
-  repodata_add_idarray(data, handle, keyname, id);
+    id = pool_str2id(data->repo->pool, str, 1);
+  repodata_add_idarray(data, solvid, keyname, id);
 }
 
 void
-repodata_add_fixarray(Repodata *data, Id handle, Id keyname, Id ghandle)
+repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
 {
-  repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FIXARRAY, 1);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
   data->attriddata[data->attriddatalen++] = ghandle;
   data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
-repodata_add_flexarray(Repodata *data, Id handle, Id keyname, Id ghandle)
+repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
 {
-  repodata_add_array(data, handle, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
+  repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
   data->attriddata[data->attriddatalen++] = ghandle;
   data->attriddata[data->attriddatalen++] = 0;
 }
 
 void
+repodata_delete_uninternalized(Repodata *data, Id solvid, Id keyname)
+{
+  Id *pp, *ap, **app;
+  app = repodata_get_attrp(data, solvid);
+  ap = *app;
+  if (!ap)
+    return;
+  for (; *ap; ap += 2)
+    if (data->keys[*ap].name == keyname)
+      break;
+  if (!*ap)
+    return;
+  pp = ap;
+  ap += 2;
+  for (; *ap; ap += 2)
+    {
+      if (data->keys[*ap].name == keyname)
+       continue;
+      *pp++ = ap[0];
+      *pp++ = ap[1];
+    }
+  *pp = 0;
+}
+
+/* XXX: does not work correctly, needs fix in iterators! */
+void
+repodata_delete(Repodata *data, Id solvid, Id keyname)
+{
+  Repokey key;
+  key.name = keyname;
+  key.type = REPOKEY_TYPE_DELETED;
+  key.size = 0;
+  key.storage = KEY_STORAGE_INCORE;
+  repodata_set(data, solvid, &key, 0);
+}
+
+/* add all (uninternalized) attrs from src to dest */
+void
 repodata_merge_attrs(Repodata *data, Id dest, Id src)
 {
   Id *keyp;
-  if (dest == src || !(keyp = data->attrs[src]))
+  if (dest == src || !(keyp = data->attrs[src - data->start]))
     return;
   for (; *keyp; keyp += 2)
     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
 }
 
+/* add some (uninternalized) attrs from src to dest */
+void
+repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
+{
+  Id *keyp;
+  if (dest == src || !(keyp = data->attrs[src - data->start]))
+    return;
+  for (; *keyp; keyp += 2)
+    if (!keyidmap || MAPTST(keyidmap, keyp[0]))
+      repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
+}
 
 
 
 /**********************************************************************/
 
-/* unify with repo_write! */
+/* TODO: unify with repo_write and repo_solv! */
 
 #define EXTDATA_BLOCK 1023
 
@@ -2282,6 +2380,7 @@ static void
 data_addid(struct extdata *xd, Id x)
 {
   unsigned char *dp;
+
   xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
   dp = xd->buf + xd->len;
 
@@ -2304,7 +2403,7 @@ data_addideof(struct extdata *xd, Id x, int eof)
 {
   if (x >= 64)
     x = (x & 63) | ((x & ~63) << 1);
-  data_addid(xd, (eof ? x: x | 64));
+  data_addid(xd, (eof ? x : x | 64));
 }
 
 static void
@@ -2317,14 +2416,14 @@ data_addblob(struct extdata *xd, unsigned char *blob, int len)
 
 /*********************************/
 
+/* internalalize some key into incore/vincore data */
+
 static void
 repodata_serialize_key(Repodata *data, struct extdata *newincore,
                       struct extdata *newvincore,
                       Id *schema,
                       Repokey *key, Id val)
 {
-  /* Otherwise we have a new value.  Parse it into the internal
-     form.  */
   Id *ida;
   struct extdata *xd;
   unsigned int oldvincorelen = 0;
@@ -2359,6 +2458,14 @@ repodata_serialize_key(Repodata *data, struct extdata *newincore,
     case REPOKEY_TYPE_DIR:
       data_addid(xd, val);
       break;
+    case REPOKEY_TYPE_BINARY:
+      {
+       Id len;
+       unsigned char *dp = data_read_id(data->attrdata + val, &len);
+       dp += len;
+       data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
+      }
+      break;
     case REPOKEY_TYPE_IDARRAY:
       for (ida = data->attriddata + val; *ida; ida++)
        data_addideof(xd, ida[0], ida[1] ? 0 : 1);
@@ -2384,32 +2491,21 @@ repodata_serialize_key(Repodata *data, struct extdata *newincore,
        schemaid = 0;
        for (ida = data->attriddata + val; *ida; ida++)
          {
-#if 0
-           fprintf(stderr, "serialize struct %d\n", *ida);
-#endif
            sp = schema;
            Id *kp = data->xattrs[-*ida];
            if (!kp)
              continue;
            num++;
            for (;*kp; kp += 2)
-             {
-#if 0
-               fprintf(stderr, "  %s:%d\n", id2str(data->repo->pool, data->keys[*kp].name), kp[1]);
-#endif
-               *sp++ = *kp;
-             }
+             *sp++ = *kp;
            *sp = 0;
            if (!schemaid)
              schemaid = repodata_schema2id(data, schema, 1);
            else if (schemaid != repodata_schema2id(data, schema, 0))
              {
-               fprintf(stderr, "  not yet implemented: substructs with different schemas\n");
+               pool_debug(data->repo->pool, SAT_FATAL, "fixarray substructs with different schemas\n");
                exit(1);
              }
-#if 0
-           fprintf(stderr, "  schema %d\n", schemaid);
-#endif
          }
        if (!num)
          break;
@@ -2421,10 +2517,7 @@ repodata_serialize_key(Repodata *data, struct extdata *newincore,
            if (!kp)
              continue;
            for (;*kp; kp += 2)
-             {
-               repodata_serialize_key(data, newincore, newvincore,
-                                      schema, data->keys + *kp, kp[1]);
-             }
+             repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
          }
        break;
       }
@@ -2450,15 +2543,12 @@ repodata_serialize_key(Repodata *data, struct extdata *newincore,
            data_addid(xd, schemaid);
            kp = data->xattrs[-*ida];
            for (;*kp; kp += 2)
-             {
-               repodata_serialize_key(data, newincore, newvincore,
-                                      schema, data->keys + *kp, kp[1]);
-             }
+             repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
          }
        break;
       }
     default:
-      fprintf(stderr, "don't know how to handle type %d\n", key->type);
+      pool_debug(data->repo->pool, SAT_FATAL, "don't know how to handle type %d\n", key->type);
       exit(1);
     }
   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
@@ -2533,7 +2623,7 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
        {
          if (seen[*keyp])
            {
-             fprintf(stderr, "Inconsistent old data (key occured twice).\n");
+             pool_debug(data->repo->pool, SAT_FATAL, "Inconsistent old data (key occured twice).\n");
              exit(1);
            }
          seen[*keyp] = -1;
@@ -2610,7 +2700,7 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
            }
          key = data->keys + *keyp;
 #if 0
-         fprintf(stderr, "internalize %d:%s:%s\n", entry, id2str(data->repo->pool, key->name), id2str(data->repo->pool, key->type));
+         fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, pool_id2str(data->repo->pool, key->name), pool_id2str(data->repo->pool, key->type));
 #endif
          ndp = dp;
          if (oldcount)
@@ -2664,7 +2754,7 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
   data->incoredata = newincore.buf;
   data->incoredatalen = newincore.len;
   data->incoredatafree = 0;
-  
+
   sat_free(data->vincore);
   data->vincore = newvincore.buf;
   data->vincorelen = newvincore.len;
@@ -2679,9 +2769,138 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
 void
 repodata_disable_paging(Repodata *data)
 {
-  if (maybe_load_repodata(data, 0)
-      && data->num_pages)
-    repodata_load_page_range(data, 0, data->num_pages - 1);
+  if (maybe_load_repodata(data, 0))
+    repopagestore_disable_paging(&data->store);
+}
+
+static void
+repodata_load_stub(Repodata *data)
+{
+  Repo *repo = data->repo;
+  Pool *pool = repo->pool;
+  int r, i;
+  struct _Pool_tmpspace oldtmpspace;
+
+  if (!pool->loadcallback)
+    {
+      data->state = REPODATA_ERROR;
+      return;
+    }
+  data->state = REPODATA_LOADING;
+
+  /* save tmp space */
+  oldtmpspace = pool->tmpspace;
+  memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
+
+  r = pool->loadcallback(pool, data, pool->loadcallbackdata);
+
+  /* restore tmp space */
+  for (i = 0; i < POOL_TMPSPACEBUF; i++)
+    sat_free(pool->tmpspace.buf[i]);
+  pool->tmpspace = oldtmpspace;
+
+  data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
+}
+
+void
+repodata_create_stubs(Repodata *data)
+{
+  Repo *repo = data->repo;
+  Pool *pool = repo->pool;
+  Repodata *sdata;
+  int *stubdataids;
+  Dataiterator di;
+  Id xkeyname = 0;
+  int i, cnt = 0;
+  int repodataid;
+  int datastart, dataend;
+
+  repodataid = data - repo->repodata;
+  datastart = data->start;
+  dataend = data->end;
+  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
+  while (dataiterator_step(&di))
+    {
+      if (di.data - repo->repodata != repodataid)
+       continue;
+      cnt++;
+    }
+  dataiterator_free(&di);
+  if (!cnt)
+    return;
+  stubdataids = sat_calloc(cnt, sizeof(*stubdataids));
+  for (i = 0; i < cnt; i++)
+    {
+      sdata = repo_add_repodata(repo, 0);
+      if (dataend > datastart)
+        repodata_extend_block(sdata, datastart, dataend - datastart);
+      stubdataids[i] = sdata - repo->repodata;
+      sdata->state = REPODATA_STUB;
+      sdata->loadcallback = repodata_load_stub;
+    }
+  i = 0;
+  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
+  sdata = 0;
+  while (dataiterator_step(&di))
+    {
+      if (di.data - repo->repodata != repodataid)
+       continue;
+      if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
+       {
+         dataiterator_entersub(&di);
+         sdata = repo->repodata + stubdataids[i++];
+         xkeyname = 0;
+         continue;
+       }
+      switch (di.key->type)
+       {
+        case REPOKEY_TYPE_ID:
+         repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
+         break;
+       case REPOKEY_TYPE_CONSTANTID:
+         repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
+         break;
+       case REPOKEY_TYPE_STR:
+         repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
+         break;
+       case REPOKEY_TYPE_VOID:
+         repodata_set_void(sdata, SOLVID_META, di.key->name);
+         break;
+       case REPOKEY_TYPE_NUM:
+         repodata_set_num(sdata, SOLVID_META, di.key->name, di.kv.num);
+         break;
+       case REPOKEY_TYPE_MD5:
+       case REPOKEY_TYPE_SHA1:
+       case REPOKEY_TYPE_SHA256:
+         repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
+         break;
+       case REPOKEY_TYPE_IDARRAY:
+         repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
+         if (di.key->name == REPOSITORY_KEYS)
+           {
+             Repokey xkey;
+
+             if (!xkeyname)
+               {
+                 if (!di.kv.eof)
+                   xkeyname = di.kv.id;
+                 continue;
+               }
+             xkey.name = xkeyname;
+              xkey.type = di.kv.id;
+              xkey.storage = KEY_STORAGE_INCORE;
+              xkey.size = 0; 
+              repodata_key2id(sdata, &xkey, 1);
+              xkeyname = 0;
+           }
+       default:
+         break;
+       }
+    }
+  dataiterator_free(&di);
+  for (i = 0; i < cnt; i++)
+    repodata_internalize(repo->repodata + stubdataids[i]);
+  sat_free(stubdataids);
 }
 
 /*