+/* id translation functions */
+
+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);
+}
+
+Id
+repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
+{
+ const char *s;
+ if (!id || !data || !fromdata)
+ return id;
+ if (data == fromdata || (!data->localpool && !fromdata->localpool))
+ return id;
+ if (fromdata->localpool)
+ s = stringpool_id2str(&fromdata->spool, id);
+ else
+ s = pool_id2str(data->repo->pool, id);
+ if (data->localpool)
+ return stringpool_str2id(&data->spool, s, create);
+ else
+ return pool_str2id(data->repo->pool, s, create);
+}
+
+Id
+repodata_translate_dir_slow(Repodata *data, Repodata *fromdata, Id dir, int create, Id *cache)
+{
+ Id parent, compid;
+ if (!dir)
+ {
+ /* make sure that the dirpool has an entry */
+ if (create && !data->dirpool.ndirs)
+ dirpool_add_dir(&data->dirpool, 0, 0, create);
+ return 0;
+ }
+ parent = dirpool_parent(&fromdata->dirpool, dir);
+ if (parent)
+ {
+ if (!(parent = repodata_translate_dir(data, fromdata, parent, create, cache)))
+ return 0;
+ }
+ compid = dirpool_compid(&fromdata->dirpool, dir);
+ if (compid > 1 && (data->localpool || fromdata->localpool))
+ {
+ if (!(compid = repodata_translate_id(data, fromdata, compid, create)))
+ return 0;
+ }
+ if (!(compid = dirpool_add_dir(&data->dirpool, parent, compid, create)))
+ return 0;
+ if (cache)
+ {
+ cache[(dir & 255) * 2] = dir;
+ cache[(dir & 255) * 2 + 1] = compid;
+ }
+ return compid;
+}
+
+/************************************************************************
+ * uninternalized lookup / search
+ */
+
+static void
+data_fetch_uninternalized(Repodata *data, Repokey *key, Id value, KeyValue *kv)
+{
+ Id *array;
+ kv->eof = 1;
+ switch (key->type)
+ {
+ case REPOKEY_TYPE_STR:
+ kv->str = (const char *)data->attrdata + value;
+ return;
+ case REPOKEY_TYPE_CONSTANT:
+ kv->num2 = 0;
+ kv->num = key->size;
+ return;
+ case REPOKEY_TYPE_CONSTANTID:
+ kv->id = key->size;
+ return;
+ case REPOKEY_TYPE_NUM:
+ kv->num2 = 0;
+ kv->num = value;
+ if (value & 0x80000000)
+ {
+ kv->num = (unsigned int)data->attrnum64data[value ^ 0x80000000];
+ kv->num2 = (unsigned int)(data->attrnum64data[value ^ 0x80000000] >> 32);
+ }
+ return;
+ case_CHKSUM_TYPES:
+ kv->num = 0; /* not stringified */
+ kv->str = (const char *)data->attrdata + value;
+ return;
+ case REPOKEY_TYPE_BINARY:
+ kv->str = (const char *)data_read_id(data->attrdata + value, (Id *)&kv->num);
+ return;
+ case REPOKEY_TYPE_IDARRAY:
+ array = data->attriddata + (value + kv->entry);
+ kv->id = array[0];
+ kv->eof = array[1] ? 0 : 1;
+ return;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ kv->num = 0; /* not stringified */
+ array = data->attriddata + (value + kv->entry * 2);
+ kv->id = array[0];
+ kv->str = (const char *)data->attrdata + array[1];
+ kv->eof = array[2] ? 0 : 1;
+ return;
+ case REPOKEY_TYPE_DIRNUMNUMARRAY:
+ array = data->attriddata + (value + kv->entry * 3);
+ kv->id = array[0];
+ kv->num = array[1];
+ kv->num2 = array[2];
+ kv->eof = array[3] ? 0 : 1;
+ return;
+ case REPOKEY_TYPE_FIXARRAY:
+ case REPOKEY_TYPE_FLEXARRAY:
+ array = data->attriddata + (value + kv->entry);
+ kv->id = array[0]; /* the handle */
+ kv->eof = array[1] ? 0 : 1;
+ return;
+ default:
+ kv->id = value;
+ return;
+ }
+}
+
+Repokey *
+repodata_lookup_kv_uninternalized(Repodata *data, Id solvid, Id keyname, KeyValue *kv)
+{
+ Id *ap;
+ if (!data->attrs || solvid < data->start || solvid >= data->end)
+ return 0;
+ ap = data->attrs[solvid - data->start];
+ if (!ap)
+ return 0;
+ for (; *ap; ap += 2)
+ {
+ Repokey *key = data->keys + *ap;
+ if (key->name != keyname)
+ continue;
+ data_fetch_uninternalized(data, key, ap[1], kv);
+ return key;
+ }
+ return 0;
+}
+
+void
+repodata_search_uninternalized(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+{
+ Id *ap;
+ int stop;
+ Solvable *s;
+ KeyValue kv;
+
+ if (!data->attrs || solvid < data->start || solvid >= data->end)
+ return;
+ ap = data->attrs[solvid - data->start];
+ if (!ap)
+ return;
+ for (; *ap; ap += 2)
+ {
+ Repokey *key = data->keys + *ap;
+ if (keyname && key->name != keyname)
+ continue;
+ s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
+ kv.entry = 0;
+ do
+ {
+ data_fetch_uninternalized(data, key, ap[1], &kv);
+ stop = callback(cbdata, s, data, key, &kv);
+ kv.entry++;
+ }
+ while (!kv.eof && !stop);
+ if (keyname || stop > SEARCH_NEXT_KEY)
+ return;
+ }
+}
+
+/************************************************************************
+ * data search
+ */
+
+
+const char *
+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 && (key->name == SOLVABLE_NAME || key->type == REPOKEY_TYPE_IDARRAY))
+ {
+ const char *s;
+ for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
+ ;
+ if (*s == ':' && s > kv->str)
+ kv->str = s + 1;
+ }
+ return kv->str;
+ case REPOKEY_TYPE_STR:
+ return kv->str;
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ if (!(flags & SEARCH_FILES))
+ return kv->str; /* match just the basename */
+ if (kv->num)
+ return kv->str; /* already stringified */
+ /* Put the full filename into kv->str. */
+ kv->str = repodata_dir2str(data, kv->id, kv->str);
+ kv->num = 1; /* mark stringification */
+ return kv->str;
+ case_CHKSUM_TYPES:
+ if (!(flags & SEARCH_CHECKSUMS))
+ return 0; /* skip em */
+ if (kv->num)
+ return kv->str; /* already stringified */
+ kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
+ kv->num = 1; /* mark stringification */
+ return kv->str;
+ default:
+ return 0;
+ }
+}
+
+
+/* this is an internal hack to pass the parent kv to repodata_search_keyskip */
+struct subschema_data {
+ void *cbdata;
+ Id solvid;
+ KeyValue *parent;
+};
+
+void
+repodata_search_arrayelement(Repodata *data, Id solvid, Id keyname, int flags, KeyValue *kv, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+{
+ repodata_search_keyskip(data, solvid, keyname, flags | SEARCH_SUBSCHEMA, (Id *)kv, callback, cbdata);
+}
+
+static int
+repodata_search_array(Repodata *data, Id solvid, Id keyname, int flags, Repokey *key, KeyValue *kv, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+{
+ Solvable *s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
+ unsigned char *dp = (unsigned char *)kv->str;
+ int stop;
+ Id schema = 0;
+
+ if (!dp || kv->entry != -1)
+ return 0;
+ while (++kv->entry < (int)kv->num)
+ {
+ if (kv->entry)
+ dp = data_skip_schema(data, dp, schema);
+ if (kv->entry == 0 || key->type == REPOKEY_TYPE_FLEXARRAY)
+ dp = data_read_id(dp, &schema);
+ kv->id = schema;
+ kv->str = (const char *)dp;
+ kv->eof = kv->entry == kv->num - 1 ? 1 : 0;
+ stop = callback(cbdata, s, data, key, kv);
+ if (stop && stop != SEARCH_ENTERSUB)
+ return stop;
+ if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
+ repodata_search_keyskip(data, solvid, keyname, flags | SEARCH_SUBSCHEMA, (Id *)kv, callback, cbdata);
+ }
+ if ((flags & SEARCH_ARRAYSENTINEL) != 0)
+ {
+ if (kv->entry)
+ dp = data_skip_schema(data, dp, schema);
+ kv->id = 0;
+ kv->str = (const char *)dp;
+ kv->eof = 2;
+ return callback(cbdata, s, data, key, kv);
+ }
+ return 0;
+}
+
+/* search a specific repodata */
+void
+repodata_search_keyskip(Repodata *data, Id solvid, Id keyname, int flags, Id *keyskip, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
+{
+ Id schema;
+ Repokey *key;
+ Id keyid, *kp, *keyp;
+ unsigned char *dp, *ddp;
+ int onekey = 0;
+ int stop;
+ KeyValue kv;
+ Solvable *s;
+
+ if (!maybe_load_repodata(data, keyname))
+ return;
+ if ((flags & SEARCH_SUBSCHEMA) != 0)
+ {
+ flags ^= SEARCH_SUBSCHEMA;
+ kv.parent = (KeyValue *)keyskip;
+ keyskip = 0;
+ schema = kv.parent->id;
+ dp = (unsigned char *)kv.parent->str;
+ }
+ else
+ {
+ schema = 0;
+ dp = solvid2data(data, solvid, &schema);
+ if (!dp)
+ return;
+ kv.parent = 0;
+ }
+ s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
+ keyp = data->schemadata + data->schemata[schema];
+ if (keyname)
+ {
+ /* search for a specific key */
+ for (kp = keyp; *kp; kp++)
+ if (data->keys[*kp].name == keyname)
+ break;
+ if (!*kp)
+ return;
+ dp = forward_to_key(data, *kp, keyp, dp);
+ if (!dp)
+ return;
+ keyp = kp;
+ onekey = 1;
+ }
+ while ((keyid = *keyp++) != 0)
+ {
+ stop = 0;
+ key = data->keys + keyid;
+ ddp = get_data(data, key, &dp, *keyp && !onekey ? 1 : 0);
+
+ if (keyskip && (key->name >= keyskip[0] || keyskip[3 + key->name] != keyskip[1] + data->repodataid))
+ {
+ if (onekey)
+ return;
+ continue;
+ }
+ if (key->type == REPOKEY_TYPE_DELETED && !(flags & SEARCH_KEEP_TYPE_DELETED))
+ {