+ printf("[NONE]\n");
+ return;
+ }
+ if (r == 1)
+ {
+ printf("[ALL]\n");
+ return;
+ }
+ for (i = start; i < bq->count; i++)
+ {
+ if (bq->elements[i] > 0)
+ printf(" %s", pool_solvid2str(pool, bq->elements[i]));
+ else if (bq->elements[i] < 0)
+ printf(" -%s", pool_solvid2str(pool, -bq->elements[i]));
+ else
+ printf(" ||");
+ }
+ printf("\n");
+}
+#endif
+
+
+static int
+pool_is_complex_dep_rd(Pool *pool, Reldep *rd)
+{
+ for (;;)
+ {
+ if (rd->flags == REL_AND || rd->flags == REL_COND || rd->flags == REL_UNLESS) /* those are the complex ones */
+ return 1;
+ if (rd->flags != REL_OR)
+ return 0;
+ if (ISRELDEP(rd->name) && pool_is_complex_dep_rd(pool, GETRELDEP(pool, rd->name)))
+ return 1;
+ if (!ISRELDEP(rd->evr))
+ return 0;
+ rd = GETRELDEP(pool, rd->evr);
+ }
+}
+
+static inline int
+pool_is_complex_dep(Pool *pool, Id dep)
+{
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags >= 8 && pool_is_complex_dep_rd(pool, rd))
+ return 1;
+ }
+ return 0;
+}
+
+static int normalize_dep(ExpanderCtx *xpctx, Id dep, Queue *bq, int flags);
+
+static int
+normalize_dep_or(ExpanderCtx *xpctx, Id dep1, Id dep2, Queue *bq, int flags, int invflags)
+{
+ int r1, r2, bqcnt2, bqcnt = bq->count;
+ r1 = normalize_dep(xpctx, dep1, bq, flags);
+ if (r1 == 1)
+ return 1; /* early exit */
+ bqcnt2 = bq->count;
+ r2 = normalize_dep(xpctx, dep2, bq, flags ^ invflags);
+ if (invflags)
+ r2 = invert_depblocks(xpctx, bq, bqcnt2, r2);
+ if (r1 == 1 || r2 == 1)
+ {
+ queue_truncate(bq, bqcnt);
+ return 1;
+ }
+ if (r1 == 0)
+ return r2;
+ if (r2 == 0)
+ return r1;
+ if ((flags & CPLXDEPS_TODNF) == 0)
+ return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+ return -1;
+}
+
+static int
+normalize_dep_and(ExpanderCtx *xpctx, Id dep1, Id dep2, Queue *bq, int flags, int invflags)
+{
+ int r1, r2, bqcnt2, bqcnt = bq->count;
+ r1 = normalize_dep(xpctx, dep1, bq, flags);
+ if (r1 == 0)
+ return 0; /* early exit */
+ bqcnt2 = bq->count;
+ r2 = normalize_dep(xpctx, dep2, bq, flags ^ invflags);
+ if (invflags)
+ r2 = invert_depblocks(xpctx, bq, bqcnt2, r2);
+ if (r1 == 0 || r2 == 0)
+ {
+ queue_truncate(bq, bqcnt);
+ return 0;
+ }
+ if (r1 == 1)
+ return r2;
+ if (r2 == 1)
+ return r1;
+ if ((flags & CPLXDEPS_TODNF) != 0)
+ return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+ return -1;
+}
+
+static int
+normalize_dep_if_else(ExpanderCtx *xpctx, Id dep1, Id dep2, Id dep3, Queue *bq, int flags)
+{
+ /* A IF (B ELSE C) -> (A OR ~B) AND (C OR B) */
+ int r1, r2, bqcnt2, bqcnt = bq->count;
+ r1 = normalize_dep_or(xpctx, dep1, dep2, bq, flags, CPLXDEPS_TODNF);
+ if (r1 == 0)
+ return 0; /* early exit */
+ bqcnt2 = bq->count;
+ r2 = normalize_dep_or(xpctx, dep2, dep3, bq, flags, 0);
+ if (r1 == 0 || r2 == 0)
+ {
+ queue_truncate(bq, bqcnt);
+ return 0;
+ }
+ if (r1 == 1)
+ return r2;
+ if (r2 == 1)
+ return r1;
+ if ((flags & CPLXDEPS_TODNF) != 0)
+ return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+ return -1;
+}
+
+static int
+normalize_dep_unless_else(ExpanderCtx *xpctx, Id dep1, Id dep2, Id dep3, Queue *bq, int flags)
+{
+ /* A UNLESS (B ELSE C) -> (A AND ~B) OR (C AND B) */
+ int r1, r2, bqcnt2, bqcnt = bq->count;
+ r1 = normalize_dep_and(xpctx, dep1, dep2, bq, flags, CPLXDEPS_TODNF);
+ if (r1 == 1)
+ return 1; /* early exit */
+ bqcnt2 = bq->count;
+ r2 = normalize_dep_and(xpctx, dep2, dep3, bq, flags, 0);
+ if (r1 == 1 || r2 == 1)
+ {
+ queue_truncate(bq, bqcnt);
+ return 1;
+ }
+ if (r1 == 0)
+ return r2;
+ if (r2 == 0)
+ return r1;
+ if ((flags & CPLXDEPS_TODNF) == 0)
+ return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+ return -1;
+}
+
+static int expander_isignored(Expander *xp, Solvable *s, Id req);
+
+static int
+normalize_dep(ExpanderCtx *xpctx, Id dep, Queue *bq, int flags)
+{
+ Pool *pool = xpctx->pool;
+ Id p, dp;
+
+ if (pool_is_complex_dep(pool, dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags == REL_COND)
+ {
+ Id evr = rd->evr;
+ if (ISRELDEP(evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, evr);
+ if (rd2->flags == REL_ELSE)
+ return normalize_dep_if_else(xpctx, rd->name, rd2->name, rd2->evr, bq, flags);
+ }
+ return normalize_dep_or(xpctx, rd->name, rd->evr, bq, flags, CPLXDEPS_TODNF);
+ }
+ if (rd->flags == REL_UNLESS)
+ {
+ Id evr = rd->evr;
+ if (ISRELDEP(evr))
+ {
+ Reldep *rd2 = GETRELDEP(pool, evr);
+ if (rd2->flags == REL_ELSE)
+ return normalize_dep_unless_else(xpctx, rd->name, rd2->name, rd2->evr, bq, flags);
+ }
+ return normalize_dep_and(xpctx, rd->name, rd->evr, bq, flags, CPLXDEPS_TODNF);
+ }
+ if (rd->flags == REL_OR)
+ return normalize_dep_or(xpctx, rd->name, rd->evr, bq, flags, 0);
+ if (rd->flags == REL_AND)
+ return normalize_dep_and(xpctx, rd->name, rd->evr, bq, flags, 0);
+ }
+
+ if (xpctx->ignore_s && (flags & CPLXDEPS_TODNF) == 0)
+ {
+ if (expander_isignored(xpctx->xp, xpctx->ignore_s, dep))
+ return 1;
+ }
+
+ dp = pool_whatprovides(pool, dep);
+ if (dp == 2)
+ return 1;
+ if (dp < 2 || !pool->whatprovidesdata[dp])
+ return 0;
+ if (pool->whatprovidesdata[dp] == SYSTEMSOLVABLE)
+ return 1;
+ if ((flags & CPLXDEPS_TODNF) != 0)
+ {
+ while ((p = pool->whatprovidesdata[dp++]) != 0)
+ queue_push2(bq, p, 0);
+ }
+ else
+ {
+ while ((p = pool->whatprovidesdata[dp++]) != 0)
+ queue_push(bq, p);
+ queue_push(bq, 0);
+ }
+ return -1;
+}
+
+#define ISCPLX(pool, d) (ISRELDEP(d) && GETRELID(d) >= pool->nrels)
+#define GETCPLX(pool, d) (GETRELID(d) - pool->nrels)
+#define MAKECPLX(pool, d) (MAKERELDEP(pool->nrels + d))
+
+#define DEPTYPE_REQUIRES 0
+#define DEPTYPE_CONFLICTS 1
+#define DEPTYPE_OBSOLETES 2
+#define DEPTYPE_RECOMMENDS 3
+#define DEPTYPE_PROVIDES 4
+
+#define ERROR_NOPROVIDER 1
+#define ERROR_CHOICE 2
+#define ERROR_CONFLICTINGPROVIDERS 3
+#define ERROR_PROVIDERINFO 4
+#define ERROR_PROVIDERINFO2 5
+#define ERROR_BADDEPENDENCY 6
+#define ERROR_CONFLICT 7
+#define ERROR_CONFLICT2 8
+#define ERROR_ALLCONFLICT 9
+#define ERROR_NOPROVIDERINFO 10
+
+static void
+expander_dbg(Expander *xp, const char *format, ...)
+{
+ va_list args;
+ char buf[1024];
+ int l;
+
+ if (!xp->debug)
+ return;
+ va_start(args, format);
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ l = strlen(buf);
+ if ((xp->debug & (EXPANDER_DEBUG_ALL | EXPANDER_DEBUG_STDOUT)) != 0)
+ {
+ printf("%s", buf);
+ if (buf[0] != ' ' || (l && buf[l - 1] == '\n'))
+ fflush(stdout);
+ }
+ if ((xp->debug & (EXPANDER_DEBUG_ALL | EXPANDER_DEBUG_STR)) != 0)
+ {
+ if (l >= xp->debugstrf) /* >= because of trailing \0 */
+ {
+ xp->debugstr = solv_realloc(xp->debugstr, xp->debugstrl + l + 1024);
+ xp->debugstrf = l + 1024;
+ }
+ strcpy(xp->debugstr + xp->debugstrl, buf);
+ xp->debugstrl += l;
+ xp->debugstrf -= l;
+ }
+}
+
+static void
+expander_clrdbg(Expander *xp)
+{
+ if (xp->debugstr)
+ free(xp->debugstr);
+ xp->debugstr = 0;
+ xp->debugstrl = xp->debugstrf = 0;
+}
+
+static const char *
+expander_solvid2name(Expander *xp, Id p)
+{
+ const char *n = pool_id2str(xp->pool, xp->pool->solvables[p].name);
+ Repo *r;
+ if (!xp->debug)
+ return n;
+ r = xp->pool->solvables[p].repo;
+ if (!r)
+ return n;
+ return pool_tmpjoin(xp->pool, n, "@", r->name);
+}
+
+static const char *
+expander_solvid2str(Expander *xp, Id p)
+{
+ const char *n = pool_solvid2str(xp->pool, p);
+ Repo *r;
+ if (!xp->debug)
+ return n;
+ r = xp->pool->solvables[p].repo;
+ if (!r)
+ return n;
+ return pool_tmpjoin(xp->pool, n, "@", r->name);
+}
+
+static int
+pkgname_sort_cmp(const void *ap, const void *bp, void *dp)
+{
+ Pool *pool = (Pool *)dp;
+ Id a = *(Id *)ap;
+ Id b = *(Id *)bp;
+ return strcmp(pool_id2str(pool, pool->solvables[a].name), pool_id2str(pool, pool->solvables[b].name));
+}
+
+static int
+expander_isignored(Expander *xp, Solvable *s, Id req)
+{
+ Pool *pool = xp->pool;
+ Id id = id2name(pool, req);
+ const char *n;
+
+ if (!xp->ignoreignore)
+ {
+ if (MAPTST(&xp->ignored, id))
+ return 1;
+ if (MAPTST(&xp->ignoredx, id))
+ {
+ Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, s->name), ":", pool_id2str(pool, id)), 0);
+ if (xid && MAPTST(&xp->ignored, xid))
+ return 1;
+ }
+ }
+ n = pool_id2str(pool, id);
+ if (!strncmp(n, "rpmlib(", 7))
+ {
+ MAPEXP(&xp->ignored, id);
+ MAPSET(&xp->ignored, id);
+ return 1;
+ }
+ if (*n == '/')
+ {
+ if (!xp->havefileprovides || pool->whatprovides[id] <= 1)
+ {
+ MAPEXP(&xp->ignored, id);
+ MAPSET(&xp->ignored, id);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+expander_to_cplxblks(ExpanderCtx *xpctx, Id p, Id dep, int deptype, Id *ptr)
+{
+ int blkoff = xpctx->cplxblks.count;
+ queue_push(&xpctx->cplxblks, p);
+ queue_push(&xpctx->cplxblks, dep);
+ queue_push(&xpctx->cplxblks, deptype);
+ for (;;)
+ {
+ Id pp = *ptr++;
+ queue_push(&xpctx->cplxblks, pp);
+ if (!pp)
+ break;
+ }
+ return blkoff;
+}
+
+static int
+expander_check_cplxblock(ExpanderCtx *xpctx, Id p, Id dep, int deptype, Id *ptr, int blkoff)
+{
+ Pool *pool = xpctx->pool;
+ int posn = 0, posi = 0, negn = 0, negi = 0;
+ Id pp, *ptr2 = ptr;
+ Id lastcon = 0;
+
+ while ((pp = *ptr2++) != 0)
+ {
+ if (pp > 0)
+ {
+ posn++;
+ if (MAPTST(&xpctx->installed, pp))
+ posi++;
+ }
+ else
+ {
+ if (p == -pp)
+ continue; /* ignore redundant self-entry */
+ negn++;
+ if (MAPTST(&xpctx->installed, -pp))
+ negi++;
+ else
+ lastcon = -pp;
+ }
+ }
+#if 0
+ printf("expander_check_cplxblock pos: %d,%d neg: %d,%d\n", posn, posi, negn, negi);
+#endif
+ if (posi)
+ return -1;
+ if (!posn && deptype == DEPTYPE_RECOMMENDS)
+ return -1;
+ if (negi == negn)
+ {
+ /* all neg installed */
+ if (posn)
+ {
+ /* posn > 0 and all neg installed, add to todo */
+ if (blkoff < 0)
+ blkoff = expander_to_cplxblks(xpctx, p, dep, deptype, ptr);
+#if 0
+ printf("put on todo, blkoff = %d\n", blkoff);
+#endif
+ queue_push2(&xpctx->todo, MAKECPLX(pool, blkoff), p);
+ }
+ else
+ {
+ /* no posn, conflict */
+ for (ptr2 = ptr; (pp = *ptr2++) != 0; )
+ {
+ if (p == -pp)
+ continue; /* ignore redundant self-entry */
+ if (deptype == DEPTYPE_REQUIRES)
+ {
+ /* do not report a requires as conflicts */
+ queue_push(&xpctx->errors, ERROR_NOPROVIDER);
+ queue_push2(&xpctx->errors, dep, p);
+ break;
+ }
+ queue_push(&xpctx->errors, ERROR_CONFLICT);
+ queue_push2(&xpctx->errors, p, -pp);
+ }
+ }
+ return -1;
+ }
+ else if (!posn && negn && negi == negn - 1)
+ {
+ /* add conflict */
+#if 0
+ printf("add conflict %d %d\n", lastcon, p);
+#endif
+ MAPEXP(&xpctx->conflicts, pool->nsolvables);
+ MAPSET(&xpctx->conflicts, lastcon);
+ if (p)
+ queue_push2(&xpctx->conflictsinfo, lastcon, p); /* always do this for rich deps */
+ return -1;
+ }
+ else
+ {
+#ifdef DEBUG_COND
+ printf("put/stay on cond queue, blkoff = %d\n", blkoff);
+#endif
+ /* either posn > 0 and 1 neg not installed or more than 1 neg not installed */
+ if (blkoff < 0)
+ blkoff = expander_to_cplxblks(xpctx, p, dep, deptype, ptr);
+ return blkoff;
+ }
+}
+
+static void
+expander_installed_complexdep(ExpanderCtx *xpctx, Id p, Id dep, int deptype)
+{
+ Queue *cplxq = &xpctx->cplxq;
+ int r, i, start = cplxq->count, blkoff;
+
+#if 0
+ printf("expander_installed_complexdep %s type %d\n", pool_dep2str(xpctx->pool, dep), deptype);
+#endif
+ if (deptype == DEPTYPE_CONFLICTS)
+ {
+ r = normalize_dep(xpctx, dep, cplxq, CPLXDEPS_TODNF);
+ r = invert_depblocks(xpctx, cplxq, start, r);
+ }
+ else
+ r = normalize_dep(xpctx, dep, cplxq, 0);
+#if 0
+ print_depblocks(xpctx, cplxq, start, r);
+#endif
+ if (r == 1)
+ return;
+ if (r == 0)
+ {
+ if (deptype == DEPTYPE_CONFLICTS)
+ {
+ queue_push(&xpctx->errors, ERROR_ALLCONFLICT);
+ queue_push2(&xpctx->errors, dep, p);
+ }
+ else if (deptype != DEPTYPE_RECOMMENDS)
+ {
+ queue_push(&xpctx->errors, ERROR_NOPROVIDER);
+ queue_push2(&xpctx->errors, dep, p);
+ }
+ return;
+ }
+ while (start < cplxq->count)
+ {
+ /* find end */
+ for (i = start; cplxq->elements[i] != 0; i++)
+ ;
+ blkoff = expander_check_cplxblock(xpctx, p, dep, deptype, cplxq->elements + start, -1);
+ if (blkoff >= 0)
+ {
+ Pool *pool = xpctx->pool;
+ Id p;
+ MAPEXP(&xpctx->todo_condmap, pool->nsolvables);
+ for (i = start; (p = cplxq->elements[i]) != 0; i++)
+ if (p < 0)
+ MAPSET(&xpctx->todo_condmap, -p);
+ queue_push(&xpctx->todo_cond, blkoff);
+ }
+ start = i + 1;
+ }
+ queue_truncate(cplxq, start);
+}
+
+static int
+expander_checkconflicts_complexdep(ExpanderCtx *xpctx, Id p, Id dep, int deptype, int recorderrors)
+{
+ Queue *cplxq = &xpctx->cplxq;
+ int r, i, start = cplxq->count;
+ Id pp;
+ int ret = 0;
+
+#if 0
+ printf("expander_checkconflicts_complexdep %s type %d\n", pool_dep2str(xpctx->pool, dep), deptype);
+#endif
+ if (deptype == DEPTYPE_CONFLICTS)
+ {
+ r = normalize_dep(xpctx, dep, cplxq, CPLXDEPS_TODNF);
+ r = invert_depblocks(xpctx, cplxq, start, r);
+ }
+ else
+ r = normalize_dep(xpctx, dep, cplxq, 0);
+#if 0
+ print_depblocks(xpctx, cplxq, start, r);
+#endif
+ /* r == 0: conflict with everything. Ignore here, pick error up when package gets installed */
+ if (r == 0 || r == 1)
+ return 0;
+ while (start < cplxq->count)
+ {
+ for (i = start; (pp = cplxq->elements[i]) != 0; i++)
+ if (pp > 0 || (pp < 0 && !MAPTST(&xpctx->installed, -pp)))
+ break;
+ if (pp == 0)
+ {
+ /* no pos and all neg installed -> conflict */
+ for (i = start; (pp = cplxq->elements[i]) != 0; i++)
+ {
+ pp = -cplxq->elements[i];
+ if (recorderrors)
+ {
+ queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT : ERROR_PROVIDERINFO);
+ queue_push2(&xpctx->errors, p, pp);
+ }
+ else if (xpctx->xp->debug)
+ {
+ Pool *pool = xpctx->pool;
+ expander_dbg(xpctx->xp, "ignoring provider %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_solvid2str(pool, pp));
+ }
+ ret = ret ? 1 : pp;
+ }
+ }
+ for (; cplxq->elements[i] != 0; i++)
+ ;
+ start = i + 1;
+ }
+ queue_truncate(cplxq, start);
+ return ret;
+}
+
+static void
+updateconflictsinfo(ExpanderCtx *xpctx)
+{
+ int i;
+ Pool *pool = xpctx->pool;
+ Queue *out = xpctx->out;
+ Queue *conflictsinfo = &xpctx->conflictsinfo;
+
+ if (xpctx->ignoreconflicts)
+ return;
+ for (i = xpctx->cidone; i < out->count; i++)
+ {
+ Id p, p2, pp2;
+ Id con, *conp;
+ Solvable *s;
+ p = out->elements[i];
+ s = pool->solvables + p;
+ /* keep in sync with expander_installed! */
+ if (s->conflicts)
+ {
+ conp = s->repo->idarraydata + s->conflicts;
+ while ((con = *conp++) != 0)
+ {
+ if (pool_is_complex_dep(pool, con))
+ continue; /* already pushed */
+ FOR_PROVIDES(p2, pp2, con)
+ {
+ if (p2 == p)
+ continue;
+ queue_push2(conflictsinfo, p2, p);
+ }
+ }
+ }
+ if (s->obsoletes)
+ {