Support new types for MD5 and SHA1 checksums (stored in binary, but with
[platform/upstream/libsolv.git] / src / repodata.c
index a2aa759..456b055 100644 (file)
@@ -14,6 +14,7 @@
 
 #define _GNU_SOURCE
 #include <string.h>
+#include <fnmatch.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include "poolid_private.h"
 #include "util.h"
 
-#include "fastlz.c"
+#include "repopack.h"
 
-unsigned char *
-data_read_id(unsigned char *dp, Id *idp)
-{
-  Id x = 0;
-  unsigned char c;
-  for (;;)
-    {
-      c = *dp++;
-      if (!(c & 0x80))
-       {
-         *idp = (x << 7) ^ c;
-          return dp;
-       }
-      x = (x << 7) ^ c ^ 128;
-    }
-}
+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);
 
-static unsigned char *
-data_read_ideof(unsigned char *dp, Id *idp, int *eof)
-{
-  Id x = 0;
-  unsigned char c;
-  for (;;)
-    {
-      c = *dp++;
-      if (!(c & 0x80))
-       {
-         if (c & 0x40)
-           {
-             c ^= 0x40;
-             *eof = 0;
-           }
-         else
-           *eof = 1;
-         *idp = (x << 6) ^ c;
-          return dp;
-       }
-      x = (x << 7) ^ c ^ 128;
-    }
-}
+#define REPODATA_BLOCK 255
 
-static unsigned char *
-data_skip(unsigned char *dp, int type)
+
+void
+repodata_init(Repodata *data, Repo *repo, int localpool)
 {
-  unsigned char x;
-  switch (type)
-    {
-    case TYPE_VOID:
-    case TYPE_CONSTANT:
-      return dp;
-    case TYPE_ID:
-    case TYPE_NUM:
-    case TYPE_DIR:
-      while ((*dp & 0x80) != 0)
-       dp++;
-      return dp + 1;
-    case TYPE_IDARRAY:
-      while ((*dp & 0xc0) != 0)
-       dp++;
-      return dp + 1;
-    case TYPE_STR:
-      while ((*dp) != 0)
-       dp++;
-      return dp + 1;
-    case TYPE_DIRSTRARRAY:
-      for (;;)
-       {
-          while ((*dp & 0x80) != 0)
-           dp++;
-         x = *dp++;
-          while ((*dp) != 0)
-           dp++;
-         dp++;
-         if (!(x & 0x40))
-           return dp;
-       }
-    case TYPE_DIRNUMNUMARRAY:
-      for (;;)
-       {
-         while ((*dp & 0x80) != 0)
-           dp++;
-         dp++;
-         while ((*dp & 0x80) != 0)
-           dp++;
-         dp++;
-         while ((*dp & 0x80) != 0)
-           dp++;
-         if (!(*dp & 0x40))
-           return dp + 1;
-         dp++;
-       }
-    default:
-      fprintf(stderr, "unknown type in data_skip\n");
-      exit(1);
-    }
+  memset(data, 0, sizeof (*data));
+  data->repo = repo;
+  data->localpool = localpool;
+  if (localpool)
+    stringpool_init_empty(&data->spool);
+  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->start = repo->start;
+  data->end = repo->end;
+  data->incoreoffset = sat_extend_resize(0, data->end - data->start, sizeof(Id), REPODATA_BLOCK);
+  data->pagefd = -1;
 }
 
-static unsigned char *
-data_fetch(unsigned char *dp, KeyValue *kv, Repokey *key)
+void
+repodata_free(Repodata *data)
 {
-  kv->eof = 1;
-  if (!dp)
-    return 0;
-  switch (key->type)
-    {
-    case TYPE_VOID:
-      return dp;
-    case TYPE_CONSTANT:
-      kv->num = key->size;
-      return dp;
-    case TYPE_STR:
-      kv->str = (const char *)dp;
-      return dp + strlen(kv->str) + 1;
-    case TYPE_ID:
-      return data_read_id(dp, &kv->id);
-    case TYPE_NUM:
-      return data_read_id(dp, &kv->num);
-    case TYPE_IDARRAY:
-      return data_read_ideof(dp, &kv->id, &kv->eof);
-    case TYPE_DIR:
-      return data_read_id(dp, &kv->id);
-    case TYPE_DIRSTRARRAY:
-      dp = data_read_ideof(dp, &kv->id, &kv->eof);
-      kv->str = (const char *)dp;
-      return dp + strlen(kv->str) + 1;
-    case TYPE_DIRNUMNUMARRAY:
-      dp = data_read_id(dp, &kv->id);
-      dp = data_read_id(dp, &kv->num);
-      return data_read_ideof(dp, &kv->num2, &kv->eof);
-    default:
-      return 0;
-    }
+  sat_free(data->keys);
+  sat_free(data->schemata);
+  sat_free(data->schemadata);
+
+  sat_free(data->spool.strings);
+  sat_free(data->spool.stringspace);
+  sat_free(data->spool.stringhashtbl);
+
+  sat_free(data->dirpool.dirs);
+  sat_free(data->dirpool.dirtraverse);
+
+  sat_free(data->incoredata);
+  sat_free(data->incoreoffset);
+  sat_free(data->verticaloffset);
+
+  sat_free(data->blob_store);
+  sat_free(data->pages);
+  sat_free(data->mapped);
+
+  sat_free(data->vincore);
+
+  sat_free(data->attrs);
+  sat_free(data->attrdata);
+  sat_free(data->attriddata);
+  
+  sat_free(data->location);
+  sat_free(data->addedfileprovides);
+
+  if (data->pagefd != -1)
+    close(data->pagefd);
 }
 
 static unsigned char *
