+static char *
+splitrel(char *rname, char *r, int *rflagsp)
+{
+ int nend = r - rname;
+ int rflags = 0;
+ if (nend && *r == '=' && r[-1] == '!')
+ {
+ nend--;
+ r++;
+ rflags = REL_LT|REL_GT;
+ }
+ for (; *r; r++)
+ {
+ if (*r == '<')
+ rflags |= REL_LT;
+ else if (*r == '=')
+ rflags |= REL_EQ;
+ else if (*r == '>')
+ rflags |= REL_GT;
+ else
+ break;
+ }
+ while (*r && (*r == ' ' || *r == '\t'))
+ r++;
+ while (nend && (rname[nend - 1] == ' ' || rname[nend - 1] == '\t'))
+ nend--;
+ if (nend <= 0 || !*r || !rflags)
+ return 0;
+ *rflagsp = rflags;
+ rname[nend] = 0;
+ return r;
+}
+
+/* match name/provides, support DOTARCH and REL modifiers
+ */
+static int
+selection_name_arch_rel(Pool *pool, Queue *selection, const char *name, int flags, int doprovides)
+{
+ int ret, rflags = 0, noprune;
+ char *r = 0, *rname = 0;
+
+ /* try to split off an relation part */
+ if ((flags & SELECTION_REL) != 0)
+ {
+ if ((r = strpbrk(name, "<=>")) != 0)
+ {
+ rname = solv_strdup(name);
+ r = rname + (r - name);
+ if ((r = splitrel(rname, r, &rflags)) == 0)
+ rname = solv_free(rname);
+ }
+ }
+
+ /* check if we need to call selection_addextra */
+ noprune = doprovides && (flags & (SELECTION_WITH_DISABLED | SELECTION_WITH_BADARCH));
+
+ if (!r)
+ {
+ /* could not split off relation */
+ ret = selection_name_arch(pool, selection, name, flags, doprovides, noprune);
+ if (ret && noprune)
+ {
+ selection_addextra(pool, selection, flags);
+ selection_prune(pool, selection);
+ }
+ return ret && selection->count ? ret : 0;
+ }
+
+ /* we could split of a relation. prune name and then filter rel */
+ ret = selection_name_arch(pool, selection, rname, flags, doprovides, noprune);
+ if (ret)
+ {
+ selection_filter_rel_noprune(pool, selection, rflags, pool_str2id(pool, r, 1));
+ if (noprune)
+ selection_addextra(pool, selection, flags);
+ selection_prune(pool, selection);
+ }
+ solv_free(rname);
+ return ret && selection->count ? ret | SELECTION_REL : 0;
+}
+
+/***** filelist matching *****/
+
+static int
+selection_filelist_sortcmp(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = dp;
+ const Id *a = ap, *b = bp;
+ if (a[0] != b[0])
+ return strcmp(pool_id2str(pool, a[0]), pool_id2str(pool, b[0]));
+ return a[1] - b[1];
+}
+
+static int
+selection_filelist(Pool *pool, Queue *selection, const char *name, int flags)
+{
+ Dataiterator di;
+ Queue q;
+ Id id;
+ int type;
+ int i, j, lastid;
+
+ /* all files in the file list start with a '/' */
+ if (*name != '/')
+ {
+ if (!(flags & SELECTION_GLOB))
+ return 0;
+ if (*name != '*' && *name != '[' && *name != '?')
+ return 0;
+ }
+ type = !(flags & SELECTION_GLOB) || strpbrk(name, "[*?") == 0 ? SEARCH_STRING : SEARCH_GLOB;
+ if ((flags & SELECTION_NOCASE) != 0)
+ type |= SEARCH_NOCASE;
+ queue_init(&q);
+ dataiterator_init(&di, pool, flags & SELECTION_INSTALLED_ONLY ? pool->installed : 0, 0, SOLVABLE_FILELIST, name, type|SEARCH_FILES|SEARCH_COMPLETE_FILELIST);
+ while (dataiterator_step(&di))
+ {
+ Solvable *s = pool->solvables + di.solvid;
+ if (!s->repo)
+ continue;
+ if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
+ {
+ if (!(flags & SELECTION_SOURCE_ONLY) && !(flags & SELECTION_WITH_SOURCE))
+ continue;
+ if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+ continue;
+ }
+ else
+ {
+ if ((flags & SELECTION_SOURCE_ONLY) != 0)
+ continue;
+ if (s->repo != pool->installed)
+ {
+ if (!(flags & SELECTION_WITH_DISABLED) && pool_disabled_solvable(pool, s))
+ continue;
+ if (!(flags & SELECTION_WITH_BADARCH) && pool_badarch_solvable(pool, s))
+ continue;
+ }
+ }
+ if ((flags & SELECTION_FLAT) != 0)
+ {
+ /* don't bother with the complex stuff */
+ queue_push2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, di.solvid);
+ dataiterator_skip_solvable(&di);
+ continue;
+ }
+ id = pool_str2id(pool, di.kv.str, 1);
+ queue_push2(&q, id, di.solvid);
+ }
+ dataiterator_free(&di);
+ if ((flags & SELECTION_FLAT) != 0)
+ {
+ queue_free(&q);
+ return selection->count ? SELECTION_FILELIST : 0;
+ }
+ if (!q.count)
+ {
+ queue_free(&q);
+ return 0;
+ }
+ solv_sort(q.elements, q.count / 2, 2 * sizeof(Id), selection_filelist_sortcmp, pool);
+ lastid = 0;
+ queue_push2(&q, 0, 0);
+ for (i = j = 0; i < q.count; i += 2)
+ {
+ if (q.elements[i] != lastid)
+ {
+ if (j == 1)
+ queue_pushunique2(selection, SOLVER_SOLVABLE | SOLVER_NOAUTOSET, q.elements[0]);
+ else if (j > 1)
+ {
+ int k;
+ Id *idp;
+ /* check if we already have it */
+ for (k = 0; k < selection->count; k += 2)
+ {
+ if (selection->elements[k] != SOLVER_SOLVABLE_ONE_OF)
+ continue;
+ idp = pool->whatprovidesdata + selection->elements[k + 1];
+ if (!memcmp(idp, q.elements, j * sizeof(Id)) && !idp[j])
+ break;
+ }
+ if (k == selection->count)
+ queue_push2(selection, SOLVER_SOLVABLE_ONE_OF, pool_ids2whatprovides(pool, q.elements, j));
+ }
+ lastid = q.elements[i];
+ j = 0;
+ }
+ if (!j || q.elements[j - 1] != q.elements[i])
+ q.elements[j++] = q.elements[i + 1];
+ }
+ queue_free(&q);
+ return SELECTION_FILELIST;
+}
+
+
+/***** canon name matching *****/
+