+ if ((flags & SEARCH_FILES) != 0 && match)
+ {
+ /* prepare basename check */
+ if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
+ {
+ const char *p = strrchr(match, '/');
+ ma->matchdata = (void *)(p ? p + 1 : match);
+ }
+ else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
+ {
+ const char *p;
+ for (p = match + strlen(match) - 1; p >= match; p--)
+ if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
+ break;
+ ma->matchdata = (void *)(p + 1);
+ }
+ }
+ return ma->error;
+}
+
+void
+datamatcher_free(Datamatcher *ma)
+{
+ if (ma->match)
+ ma->match = solv_free((char *)ma->match);
+ if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
+ {
+ regfree(ma->matchdata);
+ solv_free(ma->matchdata);
+ }
+ ma->matchdata = 0;
+}
+
+int
+datamatcher_match(Datamatcher *ma, const char *str)
+{
+ int l;
+ switch ((ma->flags & SEARCH_STRINGMASK))
+ {
+ case SEARCH_SUBSTRING:
+ if (ma->flags & SEARCH_NOCASE)
+ return strcasestr(str, ma->match) != 0;
+ else
+ return strstr(str, ma->match) != 0;
+ case SEARCH_STRING:
+ if (ma->flags & SEARCH_NOCASE)
+ return !strcasecmp(ma->match, str);
+ else
+ return !strcmp(ma->match, str);
+ case SEARCH_STRINGSTART:
+ if (ma->flags & SEARCH_NOCASE)
+ return !strncasecmp(ma->match, str, strlen(ma->match));
+ else
+ return !strncmp(ma->match, str, strlen(ma->match));
+ case SEARCH_STRINGEND:
+ l = strlen(str) - strlen(ma->match);
+ if (l < 0)
+ return 0;
+ if (ma->flags & SEARCH_NOCASE)
+ return !strcasecmp(ma->match, str + l);
+ else
+ return !strcmp(ma->match, str + l);
+ case SEARCH_GLOB:
+ return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
+ case SEARCH_REGEX:
+ return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
+ default:
+ return 0;
+ }
+}
+
+/* check if the matcher can match the provides basename */
+
+int
+datamatcher_checkbasename(Datamatcher *ma, const char *basename)
+{
+ int l;
+ const char *match = ma->matchdata;
+ if (!match)
+ return 1;
+ switch (ma->flags & SEARCH_STRINGMASK)
+ {
+ case SEARCH_STRING:
+ break;
+ case SEARCH_STRINGEND:
+ if (match != ma->match)
+ break; /* had slash, do exact match on basename */
+ /* FALLTHROUGH */
+ case SEARCH_GLOB:
+ /* check if the basename ends with match */
+ l = strlen(basename) - strlen(match);
+ if (l < 0)
+ return 0;
+ basename += l;
+ break;
+ default:
+ return 1; /* maybe matches */
+ }
+ if ((ma->flags & SEARCH_NOCASE) != 0)
+ return !strcasecmp(match, basename);
+ else
+ return !strcmp(match, basename);
+}
+
+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_enterarray,
+ di_nextarrayelement,
+
+ di_entersub,
+ di_leavesub,
+
+ di_nextsolvablekey,
+ di_entersolvablekey,
+ di_nextsolvableattr
+};
+
+/* see dataiterator.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->pool = pool;
+ di->flags = flags & ~SEARCH_THISSOLVID;
+ if (!pool || (repo && repo->pool != pool))
+ {
+ di->state = di_bye;
+ return -1;
+ }
+ if (match)
+ {
+ int error;
+ if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
+ {
+ di->state = di_bye;
+ return error;
+ }
+ }
+ di->keyname = keyname;
+ di->keynames[0] = keyname;
+ dataiterator_set_search(di, repo, p);
+ return 0;
+}
+
+void
+dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
+{
+ *di = *from;
+ if (di->dupstr)
+ {
+ if (di->dupstr == di->kv.str)
+ di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
+ else
+ {
+ di->dupstr = 0;
+ di->dupstrn = 0;
+ }
+ }
+ 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_set_match(Dataiterator *di, const char *match, int flags)
+{
+ di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
+ datamatcher_free(&di->matcher);
+ memset(&di->matcher, 0, sizeof(di->matcher));
+ if (match)
+ {
+ int error;
+ if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
+ {
+ di->state = di_bye;
+ return error;
+ }
+ }
+ return 0;
+}
+
+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 = 1;
+ if (!di->pool->urepos)
+ {
+ di->state = di_bye;
+ return;
+ }
+ if (!repo)
+ {
+ di->repoid = 1;
+ di->repo = di->pool->repos[di->repoid];
+ }
+ di->state = di_enterrepo;
+ if (p)
+ dataiterator_jump_to_solvid(di, p);
+}
+
+void
+dataiterator_set_keyname(Dataiterator *di, Id keyname)
+{
+ di->nkeynames = 0;
+ di->keyname = keyname;
+ di->keynames[0] = keyname;
+}
+
+void
+dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
+{
+ int i;
+
+ 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++;
+}
+
+void
+dataiterator_free(Dataiterator *di)
+{
+ if (di->matcher.match)
+ datamatcher_free(&di->matcher);
+ if (di->dupstr)
+ solv_free(di->dupstr);
+}
+
+static unsigned char *
+dataiterator_find_keyname(Dataiterator *di, Id keyname)
+{
+ Id *keyp;
+ Repokey *keys = di->data->keys, *key;
+ unsigned char *dp;
+
+ for (keyp = di->keyp; *keyp; keyp++)
+ if (keys[*keyp].name == keyname)
+ break;
+ if (!*keyp)
+ return 0;
+ key = keys + *keyp;
+ if (key->type == REPOKEY_TYPE_DELETED)
+ return 0;
+ if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
+ return 0; /* get_data will not work, no need to forward */
+ dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
+ if (!dp)
+ return 0;
+ di->keyp = keyp;
+ return dp;
+}
+
+static inline int
+is_filelist_extension(Repodata *data)
+{
+ int j;
+ if (!repodata_precheck_keyname(data, SOLVABLE_FILELIST))
+ return 0;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name == SOLVABLE_FILELIST)
+ break;
+ if (j == data->nkeys)
+ return 0;
+ if (data->state != REPODATA_AVAILABLE)
+ return 1;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ return 0;
+ return 1;
+}
+
+static int
+dataiterator_filelistcheck(Dataiterator *di)
+{
+ int j;
+ int needcomplete = 0;
+ Repodata *data = di->data;
+
+ if ((di->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(data, di->matcher.match))
+ needcomplete = 1;
+ if (data->state != REPODATA_AVAILABLE)
+ return needcomplete ? 1 : 0;
+ if (!needcomplete)
+ {
+ /* we don't need the complete filelist, so ignore all stubs */
+ if (data->repo->nrepodata == 2)
+ return 1;
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ return 1;
+ return 0;
+ }
+ else
+ {
+ /* we need the complete filelist. check if we habe a filtered filelist and there's
+ * a extension with the complete filelist later on */
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name == SOLVABLE_FILELIST)
+ break;
+ if (j == data->nkeys)
+ return 0; /* does not have filelist */
+ for (j = 1; j < data->nkeys; j++)
+ if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+ break;
+ if (j == data->nkeys)
+ return 1; /* this is the externsion */
+ while (data - data->repo->repodata + 1 < data->repo->nrepodata)
+ {
+ data++;
+ if (is_filelist_extension(data))
+ return 0;
+ }
+ return 1;
+ }
+}
+
+int
+dataiterator_step(Dataiterator *di)
+{
+ Id schema;
+
+ if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
+ unsigned int ddpoff = di->ddp - di->vert_ddp;
+ di->vert_off += ddpoff;
+ di->vert_len -= ddpoff;
+ di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
+ di->vert_storestate = di->data->storestate;
+ if (!di->ddp)
+ di->state = di_nextkey;
+ }
+ for (;;)
+ {
+ switch (di->state)
+ {
+ case di_enterrepo: di_enterrepo:
+ if (!di->repo || (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)
+ {
+ di->repodataid = 1; /* 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)
+ {
+ extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
+
+ di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
+ di->data = 0;
+ goto di_entersolvablekey;
+ }
+ }
+ /* FALLTHROUGH */
+
+ case di_enterrepodata: di_enterrepodata:
+ if (di->repodataid)
+ {
+ if (di->repodataid >= di->repo->nrepodata)
+ goto di_nextsolvable;
+ di->data = di->repo->repodata + di->repodataid;
+ }
+ if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
+ goto di_nextrepodata;
+ if (!maybe_load_repodata(di->data, di->keyname))
+ goto di_nextrepodata;
+ 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)
+ {
+ 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;
+ if (!di->dp)
+ goto di_nextkey;
+ /* this is get_data() modified to store vert_ data */
+ if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ Id off, len;
+ di->dp = data_read_id(di->dp, &off);
+ di->dp = data_read_id(di->dp, &len);
+ di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
+ di->vert_off = off;
+ di->vert_len = len;
+ di->vert_storestate = di->data->storestate;
+ }
+ else if (di->key->storage == KEY_STORAGE_INCORE)
+ {
+ di->ddp = di->dp;
+ if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
+ di->dp = data_skip_key(di->data, di->dp, di->key);
+ }
+ else
+ di->ddp = 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 && ++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->repoid++;
+ di->repodataid = 1;
+ if (di->repoid < di->pool->nrepos)
+ {
+ di->repo = di->pool->repos[di->repoid];
+ goto di_enterrepo;
+ }
+ }
+ /* 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, (Id *)&di->kv.num);
+ di->kv.eof = 0;
+ di->kv.entry = -1;
+ /* FALLTHROUGH */
+
+ case di_nextarrayelement: di_nextarrayelement:
+ di->kv.entry++;
+ if (di->kv.entry)
+ di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
+ if (di->kv.entry == di->kv.num)
+ {
+ 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 = 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->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
+ goto di_entersub;
+ if ((di->flags & SEARCH_SUB) != 0)
+ di->state = di_entersub;
+ else
+ di->state = di_nextarrayelement;
+ break;
+
+ case di_entersub: di_entersub:
+ if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
+ goto di_nextarrayelement; /* sorry, full */
+ di->parents[di->nparents].kv = di->kv;
+ di->parents[di->nparents].dp = di->dp;
+ di->parents[di->nparents].keyp = di->keyp;
+ di->dp = (unsigned char *)di->kv.str;
+ di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
+ memset(&di->kv, 0, sizeof(di->kv));
+ di->kv.parent = &di->parents[di->nparents].kv;
+ di->nparents++;
+ 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 */
+
+ case di_nextsolvablekey: di_nextsolvablekey:
+ if (di->keyname || di->key->name == RPM_RPMDBID)
+ goto di_enterrepodata;
+ di->key++;
+ /* FALLTHROUGH */
+
+ case di_entersolvablekey: di_entersolvablekey:
+ di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
+ if (!di->idp || !*di->idp)
+ goto di_nextsolvablekey;
+ if (di->kv.eof)
+ {
+ /* not an array */
+ di->kv.id = *di->idp;
+ di->kv.num = *di->idp; /* for rpmdbid */
+ di->kv.num2 = 0; /* for rpmdbid */
+ di->kv.entry = 0;
+ di->state = di_nextsolvablekey;
+ break;
+ }
+ di->kv.entry = -1;
+ /* FALLTHROUGH */
+
+ case di_nextsolvableattr:
+ di->state = di_nextsolvableattr;
+ di->kv.id = *di->idp++;
+ di->kv.entry++;
+ if (!*di->idp)
+ {
+ di->kv.eof = 1;
+ di->state = di_nextsolvablekey;
+ }
+ break;
+
+ }
+
+ if (di->matcher.match)
+ {
+ const char *str;
+ /* simple pre-check so that we don't need to stringify */
+ if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
+ if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
+ continue;
+ if (!(str = 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, str))
+ continue;
+ }
+ else
+ {
+ if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
+ repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
+ }
+ /* found something! */
+ return 1;
+ }
+}
+
+void
+dataiterator_entersub(Dataiterator *di)
+{
+ if (di->state == di_nextarrayelement)
+ di->state = di_entersub;
+}
+
+void
+dataiterator_setpos(Dataiterator *di)
+{
+ if (di->kv.eof == 2)
+ {
+ pool_clear_pos(di->pool);
+ return;
+ }
+ 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_setpos_parent(Dataiterator *di)
+{
+ if (!di->kv.parent || di->kv.parent->eof == 2)
+ {
+ pool_clear_pos(di->pool);
+ return;
+ }
+ 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;
+}
+
+/* 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)
+ {
+ 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;
+ }
+ di->dupstr = 0;
+ di->dupstrn = 0;
+ if (from->dupstr && from->dupstr == from->kv.str)
+ {
+ di->dupstrn = from->dupstrn;
+ di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
+ }
+}
+
+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;
+ }
+ 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)
+ {
+ di->state = di_bye;
+ 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;
+ }
+}
+
+void
+dataiterator_skip_attribute(Dataiterator *di)
+{
+ if (di->state == di_nextsolvableattr)
+ di->state = di_nextsolvablekey;
+ else
+ di->state = di_nextkey;
+}
+
+void
+dataiterator_skip_solvable(Dataiterator *di)
+{
+ 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)
+{
+ di->nparents = 0;
+ di->kv.parent = 0;
+ di->rootlevel = 0;
+ di->keyname = di->keynames[0];
+ di->state = di_nextrepo;
+}
+
+void
+dataiterator_jump_to_solvid(Dataiterator *di, Id 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;
+ if (!di->pool->pos.repodataid && di->pool->pos.solvid == SOLVID_META) {
+ solvid = SOLVID_META; /* META pos hack */
+ } else {
+ di->data = di->repo->repodata + di->pool->pos.repodataid;
+ di->repodataid = 0;
+ }
+ }
+ else if (solvid > 0)
+ {
+ di->repo = di->pool->solvables[solvid].repo;
+ di->repoid = 0;
+ }
+ if (di->repoid > 0)
+ {
+ if (!di->pool->urepos)
+ {
+ di->state = di_bye;
+ return;
+ }
+ di->repoid = 1;
+ di->repo = di->pool->repos[di->repoid];
+ }
+ if (solvid != SOLVID_POS)
+ di->repodataid = 1;
+ 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;
+ di->repoid = 0; /* 0 means stay at repo */
+ di->repodataid = 1;
+ di->solvid = 0;
+ di->flags &= ~SEARCH_THISSOLVID;
+ di->state = di_enterrepo;
+}
+
+int
+dataiterator_match(Dataiterator *di, Datamatcher *ma)
+{
+ const char *str;
+ if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
+ return 0;
+ return ma ? datamatcher_match(ma, str) : 1;
+}
+
+void
+dataiterator_strdup(Dataiterator *di)
+{
+ int l = -1;
+
+ if (!di->kv.str || di->kv.str == di->dupstr)
+ return;
+ switch (di->key->type)
+ {
+ case_CHKSUM_TYPES:
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ if (di->kv.num) /* was it stringified into tmp space? */
+ l = strlen(di->kv.str) + 1;
+ break;
+ default:
+ break;
+ }
+ if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
+ {
+ switch (di->key->type)
+ {
+ case REPOKEY_TYPE_STR:
+ case REPOKEY_TYPE_DIRSTRARRAY:
+ l = strlen(di->kv.str) + 1;
+ break;
+ case_CHKSUM_TYPES:
+ l = solv_chksum_len(di->key->type);
+ break;
+ case REPOKEY_TYPE_BINARY:
+ l = di->kv.num;
+ break;
+ }
+ }
+ if (l >= 0)
+ {
+ if (!di->dupstrn || di->dupstrn < l)
+ {
+ di->dupstrn = l + 16;
+ di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
+ }
+ if (l)
+ memcpy(di->dupstr, di->kv.str, l);
+ di->kv.str = di->dupstr;
+ }
+}
+
+/************************************************************************
+ * data modify functions
+ */
+
+/* extend repodata so that it includes solvables p */
+void
+repodata_extend(Repodata *data, Id p)
+{
+ if (data->start == data->end)
+ data->start = data->end = p;
+ if (p >= data->end)
+ {
+ int old = data->end - data->start;
+ int new = p - data->end + 1;
+ if (data->attrs)
+ {
+ data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
+ memset(data->attrs + old, 0, new * sizeof(Id *));
+ }
+ data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
+ memset(data->incoreoffset + old, 0, new * sizeof(Id));
+ data->end = p + 1;
+ }
+ if (p < data->start)
+ {
+ int old = data->end - data->start;
+ int new = data->start - p;
+ if (data->attrs)
+ {
+ data->attrs = solv_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 = solv_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;
+ }
+}
+
+/* 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++)
+ solv_free(data->attrs[i]);
+ data->attrs = solv_free(data->attrs);
+ }
+ data->incoreoffset = solv_free(data->incoreoffset);
+ data->start = data->end = 0;
+ return;
+ }
+ if (data->attrs)
+ {
+ for (i = end; i < data->end; i++)
+ solv_free(data->attrs[i - data->start]);
+ data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
+ }
+ if (data->incoreoffset)
+ data->incoreoffset = solv_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)
+{
+ if (!num)
+ return;
+ if (!data->incoreoffset)
+ {
+ /* this also means that data->attrs is NULL */
+ data->incoreoffset = solv_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 31
+#define REPODATA_ATTRDATA_BLOCK 1023
+#define REPODATA_ATTRIDDATA_BLOCK 63
+#define REPODATA_ATTRNUM64DATA_BLOCK 15
+
+
+Id
+repodata_new_handle(Repodata *data)
+{
+ if (!data->nxattrs)
+ {
+ data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
+ data->nxattrs = 2; /* -1: SOLVID_META */
+ }
+ data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
+ data->xattrs[data->nxattrs] = 0;
+ return -(data->nxattrs++);
+}
+
+static inline Id **
+repodata_get_attrp(Repodata *data, Id handle)
+{
+ if (handle < 0)