-forward_to_key(Repodata *data, Id key, Id schemaid, unsigned char *dp)
+forward_to_key(Repodata *data, Id keyid, Id schemaid, unsigned char *dp)
 {
   Id k, *keyp;
 
   keyp = data->schemadata + data->schemata[schemaid];
   while ((k = *keyp++) != 0)
     {
-      if (k == key)
+      if (k == keyid)
        return dp;
       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
        {
-         dp = data_skip(dp, TYPE_ID);  /* skip that offset */
-         dp = data_skip(dp, TYPE_ID);  /* skip that length */
+         dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that offset */
+         dp = data_skip(dp, REPOKEY_TYPE_ID);  /* skip that length */
          continue;
        }
       if (data->keys[k].storage != KEY_STORAGE_INCORE)
@@ -205,6 +137,9 @@ load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
   if (i > pend)
     return data->blob_store + data->pages[pstart].mapped_at;
 
+  if (data->pagefd == -1)
+    return 0;
+
   /* Ensure that we can map the numbers of pages we need at all.  */
   if (pend - pstart + 1 > data->ncanmap)
     {
@@ -314,27 +249,20 @@ load_page_range(Repodata *data, unsigned int pstart, unsigned int pend)
 #ifdef DEBUG_PAGING
          fprintf (stderr, "PAGEIN: %d to %d", i, pnum);
 #endif
-         /* Not mapped, so read in this page.  */
-         if (fseek(data->fp, p->file_offset, SEEK_SET) < 0)
+          if (pread(data->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
            {
-             perror ("mapping fseek");
-             exit (1);
-           }
-         if (fread(compressed ? buf : dest, in_len, 1, data->fp) != 1)
-           {
-             perror ("mapping fread");
-             exit (1);
+             perror ("mapping pread");
+             return 0;
            }
          if (compressed)
            {
              unsigned int out_len;
              out_len = unchecked_decompress_buf(buf, in_len,
                                                  dest, BLOB_PAGESIZE);
-             if (out_len != BLOB_PAGESIZE
-                 && i < data->num_pages - 1)
+             if (out_len != BLOB_PAGESIZE && i < data->num_pages - 1)
                {
-                 fprintf (stderr, "can't decompress\n");
-                 exit (1);
+                 fprintf(stderr, "can't decompress\n");
+                 return 0;
                }
 #ifdef DEBUG_PAGING
              fprintf (stderr, " (expand %d to %d)", in_len, out_len);
@@ -354,7 +282,7 @@ static unsigned char *
 make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
 {
   unsigned char *dp;
-  if (key->type == TYPE_VOID)
+  if (!len)
     return 0;
   if (off >= data->lastverticaloffset)
     {
@@ -363,12 +291,11 @@ make_vertical_available(Repodata *data, Repokey *key, Id off, Id len)
        return 0;
       return data->vincore + off;
     }
-  if (!data->fp)
-    return 0;
   if (off + len > key->size)
     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 = load_page_range(data, off / BLOB_PAGESIZE, (off + len - 1) / BLOB_PAGESIZE);
   if (dp)
     dp += off % BLOB_PAGESIZE;
@@ -399,6 +326,42 @@ get_data(Repodata *data, Repokey *key, unsigned char **dpp)
   return 0;
 }
 
+static inline int
+maybe_load_repodata(Repodata *data, Id *keyid)
+{
+  if (data->state == REPODATA_STUB)
+    {
+      if (data->loadcallback)
+       {
+         if (keyid)
+           {
+             /* key order may change when loading */
+             int i;
+             Id name = data->keys[*keyid].name;
+             Id type = data->keys[*keyid].type;
+             data->loadcallback(data);
+             if (data->state == REPODATA_AVAILABLE)
+               {
+                 for (i = 1; i < data->nkeys; i++)
+                   if (data->keys[i].name == name && data->keys[i].type == type)
+                     break;
+                 if (i < data->nkeys)
+                   *keyid = i;
+                 else
+                   return 0;
+               }
+           }
+         else
+           data->loadcallback(data);
+       }
+      else
+       data->state = REPODATA_ERROR;
+    }
+  if (data->state == REPODATA_AVAILABLE)
+    return 1;
+  data->state = REPODATA_ERROR;
+  return 0;
+}
 
 const char *
 repodata_lookup_str(Repodata *data, Id entry, Id keyid)
@@ -408,30 +371,85 @@ repodata_lookup_str(Repodata *data, Id entry, Id keyid)
   Id id, *keyp;
   unsigned char *dp;
 
-  if (data->entryschemau8)
-    schema = data->entryschemau8[entry];
-  else
-    schema = data->entryschema[entry];
+  if (!maybe_load_repodata(data, &keyid))
+    return 0;
+
+  dp = data->incoredata + data->incoreoffset[entry];
+  dp = data_read_id(dp, &schema);
   /* make sure the schema of this solvable contains the key */
   for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
     if (!*keyp)
       return 0;
-  dp = forward_to_key(data, keyid, schema, data->incoredata + data->incoreoffset[entry]);
+  dp = forward_to_key(data, keyid, schema, dp);
   key = data->keys + keyid;
   dp = get_data(data, key, &dp);
   if (!dp)
     return 0;
-  if (key->type == TYPE_STR)
+  if (key->type == REPOKEY_TYPE_STR)
     return (const char *)dp;
-  if (key->type != TYPE_ID)
+  if (key->type == REPOKEY_TYPE_CONSTANTID)
+    return id2str(data->repo->pool, key->size);
+  if (key->type == REPOKEY_TYPE_ID)
+    dp = data_read_id(dp, &id);
+  else
     return 0;
-  /* id type, must either use global or local string strore*/
-  dp = data_read_id(dp, &id);
   if (data->localpool)
     return data->spool.stringspace + data->spool.strings[id];
   return id2str(data->repo->pool, id);
 }
 
+int
+repodata_lookup_num(Repodata *data, Id entry, Id keyid, unsigned int *value)
+{
+  Id schema;
+  Repokey *key;
+  Id *keyp;
+  KeyValue kv;
+  unsigned char *dp;
+
+  *value = 0;
+
+  if (!maybe_load_repodata(data, &keyid))
+    return 0;
+
+  dp = data->incoredata + data->incoreoffset[entry];
+  dp = data_read_id(dp, &schema);
+  /* make sure the schema of this solvable contains the key */
+  for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
+    if (!*keyp)
+      return 0;
+  dp = forward_to_key(data, keyid, schema, dp);
+  key = data->keys + keyid;
+  dp = get_data(data, key, &dp);
+  if (!dp)
+    return 0;
+  if (key->type == REPOKEY_TYPE_NUM
+      || key->type == REPOKEY_TYPE_U32
+      || key->type == REPOKEY_TYPE_CONSTANT)
+    {
+      dp = data_fetch(dp, &kv, key);
+      *value = kv.num;
+      return 1;
+    }
+  return 0;
+}
+
+int
+repodata_lookup_void(Repodata *data, Id entry, Id keyid)
+{
+  Id schema;
+  Id *keyp;
+  unsigned char *dp;
+  if (!maybe_load_repodata(data, &keyid))
+    return 0;
+  dp = data->incoredata + data->incoreoffset[entry];
+  dp = data_read_id(dp, &schema);
+  for (keyp = data->schemadata + data->schemata[schema]; *keyp != keyid; keyp++)
+    if (!*keyp)
+      return 0;
+  return 1;
+}
+
 void
 repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
 {
@@ -443,12 +461,12 @@ repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbda
   int stop;
   KeyValue kv;
 
-  if (data->entryschemau8)
-    schema = data->entryschemau8[entry];
-  else
-    schema = data->entryschema[entry];
-  keyp = data->schemadata + data->schemata[schema];
+  if (!maybe_load_repodata(data, 0))
+    return;
+
   dp = data->incoredata + data->incoreoffset[entry];
+  dp = data_read_id(dp, &schema);
+  keyp = data->schemadata + data->schemata[schema];
   if (keyname)
     {
       /* search in a specific key */
@@ -481,6 +499,336 @@ repodata_search(Repodata *data, Id entry, Id keyname, int (*callback)(void *cbda
     }
 }
 
+static void
+dataiterator_newdata(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 + data->incoreoffset[di->solvid - data->start];
+  dp = data_read_id(dp, &schema);
+  Id *keyp = data->schemadata + data->schemata[schema];
+  if (keyname)
+    {
+      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, schema, dp);
+      if (!dp)
+       return;
+      keyp = kp - 1;
+    }
+  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;
+}
+
+void
+dataiterator_init(Dataiterator *di, Repo *repo, Id p, Id keyname,
+                 const char *match, int flags)
+{
+  di->flags = flags;
+  if (p)
+    {
+      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;
+      di->data = repo->repodata + repo->nrepodata - 1;
+      di->state = 0;
+    }
+  di->match = match;
+  di->keyname = keyname;
+  static Id zeroid = 0;
+  di->keyp = &zeroid;
+  di->kv.eof = 1;
+  di->repo = repo;
+  di->idp = 0;
+}
+
+/* FIXME factor and merge with repo_matchvalue */
+static int
+dataiterator_match(Dataiterator *di, KeyValue *kv)
+{
+  int flags = di->flags;
+
+  if ((flags & SEARCH_STRINGMASK) != 0)
+    {
+      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;
+       default:
+         return 0;
+       }
+      switch ((flags & SEARCH_STRINGMASK))
+       {
+         case SEARCH_SUBSTRING:
+           if (flags & SEARCH_NOCASE)
+             {
+               if (!strcasestr(kv->str, di->match))
+                 return 0;
+             }
+           else
+             {
+               if (!strstr(kv->str, di->match))
+                 return 0;
+             }
+           break;
+         case SEARCH_STRING:
+           if (flags & SEARCH_NOCASE)
+             {
+               if (strcasecmp(di->match, kv->str))
+                 return 0;
+             }
+           else
+             {
+               if (strcmp(di->match, kv->str))
+                 return 0;
+             }
+           break;
+         case SEARCH_GLOB:
+           if (fnmatch(di->match, kv->str, (flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
+             return 0;
+           break;
+#if 0
+         case SEARCH_REGEX:
+           if (regexec(&di->regexp, kv->str, 0, NULL, 0))
+             return 0;
+#endif
+         default:
+           return 0;
+       }
+    }
+  return 1;
+}
+
+static Repokey solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1] = {
+  { SOLVABLE_NAME,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_ARCH,        REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_EVR,         REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_VENDOR,      REPOKEY_TYPE_ID, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_PROVIDES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_OBSOLETES,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_CONFLICTS,   REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_REQUIRES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_RECOMMENDS,  REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_SUGGESTS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_SUPPLEMENTS, REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_ENHANCES,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { SOLVABLE_FRESHENS,    REPOKEY_TYPE_IDARRAY, 0, KEY_STORAGE_SOLVABLE },
+  { RPM_RPMDBID,          REPOKEY_TYPE_U32, 0, KEY_STORAGE_SOLVABLE },
+};
+
+int
+dataiterator_step(Dataiterator *di)
+{
+restart:
+  while (1)
+    {
+      if (di->state)
+       {
+         if (di->idp)
+           {
+             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;
+               break;
+             case SOLVABLE_ARCH:
+               if (!s->arch)
+                 continue;
+               di->kv.id = s->arch;
+               break;
+             case SOLVABLE_EVR:
+               if (!s->evr)
+                 continue;
+               di->kv.id = s->evr;
+               break;
+             case SOLVABLE_VENDOR:
+               if (!s->vendor)
+                 continue;
+               di->kv.id = s->vendor;
+               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 SOLVABLE_FRESHENS:
+               di->idp = s->freshens
+                   ? di->repo->idarraydata + s->freshens : 0;
+               continue;
+             case RPM_RPMDBID:
+               if (!di->repo->rpmdbid)
+                 continue;
+               di->kv.num = di->repo->rpmdbid[di->solvid - di->repo->start];
+               break;
+             default:
+               di->data = di->repo->repodata - 1;
+               di->kv.eof = 1;
+               di->state = 0;
+               continue;
+           }
+       }
+      else
+       {
+         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;
+                         while (++di->solvid < repo->end)
+                           if (repo->pool->solvables[di->solvid].repo == repo)
+                             break;
+                         if (di->solvid >= repo->end)
+                           return 0;
+                         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 >= 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);
+           }
+       }
+weg2:
+      if (!di->match
+         || dataiterator_match(di, &di->kv))
+       break;
+    }
+  return 1;
+}
 
 /* extend repodata so that it includes solvables p */
 void
@@ -492,22 +840,12 @@ repodata_extend(Repodata *data, Id p)
     {
       int old = data->end - data->start;
       int new = p - data->end + 1;
-      if (data->entryschemau8)
-       {
-         data->entryschemau8 = sat_realloc(data->entryschemau8, old + new);
-         memset(data->entryschemau8 + old, 0, new);
-       }
-      if (data->entryschema)
-       {
-         data->entryschema = sat_realloc2(data->entryschema, old + new, sizeof(Id));
-         memset(data->entryschema + old, 0, new * sizeof(Id));
-       }
       if (data->attrs)
        {
-         data->attrs = sat_realloc2(data->attrs, old + 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_realloc2(data->incoreoffset, old + new, sizeof(Id));
+      data->incoreoffset = sat_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
       memset(data->incoreoffset + old, 0, new * sizeof(Id));
       data->end = p + 1;
     }
@@ -515,25 +853,13 @@ repodata_extend(Repodata *data, Id p)
     {
       int old = data->end - data->start;
       int new = data->start - p;
-      if (data->entryschemau8)
-       {
-         data->entryschemau8 = sat_realloc(data->entryschemau8, old + new);
-         memmove(data->entryschemau8 + new, data->entryschemau8, old);
-         memset(data->entryschemau8, 0, new);
-       }
-      if (data->entryschema)
-       {
-         data->entryschema = sat_realloc2(data->entryschema, old + new, sizeof(Id));
-         memmove(data->entryschema + new, data->entryschema, old * sizeof(Id));
-         memset(data->entryschema, 0, new * sizeof(Id));
-       }
       if (data->attrs)
        {
-         data->attrs = sat_realloc2(data->attrs, old + 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_realloc2(data->incoreoffset, old + 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));
       memset(data->incoreoffset, 0, new * sizeof(Id));
       data->start = p;
@@ -541,16 +867,74 @@ repodata_extend(Repodata *data, Id p)
 }
 
 void
-repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
+repodata_extend_block(Repodata *data, Id start, Id num)
+{
+  if (!num)
+    return;
+  if (!data->incoreoffset)
+    {
+      data->incoreoffset = sat_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
+      data->start = start;
+      data->end = start + num;
+      return;
+    }
+  repodata_extend(data, start);
+  if (num > 1)
+    repodata_extend(data, start + num - 1);
+}
+
+/**********************************************************************/
+
+#define REPODATA_ATTRS_BLOCK 63
+#define REPODATA_ATTRDATA_BLOCK 1023
+#define REPODATA_ATTRIDDATA_BLOCK 63
+
+static void
+repodata_insert_keyid(Repodata *data, Id entry, Id keyid, Id val, int overwrite)
 {
-  Id keyid, *pp;
+  Id *pp;
   int i;
+  if (!data->attrs)
+    {
+      data->attrs = sat_calloc_block(data->end - data->start, sizeof(Id *),
+                                    REPODATA_BLOCK);
+    }
+  i = 0;
+  if (data->attrs[entry])
+    {
+      for (pp = data->attrs[entry]; *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)
+           {
+             pp[0] = keyid;
+              pp[1] = val;
+           }
+          return;
+        }
+      i = pp - data->attrs[entry];
+    }
+  data->attrs[entry] = sat_extend(data->attrs[entry], i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
+  pp = data->attrs[entry] + i;
+  *pp++ = keyid;
+  *pp++ = val;
+  *pp = 0;
+}
+
+void
+repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
+{
+  Id keyid;
 
   /* find key in keys */
   for (keyid = 1; keyid < data->nkeys; keyid++)
     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
       {
-        if (key->type == TYPE_CONSTANT && key->size != data->keys[keyid].size)
+        if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
           continue;
         break;
       }
@@ -565,27 +949,7 @@ repodata_set(Repodata *data, Id entry, Repokey *key, Id val)
          data->verticaloffset[data->nkeys - 1] = 0;
        }
     }
-  key = data->keys + keyid;
-  if (!data->attrs)
-    data->attrs = sat_calloc(data->end - data->start + 1, sizeof(Id *));
-  i = 0;
-  if (data->attrs[entry])
-    {
-      for (pp = data->attrs[entry]; *pp; pp += 2)
-        if (*pp == keyid)
-          break;
-      if (*pp)
-        {
-          pp[1] = val;
-          return;
-        }
-      i = pp - data->attrs[entry];
-    }
-  data->attrs[entry] = sat_realloc2(data->attrs[entry], i + 3, sizeof(Id));
-  pp = data->attrs[entry] + i;
-  *pp++ = keyid;
-  *pp++ = val;
-  *pp = 0;
+  repodata_insert_keyid(data, entry, keyid, val, 1);
 }
 
 void
@@ -593,7 +957,7 @@ repodata_set_id(Repodata *data, Id entry, Id keyname, Id id)
 {
   Repokey key;
   key.name = keyname;
-  key.type = TYPE_ID;
+  key.type = REPOKEY_TYPE_ID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
   repodata_set(data, entry, &key, id);
@@ -604,7 +968,7 @@ repodata_set_num(Repodata *data, Id entry, Id keyname, Id num)
 {
   Repokey key;
   key.name = keyname;
-  key.type = TYPE_NUM;
+  key.type = REPOKEY_TYPE_NUM;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
   repodata_set(data, entry, &key, num);
@@ -620,7 +984,7 @@ repodata_set_poolstr(Repodata *data, Id entry, Id keyname, const char *str)
   else
     id = str2id(data->repo->pool, str, 1);
   key.name = keyname;
-  key.type = TYPE_ID;
+  key.type = REPOKEY_TYPE_ID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
   repodata_set(data, entry, &key, id);
@@ -631,18 +995,29 @@ repodata_set_constant(Repodata *data, Id entry, Id keyname, Id constant)
 {
   Repokey key;
   key.name = keyname;
-  key.type = TYPE_CONSTANT;
+  key.type = REPOKEY_TYPE_CONSTANT;
   key.size = constant;
   key.storage = KEY_STORAGE_INCORE;
   repodata_set(data, entry, &key, 0);
 }
 
 void
+repodata_set_constantid(Repodata *data, Id entry, 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, entry, &key, 0);
+}
+
+void
 repodata_set_void(Repodata *data, Id entry, Id keyname)
 {
   Repokey key;
   key.name = keyname;
-  key.type = TYPE_VOID;
+  key.type = REPOKEY_TYPE_VOID;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
   repodata_set(data, entry, &key, 0);
@@ -656,67 +1031,216 @@ repodata_set_str(Repodata *data, Id entry, Id keyname, const char *str)
 
   l = strlen(str) + 1;
   key.name = keyname;
-  key.type = TYPE_STR;
+  key.type = REPOKEY_TYPE_STR;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  data->attrdata = sat_realloc(data->attrdata, data->attrdatalen + l);
+  data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
   memcpy(data->attrdata + data->attrdatalen, str, l);
   repodata_set(data, entry, &key, data->attrdatalen);
   data->attrdatalen += l;
 }
 
-void
-repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
+static void
+repoadata_add_array(Repodata *data, Id entry, Id keyname, Id keytype, int entrysize)
 {
+  int oldsize;
   Id *ida, *pp;
-  Repokey key;
 
-#if 0
-fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
-#endif
-  if (data->attrs[entry])
+  pp = 0;
+  if (data->attrs && data->attrs[entry])
+    for (pp = data->attrs[entry]; *pp; pp += 2)
+      if (data->keys[*pp].name == keyname && data->keys[*pp].type == keytype)
+        break;
+  if (!pp || !*pp)
     {
-      for (pp = data->attrs[entry]; *pp; pp += 2)
-        if (data->keys[*pp].name == keyname && data->keys[*pp].type == TYPE_DIRNUMNUMARRAY)
-         break;
-      if (*pp)
-       {
-         int oldsize = 0;
-         for (ida = data->attriddata + pp[1]; *ida; ida += 3)
-           oldsize += 3;
-         if (ida + 1 == data->attriddata + data->attriddatalen)
-           {
-             /* this was the last entry, just append it */
-             data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 3, sizeof(Id));
-             data->attriddatalen--;    /* overwrite terminating 0  */
-           }
-         else
-           {
-             /* too bad. move to back. */
-             data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + oldsize + 4, sizeof(Id));
-             memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
-             pp[1] = data->attriddatalen;
-             data->attriddatalen += oldsize;
-           }
-         data->attriddata[data->attriddatalen++] = dir;
-         data->attriddata[data->attriddatalen++] = num;
-         data->attriddata[data->attriddatalen++] = num2;
-         data->attriddata[data->attriddatalen++] = 0;
-         return;
-       }
+      /* not found. allocate new key */
+      Repokey key;
+      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, entry, &key, data->attriddatalen);
+      return;
+    }
+  oldsize = 0;
+  for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
+    oldsize += entrysize;
+  if (ida + 1 == data->attriddata + data->attriddatalen)
+    {
+      /* this was the last entry, just append it */
+      data->attriddata = sat_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+      data->attriddatalen--;   /* overwrite terminating 0  */
+    }
+  else
+    {
+      /* too bad. move to back. */
+      data->attriddata = sat_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
+      memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
+      pp[1] = data->attriddatalen;
+      data->attriddatalen += oldsize;
+    }
+}
+
+void
+repodata_set_bin_checksum(Repodata *data, Id entry, Id keyname, Id type,
+                     const unsigned char *str)
+{
+  Repokey key;
+  int l;
+  switch (type)
+    {
+      case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
+      case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
+      default: return;
     }
   key.name = keyname;
-  key.type = TYPE_DIRNUMNUMARRAY;
+  key.type = type;
   key.size = 0;
   key.storage = KEY_STORAGE_INCORE;
-  data->attriddata = sat_realloc2(data->attriddata, data->attriddatalen + 4, sizeof(Id));
-  repodata_set(data, entry, &key, data->attriddatalen);
+  data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+  memcpy(data->attrdata + data->attrdatalen, str, l);
+  repodata_set(data, entry, &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 entry, Id keyname, Id type,
+                     const char *str)
+{
+  int l;
+  switch (type)
+    {
+      case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
+      case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
+      default: return;
+    }
+  unsigned char buf[l];
+  if (hexstr2bytes(buf, str, l) != l)
+    {
+      fprintf(stderr, "Invalid hex character in %s\n", str);
+      return;
+    }
+  repodata_set_bin_checksum(data, entry, keyname, type, buf);
+}
+
+const char *
+repodata_chk2str(Repodata *data, Id type, const char *buf)
+{
+  int i, l;
+  char *str, *s;
+  switch (type)
+    {
+      case REPOKEY_TYPE_MD5: l = SIZEOF_MD5; break;
+      case REPOKEY_TYPE_SHA1: l = SIZEOF_SHA1; break;
+      default: return id2str(data->repo->pool, ID_EMPTY);
+    }
+  s = str = pool_alloctmpspace(data->repo->pool, 2*l + 1);
+  for (i = 0; i < l; i++, s+=2)
+    {
+      unsigned char v = buf[i];
+      unsigned char w = v >> 4;
+      s[0] = w >= 10 ? (w-10)+'a' : w + '0';
+      w = v & 15;
+      s[1] = w >= 10 ? (w-10)+'a' : w + '0';
+    }
+  *s = 0;
+  return str;
+}
+
+void
+repodata_add_dirnumnum(Repodata *data, Id entry, Id keyname, Id dir, Id num, Id num2)
+{
+
+#if 0
+fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", entry, dir, num, num2, data->attriddatalen);
+#endif
+  repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
   data->attriddata[data->attriddatalen++] = dir;
   data->attriddata[data->attriddatalen++] = num;
   data->attriddata[data->attriddatalen++] = num2;
   data->attriddata[data->attriddatalen++] = 0;
 }
 
+void
+repodata_add_dirstr(Repodata *data, Id entry, Id keyname, Id dir, const char *str)
+{
+  Id stroff;
+  int l;
+
+  l = strlen(str) + 1;
+  data->attrdata = sat_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
+  memcpy(data->attrdata + data->attrdatalen, str, l);
+  stroff = data->attrdatalen;
+  data->attrdatalen += l;
+
+#if 0
+fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", entry, dir, str,  data->attriddatalen);
+#endif
+  repoadata_add_array(data, entry, 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 entry, Id keyname, Id id)
+{
+#if 0
+fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", entry, id, data->attriddatalen);
+#endif
+  repoadata_add_array(data, entry, keyname, REPOKEY_TYPE_IDARRAY, 1);
+  data->attriddata[data->attriddatalen++] = id;
+  data->attriddata[data->attriddatalen++] = 0;
+}
+
+void
+repodata_add_poolstr_array(Repodata *data, Id entry, 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, entry, keyname, id);
+}
+
+void
+repodata_merge_attrs(Repodata *data, Id dest, Id src)
+{
+  Id *keyp;
+  if (dest == src || !(keyp = data->attrs[src]))
+    return;
+  for (; *keyp; keyp += 2)
+    repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
+}
+
 /*********************************/
 
 /* unify with repo_write! */
@@ -827,7 +1351,6 @@ fprintf(stderr, "addschema: new schema\n");
 void
 repodata_internalize(Repodata *data)
 {
-  int i;
   Repokey *key;
   Id id, entry, nentry, *ida;
   Id schematacache[256];
@@ -846,17 +1369,21 @@ repodata_internalize(Repodata *data)
   schema = sat_malloc2(data->nkeys, sizeof(Id));
   seen = sat_malloc2(data->nkeys, sizeof(Id));
 
+  /* Merge the data already existing (in data->schemata, ->incoredata and
+     friends) with the new attributes in data->attrs[].  */
   nentry = data->end - data->start;
   addschema_prepare(data, schematacache);
   memset(&newincore, 0, sizeof(newincore));
+  data_addid(&newincore, 0);
   for (entry = 0; entry < nentry; entry++)
     {
       memset(seen, 0, data->nkeys * sizeof(Id));
       sp = schema;
-      if (data->entryschemau8)
-       oldschema = data->entryschemau8[entry];
+      dp = data->incoredata + data->incoreoffset[entry];
+      if (data->incoredata)
+        dp = data_read_id(dp, &oldschema);
       else
-       oldschema = data->entryschema[entry];
+       oldschema = 0;
 #if 0
 fprintf(stderr, "oldschema %d\n", oldschema);
 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
@@ -869,55 +1396,55 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
        {
          if (seen[*keyp])
            {
-             newschema = 1;
-             continue;
+             fprintf(stderr, "Inconsistent old data (key occured twice).\n");
+             exit(1);
            }
          seen[*keyp] = -1;
          *sp++ = *keyp;
          oldcount++;
        }
-      for (keyp = data->attrs[entry]; *keyp; keyp += 2)
-       {
-         if (!seen[*keyp])
-           {
-             newschema = 1;
-             *sp++ = *keyp;
-           }
-         seen[*keyp] = keyp[1] + 1;
-       }
+      if (data->attrs[entry])
+        for (keyp = data->attrs[entry]; *keyp; keyp += 2)
+         {
+           if (!seen[*keyp])
+             {
+               newschema = 1;
+               *sp++ = *keyp;
+             }
+           seen[*keyp] = keyp[1] + 1;
+         }
       *sp++ = 0;
       if (newschema)
-       {
-         schemaid = addschema(data, schema, schematacache);
-         if (schemaid > 255 && data->entryschemau8)
-           {
-             data->entryschema = sat_malloc2(nentry, sizeof(Id));
-             for (i = 0; i < nentry; i++)
-               data->entryschema[i] = data->entryschemau8[i];
-             data->entryschemau8 = sat_free(data->entryschemau8);
-           }
-         if (data->entryschemau8)
-           data->entryschemau8[entry] = schemaid;
-         else
-           data->entryschema[entry] = schemaid;
-       }
+        /* Ideally we'd like to sort the new schema here, to ensure
+          schema equality independend of the ordering.  We can't do that
+          yet.  For once see below (old ids need to come before new ids).
+          An additional difficulty is that we also need to move
+          the values with the keys.  */
+       schemaid = addschema(data, schema, schematacache);
       else
        schemaid = oldschema;
 
 
-      /* now create data blob */
-      dp = data->incoredata + data->incoreoffset[entry];
+      /* Now create data blob.  We walk through the (possibly new) schema
+        and either copy over old data, or insert the new.  */
+      /* XXX Here we rely on the fact that the (new) schema has the form
+        o1 o2 o3 o4 ... | n1 n2 n3 ...
+        (oX being the old keyids (possibly overwritten), and nX being
+         the new keyids).  This rules out sorting the keyids in order
+        to ensure a small schema count.  */
       data->incoreoffset[entry] = newincore.len;
+      data_addid(&newincore, schemaid);
       for (keyp = data->schemadata + data->schemata[schemaid]; *keyp; keyp++)
        {
          key = data->keys + *keyp;
          ndp = dp;
          if (oldcount)
            {
+             /* Skip the data associated with this old key.  */
              if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
                {
-                 ndp = data_skip(dp, TYPE_ID);
-                 ndp = data_skip(ndp, TYPE_ID);
+                 ndp = data_skip(dp, REPOKEY_TYPE_ID);
+                 ndp = data_skip(ndp, REPOKEY_TYPE_ID);
                }
              else if (key->storage == KEY_STORAGE_INCORE)
                ndp = data_skip(dp, key->type);
@@ -925,12 +1452,17 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
            }
          if (seen[*keyp] == -1)
            {
+             /* If this key was an old one _and_ was not overwritten with
+                a different value copy over the old value (we skipped it
+                above).  */
              if (dp != ndp)
                data_addblob(&newincore, dp, ndp - dp);
              seen[*keyp] = 0;
            }
          else if (seen[*keyp])
            {
+             /* Otherwise we have a new value.  Parse it into the internal
+                form.  */
              struct extdata *xd;
              unsigned int oldvincorelen = 0;
 
@@ -943,18 +1475,29 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
              id = seen[*keyp] - 1;
              switch (key->type)
                {
-               case TYPE_VOID:
-               case TYPE_CONSTANT:
+               case REPOKEY_TYPE_VOID:
+               case REPOKEY_TYPE_CONSTANT:
+               case REPOKEY_TYPE_CONSTANTID:
                  break;
-               case TYPE_STR:
+               case REPOKEY_TYPE_STR:
                  data_addblob(xd, data->attrdata + id, strlen((char *)(data->attrdata + id)) + 1);
                  break;
-               case TYPE_ID:
-               case TYPE_NUM:
-               case TYPE_DIR:
+               case REPOKEY_TYPE_MD5:
+                 data_addblob(xd, data->attrdata + id, SIZEOF_MD5);
+                 break;
+               case REPOKEY_TYPE_SHA1:
+                 data_addblob(xd, data->attrdata + id, SIZEOF_SHA1);
+                 break;
+               case REPOKEY_TYPE_ID:
+               case REPOKEY_TYPE_NUM:
+               case REPOKEY_TYPE_DIR:
                  data_addid(xd, id);
                  break;
-               case TYPE_DIRNUMNUMARRAY:
+               case REPOKEY_TYPE_IDARRAY:
+                 for (ida = data->attriddata + id; *ida; ida++)
+                   data_addideof(xd, ida[0], ida[1] ? 0 : 1);
+                 break;
+               case REPOKEY_TYPE_DIRNUMNUMARRAY:
                  for (ida = data->attriddata + id; *ida; ida += 3)
                    {
                      data_addid(xd, ida[0]);
@@ -962,6 +1505,13 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
                      data_addideof(xd, ida[2], ida[3] ? 0 : 1);
                    }
                  break;
+               case REPOKEY_TYPE_DIRSTRARRAY:
+                 for (ida = data->attriddata + id; *ida; ida += 2)
+                   {
+                     data_addideof(xd, ida[0], ida[2] ? 0 : 1);
+                     data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
+                   }
+                 break;
                default:
                  fprintf(stderr, "don't know how to handle type %d\n", key->type);
                  exit(1);
@@ -976,17 +1526,26 @@ fprintf(stderr, "schemadata %p\n", data->schemadata);
            }
          dp = ndp;
        }
+      if (data->attrs[entry])
+        sat_free(data->attrs[entry]);
     }
+  sat_free(schema);
+  sat_free(seen);
+
+  sat_free(data->incoredata);
   data->incoredata = newincore.buf;
   data->incoredatalen = newincore.len;
   data->incoredatafree = 0;
   
+  sat_free(data->vincore);
   data->vincore = newvincore.buf;
   data->vincorelen = newvincore.len;
 
   data->attrs = sat_free(data->attrs);
   data->attrdata = sat_free(data->attrdata);
+  data->attriddata = sat_free(data->attriddata);
   data->attrdatalen = 0;
+  data->attriddatalen = 0;
 }
 
 Id
@@ -998,6 +1557,8 @@ repodata_str2dir(Repodata *data, const char *dir, int create)
   parent = 0;
   while (*dir == '/' && dir[1] == '/')
     dir++;
+  if (*dir == '/' && !dir[1])
+    return 1;
   while (*dir)
     {
       dire = strchrnul(dir, '/');
@@ -1019,16 +1580,58 @@ repodata_str2dir(Repodata *data, const char *dir, int create)
   return parent;
 }
 
+const char *
+repodata_dir2str(Repodata *data, Id did, const char *suf)
+{
+  Pool *pool = data->repo->pool;
+  int l = 0;
+  Id parent, comp;
+  const char *comps;
+  char *p;
+
+  if (!did)
+    return suf ? suf : "";
+  parent = did;
+  while (parent)
+    {
+      comp = dirpool_compid(&data->dirpool, parent);
+      comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
+      l += strlen(comps);
+      parent = dirpool_parent(&data->dirpool, parent);
+      if (parent)
+       l++;
+    }
+  if (suf)
+    l += strlen(suf) + 1;
+  p = pool_alloctmpspace(pool, l + 1) + l;
+  *p = 0;
+  if (suf)
+    {
+      p -= strlen(suf);
+      strcpy(p, suf);
+      *--p = '/';
+    }
+  parent = did;
+  while (parent)
+    {
+      comp = dirpool_compid(&data->dirpool, parent);
+      comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
+      l = strlen(comps);
+      p -= l;
+      strncpy(p, comps, l);
+      parent = dirpool_parent(&data->dirpool, parent);
+      if (parent)
+        *--p = '/';
+    }
+  return p;
+}
+
 unsigned int
 repodata_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
 {
   return compress_buf(page, len, cpage, max);
 }
 
-/* Try to either setup on-demand paging (using FP as backing
-   file), or in case that doesn't work (FP not seekable) slurps in
-   all pages and deactivates paging.  */
-
 #define SOLV_ERROR_EOF              3
 
 static inline unsigned int
@@ -1047,6 +1650,12 @@ read_u32(FILE *fp)
   return x;
 }
 
+#define SOLV_ERROR_EOF         3
+#define SOLV_ERROR_CORRUPT     6
+
+/* Try to either setup on-demand paging (using FP as backing
+   file), or in case that doesn't work (FP not seekable) slurps in
+   all pages and deactivates paging.  */
 void
 repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int blobsz)
 {
@@ -1056,16 +1665,22 @@ repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int b
   unsigned int can_seek;
   long cur_file_ofs;
   unsigned char buf[BLOB_PAGESIZE];
+
   if (pagesz != BLOB_PAGESIZE)
     {
       /* We could handle this by slurping in everything.  */
-      fprintf (stderr, "non matching page size\n");
-      exit (1);
+      data->error = SOLV_ERROR_CORRUPT;
+      return;
     }
   can_seek = 1;
   if ((cur_file_ofs = ftell(fp)) < 0)
     can_seek = 0;
-  clearerr (fp);
+  clearerr(fp);
+  if (can_seek)
+    data->pagefd = dup(fileno(fp));
+  if (data->pagefd == -1)
+    can_seek = 0;
+
 #ifdef DEBUG_PAGING
   fprintf (stderr, "can %sseek\n", can_seek ? "" : "NOT ");
 #endif
@@ -1099,7 +1714,10 @@ repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int b
              fprintf (stderr, "can't seek after we thought we can\n");
              /* We can't fall back to non-seeking behaviour as we already
                 read over some data pages without storing them away.  */
-             exit (1);
+             data->error = SOLV_ERROR_EOF;
+             close(data->pagefd);
+             data->pagefd = -1;
+             return;
            }
          cur_file_ofs += in_len;
        }
@@ -1113,45 +1731,30 @@ repodata_read_or_setup_pages(Repodata *data, unsigned int pagesz, unsigned int b
          /* We can't seek, so suck everything in.  */
          if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
            {
-             perror ("fread");
-             exit (1);
+             perror("fread");
+             data->error = SOLV_ERROR_EOF;
+             return;
            }
          if (compressed)
            {
              out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
-             if (out_len != BLOB_PAGESIZE
-                 && i < npages - 1)
+             if (out_len != BLOB_PAGESIZE && i < npages - 1)
                {
-                 fprintf (stderr, "can't decompress\n");
-                 exit (1);
+                 data->error = SOLV_ERROR_CORRUPT;
+                 return;
                }
            }
        }
     }
+}
 
-  if (can_seek)
-    {
-      /* If we are here we were able to seek to all page
-         positions, so activate paging by copying FP into our structure.
-        We dup() the file, so that our callers can fclose() it and we
-        still have it open.  But this means that we share file positions
-        with the input filedesc.  So in case our caller reads it after us,
-        and calls back into us we might change the file position unexpectedly
-        to him.  */
-      int fd = dup (fileno (fp));
-      if (fd < 0)
-        {
-         /* Jeez!  What a bloody system, we can't dup() anymore.  */
-         perror ("dup");
-         exit (1);
-       }
-      /* XXX we don't close this yet anywhere.  */
-      data->fp = fdopen (fd, "r");
-      if (!data->fp)
-        {
-         /* My God!  What happened now?  */
-         perror ("fdopen");
-         exit (1);
-       }
-    }
+void
+repodata_disable_paging(Repodata *data)
+{
+  if (maybe_load_repodata(data, 0)
+      && data->num_pages)
+    load_page_range (data, 0, data->num_pages - 1);
 }
+/*
+vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
+*/