Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'universe-rework'
Vsevolod Stakhov committed 11 years ago
commit eb80cd9577067950eb7c65564185eff6b4d686e2
parent 23ab3de
11 files changed +2085 -1705
modified external/Makefile.am
@@ -59,6 +59,7 @@ noinst_HEADERS= expat/amiga/expat_68k.h \
		libyaml/src/yaml_private.h \
		libyaml/win32/config.h \
		sqlite/sqlite3.h \
+
		uthash/utarray.h \
		uthash/uthash.h \
		uthash/utlist.h \
		config.h
added external/uthash/utarray.h
@@ -0,0 +1,232 @@
+
/*
+
Copyright (c) 2008-2014, Troy D. Hanson   http://troydhanson.github.com/uthash/
+
All rights reserved.
+

+
Redistribution and use in source and binary forms, with or without
+
modification, are permitted provided that the following conditions are met:
+

+
    * Redistributions of source code must retain the above copyright
+
      notice, this list of conditions and the following disclaimer.
+

+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
*/
+

+
/* a dynamic array implementation using macros 
+
 */
+
#ifndef UTARRAY_H
+
#define UTARRAY_H
+

+
#define UTARRAY_VERSION 1.9.9
+

+
#ifdef __GNUC__
+
#define _UNUSED_ __attribute__ ((__unused__)) 
+
#else
+
#define _UNUSED_ 
+
#endif
+

+
#include <stddef.h>  /* size_t */
+
#include <string.h>  /* memset, etc */
+
#include <stdlib.h>  /* exit */
+

+
#define oom() exit(-1)
+

+
typedef void (ctor_f)(void *dst, const void *src);
+
typedef void (dtor_f)(void *elt);
+
typedef void (init_f)(void *elt);
+
typedef struct {
+
    size_t sz;
+
    init_f *init;
+
    ctor_f *copy;
+
    dtor_f *dtor;
+
} UT_icd;
+

+
typedef struct {
+
    unsigned i,n;/* i: index of next available slot, n: num slots */
+
    UT_icd icd;  /* initializer, copy and destructor functions */
+
    char *d;     /* n slots of size icd->sz*/
+
} UT_array;
+

+
#define utarray_init(a,_icd) do {                                             \
+
  memset(a,0,sizeof(UT_array));                                               \
+
  (a)->icd=*_icd;                                                             \
+
} while(0)
+

+
#define utarray_done(a) do {                                                  \
+
  if ((a)->n) {                                                               \
+
    if ((a)->icd.dtor) {                                                      \
+
      size_t _ut_i;                                                           \
+
      for(_ut_i=0; _ut_i < (a)->i; _ut_i++) {                                 \
+
        (a)->icd.dtor(utarray_eltptr(a,_ut_i));                               \
+
      }                                                                       \
+
    }                                                                         \
+
    free((a)->d);                                                             \
+
  }                                                                           \
+
  (a)->n=0;                                                                   \
+
} while(0)
+

+
#define utarray_new(a,_icd) do {                                              \
+
  a=(UT_array*)malloc(sizeof(UT_array));                                      \
+
  utarray_init(a,_icd);                                                       \
+
} while(0)
+

+
#define utarray_free(a) do {                                                  \
+
  utarray_done(a);                                                            \
+
  free(a);                                                                    \
+
} while(0)
+

+
#define utarray_reserve(a,by) do {                                            \
+
  if (((a)->i+by) > ((a)->n)) {                                               \
+
    while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); }     \
+
    if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom();  \
+
  }                                                                           \
+
} while(0)
+

+
#define utarray_push_back(a,p) do {                                           \
+
  utarray_reserve(a,1);                                                       \
+
  if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); }      \
+
  else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); };              \
+
} while(0)
+

+
#define utarray_pop_back(a) do {                                              \
+
  if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); }       \
+
  else { (a)->i--; }                                                          \
+
} while(0)
+

+
#define utarray_extend_back(a) do {                                           \
+
  utarray_reserve(a,1);                                                       \
+
  if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); }            \
+
  else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); }                   \
+
  (a)->i++;                                                                   \
+
} while(0)
+

+
#define utarray_len(a) ((a)->i)
+

+
#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
+
#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) )))
+

+
#define utarray_insert(a,p,j) do {                                            \
+
  if (j > (a)->i) utarray_resize(a,j);                                        \
+
  utarray_reserve(a,1);                                                       \
+
  if ((j) < (a)->i) {                                                         \
+
    memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j),                  \
+
             ((a)->i - (j))*((a)->icd.sz));                                   \
+
  }                                                                           \
+
  if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); }             \
+
  else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); };                     \
+
  (a)->i++;                                                                   \
+
} while(0)
+

+
#define utarray_inserta(a,w,j) do {                                           \
+
  if (utarray_len(w) == 0) break;                                             \
+
  if (j > (a)->i) utarray_resize(a,j);                                        \
+
  utarray_reserve(a,utarray_len(w));                                          \
+
  if ((j) < (a)->i) {                                                         \
+
    memmove(_utarray_eltptr(a,(j)+utarray_len(w)),                            \
+
            _utarray_eltptr(a,j),                                             \
+
            ((a)->i - (j))*((a)->icd.sz));                                    \
+
  }                                                                           \
+
  if ((a)->icd.copy) {                                                        \
+
    size_t _ut_i;                                                             \
+
    for(_ut_i=0;_ut_i<(w)->i;_ut_i++) {                                       \
+
      (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i));    \
+
    }                                                                         \
+
  } else {                                                                    \
+
    memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0),                        \
+
           utarray_len(w)*((a)->icd.sz));                                     \
+
  }                                                                           \
+
  (a)->i += utarray_len(w);                                                   \
+
} while(0)
+

+
#define utarray_resize(dst,num) do {                                          \
+
  size_t _ut_i;                                                               \
+
  if (dst->i > (size_t)(num)) {                                               \
+
    if ((dst)->icd.dtor) {                                                    \
+
      for(_ut_i=num; _ut_i < dst->i; _ut_i++) {                               \
+
        (dst)->icd.dtor(utarray_eltptr(dst,_ut_i));                           \
+
      }                                                                       \
+
    }                                                                         \
+
  } else if (dst->i < (size_t)(num)) {                                        \
+
    utarray_reserve(dst,num-dst->i);                                          \
+
    if ((dst)->icd.init) {                                                    \
+
      for(_ut_i=dst->i; _ut_i < num; _ut_i++) {                               \
+
        (dst)->icd.init(utarray_eltptr(dst,_ut_i));                           \
+
      }                                                                       \
+
    } else {                                                                  \
+
      memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i));       \
+
    }                                                                         \
+
  }                                                                           \
+
  dst->i = num;                                                               \
+
} while(0)
+

+
#define utarray_concat(dst,src) do {                                          \
+
  utarray_inserta((dst),(src),utarray_len(dst));                              \
+
} while(0)
+

+
#define utarray_erase(a,pos,len) do {                                         \
+
  if ((a)->icd.dtor) {                                                        \
+
    size_t _ut_i;                                                             \
+
    for(_ut_i=0; _ut_i < len; _ut_i++) {                                      \
+
      (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i));                           \
+
    }                                                                         \
+
  }                                                                           \
+
  if ((a)->i > (pos+len)) {                                                   \
+
    memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len),          \
+
            (((a)->i)-(pos+len))*((a)->icd.sz));                              \
+
  }                                                                           \
+
  (a)->i -= (len);                                                            \
+
} while(0)
+

+
#define utarray_renew(a,u) do {                                               \
+
  if (a) utarray_clear(a); \
+
  else utarray_new((a),(u));   \
+
} while(0) 
+

+
#define utarray_clear(a) do {                                                 \
+
  if ((a)->i > 0) {                                                           \
+
    if ((a)->icd.dtor) {                                                      \
+
      size_t _ut_i;                                                           \
+
      for(_ut_i=0; _ut_i < (a)->i; _ut_i++) {                                 \
+
        (a)->icd.dtor(utarray_eltptr(a,_ut_i));                               \
+
      }                                                                       \
+
    }                                                                         \
+
    (a)->i = 0;                                                               \
+
  }                                                                           \
+
} while(0)
+

+
#define utarray_sort(a,cmp) do {                                              \
+
  qsort((a)->d, (a)->i, (a)->icd.sz, cmp);                                    \
+
} while(0)
+

+
#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp)
+

+
#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
+
#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
+
#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL))
+
#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
+
#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(size_t)(a)->icd.sz) : -1)
+

+
/* last we pre-define a few icd for common utarrays of ints and strings */
+
static void utarray_str_cpy(void *dst, const void *src) {
+
  char **_src = (char**)src, **_dst = (char**)dst;
+
  *_dst = (*_src == NULL) ? NULL : strdup(*_src);
+
}
+
static void utarray_str_dtor(void *elt) {
+
  char **eltc = (char**)elt;
+
  if (*eltc) free(*eltc);
+
}
+
static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
+
static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL};
+
static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL};
+

+

+
#endif /* UTARRAY_H */
modified libpkg/Makefile.am
@@ -22,13 +22,14 @@ libpkg_la_SOURCES= pkg.c \
			pkg_audit.c \
			pkg_checksum.c \
			pkg_config.c \
-
			pkg_conflicts.c \
			pkg_cudf.c \
			pkg_create.c \
			pkg_delete.c \
			pkg_elf.c \
			pkg_event.c \
			pkg_jobs.c \
+
			pkg_jobs_conflicts.c \
+
			pkg_jobs_universe.c \
			pkg_manifest.c \
			pkg_object.c \
			pkg_ports.c \
@@ -92,6 +93,7 @@ noinst_HEADERS= private/db_upgrades.h \
			private/pkg.h \
			private/pkg_printf.h \
			private/pkgdb.h \
-
			private/utils.h
+
			private/utils.h \
+
			private/pkg_jobs.h

SUBDIRS = repo .
deleted libpkg/pkg_conflicts.c
@@ -1,174 +0,0 @@
-
/*-
-
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
-
 * All rights reserved.
-
 *
-
 * Redistribution and use in source and binary forms, with or without
-
 * modification, are permitted provided that the following conditions
-
 * are met:
-
 * 1. Redistributions of source code must retain the above copyright
-
 *    notice, this list of conditions and the following disclaimer
-
 *    in this position and unchanged.
-
 * 2. Redistributions in binary form must reproduce the above copyright
-
 *    notice, this list of conditions and the following disclaimer in the
-
 *    documentation and/or other materials provided with the distribution.
-
 *
-
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
-
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
-
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
 */
-

-
#include <sys/param.h>
-
#include <sys/types.h>
-
#include <stdbool.h>
-
#include <stdlib.h>
-
#include <string.h>
-

-
#include "pkg.h"
-
#include "private/event.h"
-
#include "private/pkg.h"
-
#include "private/pkgdb.h"
-

-
struct pkg_conflict_chain {
-
	struct pkg_job_request *req;
-
	struct pkg_conflict_chain *next;
-
};
-

-
static int
-
pkg_conflicts_chain_cmp_cb(struct pkg_conflict_chain *a, struct pkg_conflict_chain *b)
-
{
-
	const char *vera, *verb;
-

-
	if (a->req->skip || b->req->skip) {
-
		return (a->req->skip - b->req->skip);
-
	}
-

-
	pkg_get(a->req->item->pkg, PKG_VERSION, &vera);
-
	pkg_get(b->req->item->pkg, PKG_VERSION, &verb);
-

-
	/* Inverse sort to get the maximum version as the first element */
-
	return (pkg_version_cmp(vera, verb));
-
}
-

-
static int
-
pkg_conflicts_request_resolve_chain(struct pkg *req, struct pkg_conflict_chain *chain)
-
{
-
	struct pkg_conflict_chain *elt, *selected = NULL;
-
	const char *name, *origin, *slash_pos;
-

-
	pkg_get(req, PKG_NAME, &name);
-
	/*
-
	 * First of all prefer pure origins, where the last element of
-
	 * an origin is pkg name
-
	 */
-
	LL_FOREACH(chain, elt) {
-
		pkg_get(elt->req->item->pkg, PKG_ORIGIN, &origin);
-
		slash_pos = strrchr(origin, '/');
-
		if (slash_pos != NULL) {
-
			if (strcmp(slash_pos + 1, name) == 0) {
-
				selected = elt;
-
				break;
-
			}
-
		}
-
	}
-

-
	if (selected == NULL) {
-
		/* XXX: add manual selection here */
-
		/* Sort list by version of package */
-
		LL_SORT(chain, pkg_conflicts_chain_cmp_cb);
-
		selected = chain;
-
	}
-

-
	pkg_get(selected->req->item->pkg, PKG_ORIGIN, &origin);
-
	pkg_debug(2, "select %s in the chain of conflicts for %s", origin, name);
-
	/* Disable conflicts from a request */
-
	LL_FOREACH(chain, elt) {
-
		if (elt != selected)
-
			elt->req->skip = true;
-
	}
-

-
	return (EPKG_OK);
-
}
-

-
static void
-
pkg_conflicts_request_add_chain(struct pkg_conflict_chain **chain, struct pkg_job_request *req)
-
{
-
	struct pkg_conflict_chain *elt;
-

-
	elt = calloc(1, sizeof(struct pkg_conflict_chain));
-
	if (elt == NULL) {
-
		pkg_emit_errno("resolve_request_conflicts", "calloc: struct pkg_conflict_chain");
-
	}
-
	elt->req = req;
-
	LL_PREPEND(*chain, elt);
-
}
-

-
int
-
pkg_conflicts_request_resolve(struct pkg_jobs *j)
-
{
-
	struct pkg_job_request *req, *rtmp, *found;
-
	struct pkg_conflict *c, *ctmp;
-
	struct pkg_conflict_chain *chain;
-
	const char *origin;
-

-
	HASH_ITER(hh, j->request_add, req, rtmp) {
-
		chain = NULL;
-
		if (req->skip)
-
			continue;
-

-
		HASH_ITER(hh, req->item->pkg->conflicts, c, ctmp) {
-
			HASH_FIND_STR(j->request_add, pkg_conflict_uniqueid(c), found);
-
			if (found && !found->skip) {
-
				pkg_conflicts_request_add_chain(&chain, found);
-
			}
-
		}
-
		if (chain != NULL) {
-
			pkg_get(req->item->pkg, PKG_ORIGIN, &origin);
-
			/* Add package itself */
-
			pkg_conflicts_request_add_chain(&chain, req);
-

-
			if (pkg_conflicts_request_resolve_chain(req->item->pkg, chain) != EPKG_OK) {
-
				LL_FREE(chain, free);
-
				return (EPKG_FATAL);
-
			}
-
			LL_FREE(chain, free);
-
		}
-
	}
-

-
	return (EPKG_OK);
-
}
-

-
void
-
pkg_conflicts_register(struct pkg *p1, struct pkg *p2, enum pkg_conflict_type type)
-
{
-
	struct pkg_conflict *c1, *c2, *test;
-
	const char *u1, *u2;
-

-
	pkg_get(p1, PKG_UNIQUEID, &u1);
-
	pkg_get(p2, PKG_UNIQUEID, &u2);
-

-
	pkg_conflict_new(&c1);
-
	pkg_conflict_new(&c2);
-
	if (c1 != NULL && c2 != NULL) {
-
		c1->type = c2->type = type;
-
		HASH_FIND_STR(p1->conflicts, u2, test);
-
		if (test == NULL) {
-
			sbuf_set(&c1->uniqueid, u2);
-
			HASH_ADD_KEYPTR(hh, p1->conflicts, pkg_conflict_uniqueid(c1), sbuf_size(c1->uniqueid), c1);
-
			pkg_debug(2, "registering conflict between %s and %s", u1, u2);
-
		}
-

-
		HASH_FIND_STR(p2->conflicts, u1, test);
-
		if (test == NULL) {
-
			sbuf_set(&c2->uniqueid, u1);
-
			HASH_ADD_KEYPTR(hh, p2->conflicts, pkg_conflict_uniqueid(c2), sbuf_size(c2->uniqueid), c2);
-
			pkg_debug(2, "registering conflict between %s and %s", u2, u1);
-
		}
-
	}
-
}
modified libpkg/pkg_cudf.c
@@ -40,6 +40,7 @@
#include "private/event.h"
#include "private/pkg.h"
#include "private/pkgdb.h"
+
#include "private/pkg_jobs.h"

/*
 * CUDF does not support packages with '_' in theirs names, therefore
@@ -91,7 +92,7 @@ cudf_print_element(FILE *f, const char *line, bool has_next, int *column)
}

static inline int
-
cudf_print_conflict(FILE *f, const char *origin, int ver, bool has_next, int *column)
+
cudf_print_conflict(FILE *f, const char *uid, int ver, bool has_next, int *column)
{
	int ret = 0;
	if (*column > 80) {
@@ -99,7 +100,7 @@ cudf_print_conflict(FILE *f, const char *origin, int ver, bool has_next, int *co
		ret += fprintf(f, "\n ");
	}

-
	ret += cudf_print_package_name(f, origin);
+
	ret += cudf_print_package_name(f, uid);
	ret += fprintf(f, "=%d", ver);

	if (has_next)
@@ -118,18 +119,18 @@ static int
cudf_emit_pkg(struct pkg *pkg, int version, FILE *f,
		struct pkg_job_universe_item *conflicts_chain)
{
-
	const char *origin;
+
	const char *uid;
	struct pkg_dep *dep, *dtmp;
	struct pkg_provide *prov, *ptmp;
	struct pkg_conflict *conflict, *ctmp;
	struct pkg_job_universe_item *u;
	int column = 0, ver;

-
	pkg_get(pkg, PKG_ORIGIN, &origin);
+
	pkg_get(pkg, PKG_UNIQUEID, &uid);
	if (fprintf(f, "package: ") < 0)
		return (EPKG_FATAL);

-
	if (cudf_print_package_name(f, origin) < 0)
+
	if (cudf_print_package_name(f, uid) < 0)
		return (EPKG_FATAL);

	if (fprintf(f, "\nversion: %d\n", version) < 0)
@@ -173,7 +174,7 @@ cudf_emit_pkg(struct pkg *pkg, int version, FILE *f,
		ver = 1;
		LL_FOREACH(conflicts_chain, u) {
			if (u->pkg != pkg && u->priority != INT_MIN) {
-
				if (cudf_print_conflict(f, origin, ver,
+
				if (cudf_print_conflict(f, uid, ver,
						(u->next != NULL && u->next->pkg != pkg), &column) < 0) {
					return (EPKG_FATAL);
				}
@@ -193,7 +194,7 @@ static int
cudf_emit_request_packages(const char *op, struct pkg_jobs *j, FILE *f)
{
	struct pkg_job_request *req, *tmp;
-
	const char *origin;
+
	const char *uid;
	int column = 0;
	bool printed = false;

@@ -202,8 +203,8 @@ cudf_emit_request_packages(const char *op, struct pkg_jobs *j, FILE *f)
	HASH_ITER(hh, j->request_add, req, tmp) {
		if (req->skip)
			continue;
-
		pkg_get(req->item->pkg, PKG_ORIGIN, &origin);
-
		if (cudf_print_element(f, origin,
+
		pkg_get(req->item->pkg, PKG_ORIGIN, &uid);
+
		if (cudf_print_element(f, uid,
				(req->hh.next != NULL), &column) < 0) {
			return (EPKG_FATAL);
		}
@@ -221,8 +222,8 @@ cudf_emit_request_packages(const char *op, struct pkg_jobs *j, FILE *f)
	HASH_ITER(hh, j->request_delete, req, tmp) {
		if (req->skip)
			continue;
-
		pkg_get(req->item->pkg, PKG_ORIGIN, &origin);
-
		if (cudf_print_element(f, origin,
+
		pkg_get(req->item->pkg, PKG_ORIGIN, &uid);
+
		if (cudf_print_element(f, uid,
				(req->hh.next != NULL), &column) < 0) {
			return (EPKG_FATAL);
		}
@@ -268,7 +269,7 @@ pkg_jobs_cudf_emit_file(struct pkg_jobs *j, pkg_jobs_t t, FILE *f)
	if (fprintf(f, "preamble: \n\n") < 0)
		return (EPKG_FATAL);

-
	HASH_ITER(hh, j->universe, it, itmp) {
+
	HASH_ITER(hh, j->universe->items, it, itmp) {
		/* XXX
		 * Here are dragons:
		 * after sorting it we actually modify the head of the list, but there is
@@ -316,7 +317,7 @@ pkg_jobs_cudf_emit_file(struct pkg_jobs *j, pkg_jobs_t t, FILE *f)
}

/*
-
 * Perform backward conversion of an origin replacing '@' to '_'
+
 * Perform backward conversion of an uid replacing '@' to '_'
 */
static char *
cudf_strdup(const char *in)
@@ -366,7 +367,7 @@ pkg_jobs_cudf_insert_res_job (struct pkg_solved **target,
}

struct pkg_cudf_entry {
-
	char *origin;
+
	char *uid;
	bool was_installed;
	bool installed;
	char *version;
@@ -376,13 +377,13 @@ static int
pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
{
	struct pkg_job_universe_item *it, *cur, *selected = NULL, *old = NULL, *head;
-
	const char *origin, *oldversion;
+
	const char *uid, *oldversion;
	int ver, n;

-
	HASH_FIND(hh, j->universe, entry->origin, strlen(entry->origin), it);
+
	it = pkg_jobs_universe_find(j->universe, entry->uid);
	if (it == NULL) {
		pkg_emit_error("package %s is found in CUDF output but not in the universe",
-
				entry->origin);
+
				entry->uid);
		return (EPKG_FATAL);
	}

@@ -412,21 +413,21 @@ pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
	if (selected == NULL) {
		pkg_emit_error("package %s-%d is found in CUDF output but the "
				"universe has no such version (only %d versions found)",
-
				entry->origin, ver, n);
+
				entry->uid, ver, n);
		return (EPKG_FATAL);
	}

-
	pkg_get(selected->pkg, PKG_ORIGIN, &origin);
+
	pkg_get(selected->pkg, PKG_ORIGIN, &uid);
	if (n == 1) {
		if (entry->installed && selected->pkg->type != PKG_INSTALLED) {
			pkg_debug(3, "pkg_cudf: schedule installation of %s(%d)",
-
					entry->origin, ver);
+
					entry->uid, ver);
			pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_INSTALL);
			j->count ++;
		}
		else if (!entry->installed && selected->pkg->type == PKG_INSTALLED) {
			pkg_debug(3, "pkg_cudf: schedule removing of %s(%d)",
-
					entry->origin, ver);
+
					entry->uid, ver);
			pkg_jobs_cudf_insert_res_job (&j->jobs, selected, NULL, PKG_SOLVED_DELETE);
			j->count ++;
		}
@@ -440,7 +441,7 @@ pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
			}
		}
		pkg_debug(3, "pkg_cudf: schedule upgrade of %s(to %d)",
-
				entry->origin, ver);
+
				entry->uid, ver);
		assert(old != NULL);
		/* XXX: this is a hack due to iterators stupidity */
		pkg_get(old->pkg, PKG_VERSION, &oldversion);
@@ -471,28 +472,28 @@ pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f)
			value = strsep(&begin, " \t");

		if (strcmp(param, "package") == 0) {
-
			if (cur_pkg.origin != NULL) {
+
			if (cur_pkg.uid != NULL) {
				if (pkg_jobs_cudf_add_package(j, &cur_pkg) != EPKG_OK)  {
					free(line);
					return (EPKG_FATAL);
				}
			}
-
			cur_pkg.origin = cudf_strdup(value);
+
			cur_pkg.uid = cudf_strdup(value);
			cur_pkg.was_installed = false;
			cur_pkg.installed = false;
			cur_pkg.version = NULL;
		}
		else if (strcmp(param, "version") == 0) {
-
			if (cur_pkg.origin == NULL) {
-
				pkg_emit_error("version line has no corresponding origin in CUDF output");
+
			if (cur_pkg.uid == NULL) {
+
				pkg_emit_error("version line has no corresponding uid in CUDF output");
				free(line);
				return (EPKG_FATAL);
			}
			cur_pkg.version = cudf_strdup(value);
		}
		else if (strcmp(param, "installed") == 0) {
-
			if (cur_pkg.origin == NULL) {
-
				pkg_emit_error("installed line has no corresponding origin in CUDF output");
+
			if (cur_pkg.uid == NULL) {
+
				pkg_emit_error("installed line has no corresponding uid in CUDF output");
				free(line);
				return (EPKG_FATAL);
			}
@@ -500,8 +501,8 @@ pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f)
				cur_pkg.installed = true;
		}
		else if (strcmp(param, "was-installed") == 0) {
-
			if (cur_pkg.origin == NULL) {
-
				pkg_emit_error("was-installed line has no corresponding origin in CUDF output");
+
			if (cur_pkg.uid == NULL) {
+
				pkg_emit_error("was-installed line has no corresponding uid in CUDF output");
				free(line);
				return (EPKG_FATAL);
			}
@@ -510,7 +511,7 @@ pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f)
		}
	}

-
	if (cur_pkg.origin != NULL) {
+
	if (cur_pkg.uid != NULL) {
		if (pkg_jobs_cudf_add_package(j, &cur_pkg) != EPKG_OK)  {
			free(line);
			return (EPKG_FATAL);
modified libpkg/pkg_jobs.c
@@ -47,14 +47,10 @@
#include "private/event.h"
#include "private/pkg.h"
#include "private/pkgdb.h"
+
#include "private/pkg_jobs.h"

-
static int pkg_jobs_find_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m,
-
		bool root, bool recursive, bool add_request);
-
static struct pkg *pkg_jobs_get_local_pkg(struct pkg_jobs *j, const char *uid, unsigned flag);
-
static struct pkg *pkg_jobs_get_remote_pkg(struct pkg_jobs *j, const char *uid, unsigned flag);
+
static int pkg_jobs_find_upgrade(struct pkg_jobs *j, const char *pattern, match_t m);
static int pkg_jobs_fetch(struct pkg_jobs *j);
-
static bool pkg_jobs_newer_than_local(struct pkg_jobs *j, struct pkg *rp, bool force);
-
static bool pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive);
static bool new_pkg_version(struct pkg_jobs *j);
static int pkg_jobs_check_conflicts(struct pkg_jobs *j);

@@ -70,6 +66,13 @@ pkg_jobs_new(struct pkg_jobs **j, pkg_jobs_t t, struct pkgdb *db)
		return (EPKG_FATAL);
	}

+
	(*j)->universe = pkg_jobs_universe_new(*j);
+

+
	if ((*j)->universe == NULL) {
+
		free(*j);
+
		return (EPKG_FATAL);
+
	}
+

	(*j)->db = db;
	(*j)->type = t;
	(*j)->solved = 0;
@@ -125,30 +128,10 @@ pkg_jobs_pattern_free(struct job_pattern *jp)
	free(jp);
}

-
static void
-
pkg_jobs_provide_free(struct pkg_job_provide *pr)
-
{
-
	struct pkg_job_provide *cur, *tmp;
-

-
	DL_FOREACH_SAFE(pr, cur, tmp) {
-
		free (cur);
-
	}
-
}
-

-
static void
-
pkg_jobs_replacement_free(struct pkg_job_replace *r)
-
{
-
	free(r->new_uid);
-
	free(r->old_uid);
-
	free(r);
-
}
-

void
pkg_jobs_free(struct pkg_jobs *j)
{
	struct pkg_job_request *req, *tmp;
-
	struct pkg_job_universe_item *un, *untmp, *cur, *curtmp;
-
	struct pkg *reinstall = NULL;

	if (j == NULL)
		return;
@@ -161,29 +144,10 @@ pkg_jobs_free(struct pkg_jobs *j)
		HASH_DEL(j->request_delete, req);
		free(req);
	}
-
	HASH_ITER(hh, j->universe, un, untmp) {
-
		HASH_DEL(j->universe, un);
-

-
		if (un->reinstall != NULL)
-
			reinstall = un->reinstall;
-

-
		LL_FOREACH_SAFE(un, cur, curtmp) {
-
			if (cur->pkg != reinstall)
-
				pkg_free(cur->pkg);
-
			free(cur);
-
		}

-
		if (reinstall != NULL)
-
			pkg_free(reinstall);
-

-
		reinstall = NULL;
-
	}
-
	HASH_FREE(j->seen, free);
-
	HASH_FREE(j->patterns, pkg_jobs_pattern_free);
-
	HASH_FREE(j->provides, pkg_jobs_provide_free);
+
	pkg_jobs_universe_free(j->universe);
	LL_FREE(j->jobs, free);
-
	LL_FREE(j->uid_replaces, pkg_jobs_replacement_free);
-

+
	HASH_FREE(j->patterns, pkg_jobs_pattern_free);
	free(j);
}

@@ -288,7 +252,7 @@ pkg_jobs_iter(struct pkg_jobs *jobs, void **iter,
	return (true);
}

-
static void
+
void
pkg_jobs_add_req(struct pkg_jobs *j, const char *uid,
		struct pkg_job_universe_item *item)
{
@@ -299,7 +263,7 @@ pkg_jobs_add_req(struct pkg_jobs *j, const char *uid,
	else
		head = &j->request_delete;

-
	HASH_FIND(hh, *head, uid, strlen(uid), test);
+
	HASH_FIND_PTR(*head, &item, test);

	if (test != NULL)
		return;
@@ -311,157 +275,7 @@ pkg_jobs_add_req(struct pkg_jobs *j, const char *uid,
	}
	req->item = item;

-
	HASH_ADD_KEYPTR(hh, *head, uid, strlen(uid), req);
-
}
-

-
enum pkg_priority_update_type {
-
	PKG_PRIORITY_UPDATE_REQUEST = 0,
-
	PKG_PRIORITY_UPDATE_UNIVERSE,
-
	PKG_PRIORITY_UPDATE_CONFLICT,
-
	PKG_PRIORITY_UPDATE_DELETE
-
};
-

-
#define RECURSION_LIMIT 1024
-

-
static void
-
pkg_jobs_update_universe_priority(struct pkg_jobs *j,
-
		struct pkg_job_universe_item *item, int priority,
-
		enum pkg_priority_update_type type)
-
{
-
	const char *uid, *digest;
-
	struct pkg_dep *d = NULL;
-
	struct pkg_conflict *c = NULL;
-
	struct pkg_job_universe_item *found, *cur, *it;
-
	const char *is_local;
-
	int maxpri;
-

-
	int (*deps_func)(const struct pkg *pkg, struct pkg_dep **d);
-
	int (*rdeps_func)(const struct pkg *pkg, struct pkg_dep **d);
-

-
	if (priority > RECURSION_LIMIT) {
-
		pkg_debug(1, "recursion limit has been reached, something is bad"
-
					" with dependencies/conflicts graph");
-
		return;
-
	}
-
	else if (priority + 10 > RECURSION_LIMIT) {
-
		pkg_get(item->pkg, PKG_UNIQUEID, &uid);
-
		pkg_debug(2, "approaching recursion limit at %d, while processing of"
-
					" package %s", priority, uid);
-
	}
-

-
	LL_FOREACH(item, it) {
-

-
		pkg_get(it->pkg, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest);
-
		if ((item->next != NULL || item->prev != NULL) &&
-
				it->pkg->type != PKG_INSTALLED &&
-
				(type == PKG_PRIORITY_UPDATE_CONFLICT ||
-
				 type == PKG_PRIORITY_UPDATE_DELETE)) {
-
			/*
-
			 * We do not update priority of a remote part of conflict, as we know
-
			 * that remote packages should not contain conflicts (they should be
-
			 * resolved in request prior to calling of this function)
-
			 */
-
			pkg_debug(4, "skip update priority for %s-%s", uid, digest);
-
			continue;
-
		}
-
		if (it->priority > priority)
-
			continue;
-

-
		is_local = it->pkg->type == PKG_INSTALLED ? "local" : "remote";
-
		pkg_debug(2, "universe: update %s priority of %s(%s): %d -> %d, reason: %d",
-
				is_local, uid, digest, it->priority, priority, type);
-
		it->priority = priority;
-

-
		if (type == PKG_PRIORITY_UPDATE_DELETE) {
-
			/*
-
			 * For delete requests we inverse deps and rdeps logic
-
			 */
-
			deps_func = pkg_rdeps;
-
			rdeps_func = pkg_deps;
-
		}
-
		else {
-
			deps_func = pkg_deps;
-
			rdeps_func = pkg_rdeps;
-
		}
-

-
		while (deps_func(it->pkg, &d) == EPKG_OK) {
-
			HASH_FIND_STR(j->universe, d->uid, found);
-
			if (found != NULL) {
-
				LL_FOREACH(found, cur) {
-
					if (cur->priority < priority + 1)
-
						pkg_jobs_update_universe_priority(j, cur,
-
								priority + 1, type);
-
				}
-
			}
-
		}
-

-
		d = NULL;
-
		maxpri = priority;
-
		while (rdeps_func(it->pkg, &d) == EPKG_OK) {
-
			HASH_FIND_STR(j->universe, d->uid, found);
-
			if (found != NULL) {
-
				LL_FOREACH(found, cur) {
-
					if (cur->priority >= maxpri) {
-
						maxpri = cur->priority + 1;
-
					}
-
				}
-
			}
-
		}
-
		if (maxpri != priority) {
-
			pkg_jobs_update_universe_priority(j, it,
-
					maxpri, type);
-
			return;
-
		}
-
		if (it->pkg->type != PKG_INSTALLED) {
-
			while (pkg_conflicts(it->pkg, &c) == EPKG_OK) {
-
				HASH_FIND_STR(j->universe, pkg_conflict_uniqueid(c), found);
-
				if (found != NULL) {
-
					LL_FOREACH(found, cur) {
-
						if (cur->pkg->type == PKG_INSTALLED) {
-
							/*
-
							 * Move delete requests to be done before installing
-
							 */
-
							if (cur->priority <= it->priority)
-
								pkg_jobs_update_universe_priority(j, cur,
-
									it->priority + 1, PKG_PRIORITY_UPDATE_CONFLICT);
-
						}
-
					}
-
				}
-
			}
-
		}
-
	}
-
}
-

-
static void
-
pkg_jobs_update_conflict_priority(struct pkg_jobs *j, struct pkg_solved *req)
-
{
-
	struct pkg_conflict *c = NULL;
-
	struct pkg *lp = req->items[1]->pkg;
-
	struct pkg_job_universe_item *found, *cur, *rit = NULL;
-

-
	while (pkg_conflicts(lp, &c) == EPKG_OK) {
-
		rit = NULL;
-
		HASH_FIND_STR(j->universe, pkg_conflict_uniqueid(c), found);
-
		assert(found != NULL);
-

-
		LL_FOREACH(found, cur) {
-
			if (cur->pkg->type != PKG_INSTALLED) {
-
				rit = cur;
-
				break;
-
			}
-
		}
-

-
		assert(rit != NULL);
-
		if (rit->priority >= req->items[1]->priority) {
-
			pkg_jobs_update_universe_priority(j, req->items[1],
-
					rit->priority + 1, PKG_PRIORITY_UPDATE_CONFLICT);
-
			/*
-
			 * Update priorities for a remote part as well
-
			 */
-
			pkg_jobs_update_universe_priority(j, req->items[0],
-
					req->items[0]->priority, PKG_PRIORITY_UPDATE_REQUEST);
-
		}
-
	}
+
	HASH_ADD_PTR(*head, item, req);
}

static int
@@ -477,7 +291,7 @@ pkg_jobs_set_request_priority(struct pkg_jobs *j, struct pkg_solved *req)
		 * update priorities of local packages and try to update priorities of remote ones
		 */
		if (req->items[0]->priority == 0)
-
			pkg_jobs_update_conflict_priority(j, req);
+
			pkg_jobs_update_conflict_priority(j->universe, req);

		if (req->items[1]->priority > req->items[0]->priority &&
				!req->already_deleted) {
@@ -502,12 +316,12 @@ pkg_jobs_set_request_priority(struct pkg_jobs *j, struct pkg_solved *req)
	}
	else if (req->type == PKG_SOLVED_DELETE) {
		if (req->items[0]->priority == 0)
-
			pkg_jobs_update_universe_priority(j, req->items[0], 0,
+
			pkg_jobs_update_universe_priority(j->universe, req->items[0],
					PKG_PRIORITY_UPDATE_DELETE);
	}
	else {
		if (req->items[0]->priority == 0)
-
			pkg_jobs_update_universe_priority(j, req->items[0], 0,
+
			pkg_jobs_update_universe_priority(j->universe, req->items[0],
					PKG_PRIORITY_UPDATE_REQUEST);
	}

@@ -547,335 +361,6 @@ iter_again:
	DL_SORT(j->jobs, pkg_jobs_sort_priority);
}

-
#undef PRIORITY_CAN_UPDATE
-

-

-
/**
-
 * Check whether a package is in the universe already or add it
-
 * @return item or NULL
-
 */
-
static int
-
pkg_jobs_handle_pkg_universe(struct pkg_jobs *j, struct pkg *pkg,
-
		struct pkg_job_universe_item **found)
-
{
-
	struct pkg_job_universe_item *item, *tmp = NULL;
-
	const char *uid, *digest, *version, *name;
-
	struct pkg_job_seen *seen;
-

-
	pkg_get(pkg, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest,
-
			PKG_VERSION, &version, PKG_NAME, &name);
-
	if (digest == NULL) {
-
		pkg_debug(3, "no digest found for package %s (%s-%s)", uid,
-
				name, version);
-
		if (pkg_checksum_calculate(pkg, j->db) != EPKG_OK) {
-
			*found = NULL;
-
			return (EPKG_FATAL);
-
		}
-
		pkg_get(pkg, PKG_DIGEST, &digest);
-
	}
-

-
	HASH_FIND_STR(j->seen, digest, seen);
-
	if (seen != NULL) {
-
		if (found != NULL)
-
			*found = seen->un;
-

-
		return (EPKG_END);
-
	}
-

-
	pkg_debug(2, "universe: add new %s pkg: %s, (%s-%s:%s)",
-
				(pkg->type == PKG_INSTALLED ? "local" : "remote"), uid,
-
				name, version, digest);
-

-
	item = calloc(1, sizeof (struct pkg_job_universe_item));
-
	if (item == NULL) {
-
		pkg_emit_errno("pkg_jobs_pkg_insert_universe", "calloc: struct pkg_job_universe_item");
-
		return (EPKG_FATAL);
-
	}
-

-
	item->pkg = pkg;
-

-
	HASH_FIND_STR(j->universe, uid, tmp);
-
	if (tmp == NULL) {
-
		HASH_ADD_KEYPTR(hh, j->universe, uid, strlen(uid), item);
-
	}
-

-
	DL_APPEND(tmp, item);
-

-
	seen = calloc(1, sizeof(struct pkg_job_seen));
-
	seen->digest = digest;
-
	seen->un = item;
-
	HASH_ADD_KEYPTR(hh, j->seen, seen->digest, strlen(seen->digest), seen);
-

-
	j->total++;
-

-
	if (found != NULL)
-
		*found = item;
-

-
	return (EPKG_OK);
-
}
-

-
static int
-
pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
-
		bool recursive, bool deps_only, struct pkg_job_universe_item **result)
-
{
-
	struct pkg_dep *d = NULL;
-
	struct pkg_conflict *c = NULL;
-
	struct pkg *npkg, *rpkg;
-
	struct pkg_job_universe_item *unit;
-
	struct pkg_shlib *shlib = NULL;
-
	struct pkgdb_it *it;
-
	struct pkg_job_provide *pr, *prhead;
-
	struct pkg_job_seen *seen;
-
	int ret;
-
	bool automatic = false, mirror = false;
-
	const char *uid, *name, *digest;
-
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
-
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
-
			PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
-

-
	if (!deps_only) {
-
		/* Add the requested package itself */
-
		ret = pkg_jobs_handle_pkg_universe(j, pkg, result);
-

-
		if (ret == EPKG_END)
-
			return (EPKG_OK);
-
		else if (ret == EPKG_OK && !recursive)
-
			return (EPKG_OK);
-
		else if (ret != EPKG_OK)
-
			return (EPKG_FATAL);
-
	}
-

-
	mirror = (j->flags & PKG_FLAG_FETCH_MIRROR) ? true : false;
-

-
	/* Go through all depends */
-
	while (pkg_deps(pkg, &d) == EPKG_OK) {
-
		/* XXX: this assumption can be applied only for the current plain dependencies */
-
		HASH_FIND_STR(j->universe, d->uid, unit);
-
		if (unit != NULL)
-
			continue;
-

-
		rpkg = NULL;
-
		npkg = NULL;
-
		if (!mirror)
-
			npkg = pkg_jobs_get_local_pkg(j, d->uid, 0);
-

-
		if (npkg == NULL && !IS_DELETE(j)) {
-
			/*
-
			 * We have a package installed, but its dependencies are not,
-
			 * try to search a remote dependency
-
			 */
-
			if (!mirror) {
-
				pkg_get(pkg, PKG_NAME, &name);
-
				pkg_debug(1, "dependency %s of local package %s is not installed",
-
					pkg_dep_get(d, PKG_DEP_NAME), name);
-
			}
-
			npkg = pkg_jobs_get_remote_pkg(j, d->uid, 0);
-
			if (npkg == NULL) {
-
				/* Cannot continue */
-
				pkg_emit_error("%s has a missing dependency: %s",
-
						name, pkg_dep_get(d, PKG_DEP_NAME));
-

-
				if ((j->flags & PKG_FLAG_FORCE_MISSING) == PKG_FLAG_FORCE_MISSING)
-
					continue;
-

-
				return (EPKG_FATAL);
-
			}
-
		}
-
		else if (npkg == NULL) {
-
			/* For delete jobs we don't care about uninstalled dependencies */
-
			continue;
-
		}
-
		else if (!IS_DELETE(j) && npkg->type == PKG_INSTALLED) {
-
			/* For upgrade jobs we need to ensure that we do not have a newer version */
-
			rpkg = pkg_jobs_get_remote_pkg(j, d->uid, 0);
-
			if (rpkg != NULL) {
-
				if (!pkg_need_upgrade(rpkg, npkg, false)) {
-
					/*
-
					 * We can do it safely here, as rpkg is definitely NOT in
-
					 * the universe
-
					 */
-
					pkg_free(rpkg);
-
					rpkg = NULL;
-
				}
-
			}
-
		}
-

-
		if (pkg_jobs_add_universe(j, npkg, recursive, false, &unit) != EPKG_OK)
-
			continue;
-

-
		if (mirror)
-
			pkg_jobs_add_req(j, d->uid, unit);
-

-
		if (rpkg != NULL) {
-
			/* Save automatic flag */
-
			pkg_get(npkg, PKG_AUTOMATIC, &automatic);
-
			pkg_set(rpkg, PKG_AUTOMATIC, automatic);
-

-
			if (pkg_jobs_add_universe(j, rpkg, recursive, false, NULL) != EPKG_OK)
-
				continue;
-
		}
-
	}
-

-
	/*
-
	 * XXX: handle shlibs somehow
-
	 */
-
	if (mirror)
-
		return (EPKG_OK);
-

-
	/* Go through all rdeps */
-
	d = NULL;
-
	while (pkg_rdeps(pkg, &d) == EPKG_OK) {
-
		/* XXX: this assumption can be applied only for the current plain dependencies */
-
		HASH_FIND_STR(j->universe, d->uid, unit);
-
		if (unit != NULL)
-
			continue;
-

-
		npkg = pkg_jobs_get_local_pkg(j, d->uid, 0);
-
		if (npkg != NULL) {
-
			/* Do not bother about remote deps */
-
			if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
-
				continue;
-
		}
-
	}
-

-
	if (!IS_DELETE(j)) {
-
		/* Examine conflicts */
-
		while (pkg_conflicts(pkg, &c) == EPKG_OK) {
-
			/* XXX: this assumption can be applied only for the current plain dependencies */
-
			HASH_FIND_STR(j->universe, pkg_conflict_uniqueid(c), unit);
-
			if (unit != NULL)
-
				continue;
-

-
			/* Check local and remote conflicts */
-
			if (pkg->type == PKG_INSTALLED) {
-
				/* Installed packages can conflict with remote ones */
-
				npkg = pkg_jobs_get_remote_pkg(j, pkg_conflict_uniqueid(c), 0);
-
				if (npkg == NULL)
-
					continue;
-

-
				if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
-
					continue;
-
			}
-
			else {
-
				/* Remote packages can conflict with remote and local */
-
				npkg = pkg_jobs_get_local_pkg(j, pkg_conflict_uniqueid(c), 0);
-
				if (npkg == NULL)
-
					continue;
-

-
				if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
-
					continue;
-

-
				if (c->type != PKG_CONFLICT_REMOTE_LOCAL) {
-
					npkg = pkg_jobs_get_remote_pkg(j, pkg_conflict_uniqueid(c), 0);
-
					if (npkg == NULL)
-
						continue;
-

-
					if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL)
-
							!= EPKG_OK)
-
						continue;
-
				}
-
			}
-
		}
-
	}
-

-
	/* For remote packages we should also handle shlib deps */
-
	if (pkg->type != PKG_INSTALLED && !IS_DELETE(j)) {
-
		while (pkg_shlibs_required(pkg, &shlib) == EPKG_OK) {
-
			HASH_FIND_STR(j->provides, pkg_shlib_name(shlib), pr);
-
			if (pr != NULL)
-
				continue;
-

-
			/* Not found, search in the repos */
-
			it = pkgdb_repo_shlib_provide(j->db, pkg_shlib_name(shlib), j->reponame);
-
			if (it != NULL) {
-
				rpkg = NULL;
-
				prhead = NULL;
-
				while (pkgdb_it_next(it, &rpkg, flags) == EPKG_OK) {
-
					pkg_get(rpkg, PKG_DIGEST, &digest, PKG_UNIQUEID, &uid);
-
					/* Check for local packages */
-
					HASH_FIND_STR(j->universe, uid, unit);
-
					if (unit != NULL) {
-
						if (pkg_need_upgrade (rpkg, unit->pkg, false)) {
-
							/* Remote provide is newer, so we can add it */
-
							if (pkg_jobs_add_universe(j, rpkg, recursive, false,
-
																&unit) != EPKG_OK)
-
								continue;
-

-
							rpkg = NULL;
-
						}
-
					}
-
					else {
-
						/* Maybe local package has just been not added */
-
						npkg = pkg_jobs_get_local_pkg(j, uid, 0);
-
						if (npkg != NULL) {
-
							if (pkg_jobs_add_universe(j, npkg, recursive, false,
-
									&unit) != EPKG_OK)
-
								return (EPKG_FATAL);
-
							if (pkg_need_upgrade (rpkg, npkg, false)) {
-
								/* Remote provide is newer, so we can add it */
-
								if (pkg_jobs_add_universe(j, rpkg, recursive, false,
-
										&unit) != EPKG_OK)
-
									continue;
-
							}
-
						}
-
					}
-
					/* Skip seen packages */
-
					if (unit == NULL) {
-
						if (digest == NULL) {
-
							pkg_debug(3, "no digest found for package %s", uid);
-
							if (pkg_checksum_calculate(pkg, j->db) != EPKG_OK) {
-
								return (EPKG_FATAL);
-
							}
-
							pkg_get(pkg, PKG_DIGEST, &digest);
-
						}
-
						HASH_FIND_STR(j->seen, digest, seen);
-
						if (seen == NULL) {
-
							pkg_jobs_add_universe(j, rpkg, recursive, false,
-
									&unit);
-

-
							/* Reset package to avoid freeing */
-
							rpkg = NULL;
-
						}
-
						else {
-
							unit = seen->un;
-
						}
-
					}
-

-
					pr = calloc (1, sizeof (*pr));
-
					if (pr == NULL) {
-
						pkg_emit_errno("pkg_jobs_add_universe", "calloc: "
-
								"struct pkg_job_provide");
-
						return (EPKG_FATAL);
-
					}
-

-
					pr->un = unit;
-
					pr->provide = pkg_shlib_name(shlib);
-

-
					if (prhead == NULL) {
-
						DL_APPEND(prhead, pr);
-
						HASH_ADD_KEYPTR(hh, j->provides, pr->provide,
-
								strlen(pr->provide), prhead);
-
					}
-
					else {
-
						DL_APPEND(prhead, pr);
-
					}
-
				}
-
				pkgdb_it_free(it);
-
				if (prhead == NULL) {
-
					pkg_get(pkg, PKG_NAME, &name);
-
					pkg_debug(1, "cannot find packages that provide %s required for %s",
-
							pkg_shlib_name(shlib), name);
-
					/*
-
					 * XXX: this is not normal but it is very common for the existing
-
					 * repos, hence we just ignore this stale dependency
-
					 */
-
				}
-
			}
-
		}
-
	}
-

-
	return (EPKG_OK);
-
}

/**
 * Test whether package specified is automatic with all its rdeps
@@ -893,7 +378,7 @@ pkg_jobs_test_automatic(struct pkg_jobs *j, struct pkg *p)
	bool automatic;

	while (pkg_rdeps(p, &d) == EPKG_OK && ret) {
-
		HASH_FIND_STR(j->universe, d->uid, unit);
+
		unit = pkg_jobs_universe_find(j->universe, d->uid);
		if (unit != NULL) {
			pkg_get(unit->pkg, PKG_AUTOMATIC, &automatic);
			if (!automatic) {
@@ -902,7 +387,7 @@ pkg_jobs_test_automatic(struct pkg_jobs *j, struct pkg *p)
			npkg = unit->pkg;
		}
		else {
-
			npkg = pkg_jobs_get_local_pkg(j, d->uid,
+
			npkg = pkg_jobs_universe_get_local(j->universe, d->uid,
					PKG_LOAD_BASIC|PKG_LOAD_RDEPS|PKG_LOAD_ANNOTATIONS);
			pkg_get(npkg, PKG_AUTOMATIC, &automatic);
			if (npkg == NULL)
@@ -914,7 +399,7 @@ pkg_jobs_test_automatic(struct pkg_jobs *j, struct pkg *p)
				pkg_free(npkg);
				return (false);
			}
-
			if (pkg_jobs_add_universe(j, npkg, false, false, NULL) != EPKG_OK)
+
			if (pkg_jobs_universe_process(j->universe, npkg) != EPKG_OK)
				return (false);
		}

@@ -939,11 +424,11 @@ new_pkg_version(struct pkg_jobs *j)
	j->flags &= ~(PKG_FLAG_FORCE|PKG_FLAG_RECURSIVE);

	/* determine local pkgng */
-
	p = pkg_jobs_get_local_pkg(j, uid, PKG_LOAD_BASIC|PKG_LOAD_ANNOTATIONS);
+
	p = pkg_jobs_universe_get_local(j->universe, uid, 0);

	if (p == NULL) {
		uid = "pkg~ports-mgmt/pkg-devel";
-
		p = pkg_jobs_get_local_pkg(j, uid, PKG_LOAD_BASIC|PKG_LOAD_ANNOTATIONS);
+
		p = pkg_jobs_universe_get_local(j->universe, uid, 0);
	}

	/* you are using git version skip */
@@ -952,10 +437,10 @@ new_pkg_version(struct pkg_jobs *j)
		goto end;
	}

-
	pkg_jobs_add_universe(j, p, true, false, NULL);
+
	pkg_jobs_universe_process(j->universe, p);

	/* Use maximum priority for pkg */
-
	if (pkg_jobs_find_remote_pkg(j, uid, MATCH_EXACT, false, true, true) == EPKG_OK) {
+
	if (pkg_jobs_find_upgrade(j, uid, MATCH_EXACT) == EPKG_OK) {
		ret = true;
		goto end;
	}
@@ -967,10 +452,8 @@ end:
}

static int
-
pkg_jobs_process_remote_pkg(struct pkg_jobs *j, struct pkg *p,
-
		bool root, bool force, bool recursive,
-
		struct pkg_job_universe_item **unit,
-
		bool add_request)
+
pkg_jobs_process_remote_pkg(struct pkg_jobs *j, struct pkg *rp,
+
		bool force, struct pkg_job_universe_item **unit)
{
	struct pkg_job_universe_item *jit;
	struct pkg_job_seen *seen;
@@ -978,42 +461,59 @@ pkg_jobs_process_remote_pkg(struct pkg_jobs *j, struct pkg *p,
	int rc = EPKG_FATAL;
	bool automatic;
	const char *uid, *digest;
+
	struct pkg *lp = NULL;

-
	pkg_get(p, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest);
+
	pkg_get(rp, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest);

	if (digest == NULL) {
-
		if (pkg_checksum_calculate(p, j->db) != EPKG_OK) {
+
		if (pkg_checksum_calculate(rp, j->db) != EPKG_OK) {
			return (EPKG_FATAL);
		}
-
		pkg_get(p, PKG_DIGEST, &digest);
+
		pkg_get(rp, PKG_DIGEST, &digest);
	}
-
	HASH_FIND_STR(j->seen, digest, seen);
+
	seen = pkg_jobs_universe_seen(j->universe, digest);
	if (seen != NULL) {
		/* We have already added exactly the same package to the universe */
		pkg_debug(3, "already seen package %s-%s(%c) in the universe, do not add it again",
				uid, digest,
				seen->un->pkg->type == PKG_INSTALLED ? 'l' : 'r');
-
		if (seen->un->pkg->type != p->type && !force) {
+
		if (seen->un->pkg->type != rp->type && !force) {
			/* Remote package is the same as local */
			return (EPKG_INSTALLED);
		}
		else {
			/* However, we may want to add it to the job request */
-
			HASH_FIND_STR(j->request_add, uid, jreq);
+
			HASH_FIND_PTR(j->request_add, &seen->un, jreq);
			if (jreq == NULL)
				pkg_jobs_add_req(j, uid, seen->un);
			if (force) {
-
				seen->un->reinstall = p;
+
				pkg_jobs_universe_add_pkg(j->universe, rp, true, NULL);
				pkg_get(seen->un->pkg, PKG_AUTOMATIC, &automatic);
-
				pkg_set(p, PKG_AUTOMATIC, automatic);
+
				pkg_set(rp, PKG_AUTOMATIC, automatic);
			}
		}
		return (EPKG_OK);
	}
-
	HASH_FIND_STR(j->universe, uid, jit);
+
	jit = pkg_jobs_universe_find(j->universe, uid);
	if (jit != NULL) {
-
		/* We have a more recent package */
-
		if (!force && !pkg_need_upgrade(p, jit->pkg, false)) {
+
		lp = jit->pkg;
+

+
		/*
+
		 * XXX: what happens if we have multirepo enabled and we add
+
		 * two packages from different repos?
+
		 */
+
	}
+
	else {
+
		if (j->type != PKG_JOBS_FETCH) {
+
			lp = pkg_jobs_universe_get_local(j->universe, uid, 0);
+
			if (lp != NULL)
+
				pkg_jobs_universe_process_item(j->universe, lp, &jit);
+
		}
+
	}
+

+
	/* We have a more recent package */
+
	if (lp != NULL) {
+
		if (!force && !pkg_jobs_need_upgrade(rp, lp)) {
			/*
			 * We can have package from another repo in the
			 * universe, but if it is older than this one we just
@@ -1023,86 +523,25 @@ pkg_jobs_process_remote_pkg(struct pkg_jobs *j, struct pkg *p,
				return (EPKG_INSTALLED);
			}
			else {
-
				pkg_debug(3, "already added newer package %s to the universe, do not add it again",
-
								uid);
-
				if (add_request)
-
					pkg_jobs_add_req(j, uid, jit);
+
				pkg_debug(3, "already added newer package %s to the universe, "
+
							"do not add it again", uid);
				return (EPKG_OK);
			}
		}
-
		/*
-
		 * XXX: what happens if we have multirepo enabled and we add
-
		 * two packages from different repos?
-
		 */
-
	}
-
	else {
-
		if (j->type != PKG_JOBS_FETCH) {
-
			if (!pkg_jobs_newer_than_local(j, p, force)) {
-
				return (EPKG_INSTALLED);
-
			}
-
		}
	}

	rc = EPKG_OK;
-
	p->direct = root;
	/* Add a package to request chain and populate universe */
-
	rc = pkg_jobs_add_universe(j, p, recursive, false, &jit);
-
	if (add_request)
-
		pkg_jobs_add_req(j, uid, jit);
-

-
	if (unit != NULL)
-
		*unit = jit;
-

-
	return (rc);
-
}
+
	rc = pkg_jobs_universe_process_item(j->universe, rp, &jit);

-
static void
-
pkg_jobs_change_uid(struct pkg_jobs *j, struct pkg_job_universe_item *unit,
-
	const char *new_uid, size_t uidlen, bool update_rdeps)
-
{
-
	struct pkg_dep *rd = NULL, *d = NULL;
-
	struct pkg_job_universe_item *found;
-
	struct pkg *lp;
-
	const char *old_uid;
-
	struct pkg_job_replace *replacement;
-

-
	pkg_get(unit->pkg, PKG_UNIQUEID, &old_uid);
-

-
	if (update_rdeps) {
-
		/* For all rdeps update deps accordingly */
-
		while (pkg_rdeps(unit->pkg, &rd) == EPKG_OK) {
-
			HASH_FIND(hh, j->universe, rd->uid, strlen(rd->uid), found);
-
			if (found == NULL) {
-
				lp = pkg_jobs_get_local_pkg(j, rd->uid, 0);
-
				pkg_jobs_add_universe(j, lp, true, false, &found);
-
			}
-

-
			if (found != NULL) {
-
				while (pkg_deps(found->pkg, &d) == EPKG_OK) {
-
					if (strcmp(d->uid, old_uid) == 0) {
-
						free(d->uid);
-
						d->uid = strdup(new_uid);
-
					}
-
				}
-
			}
-
		}
-
	}
+
	if (rc == EPKG_OK) {
+
		if (unit != NULL)
+
			*unit = jit;

-
	replacement = calloc(1, sizeof(*replacement));
-
	if (replacement != NULL) {
-
		replacement->old_uid = strdup(old_uid);
-
		replacement->new_uid = strdup(new_uid);
-
		LL_PREPEND(j->uid_replaces, replacement);
+
		pkg_jobs_add_req(j, uid, jit);
	}

-
	HASH_DELETE(hh, j->universe, unit);
-
	pkg_set(unit->pkg, PKG_UNIQUEID, new_uid);
-
	HASH_FIND(hh, j->universe, new_uid, uidlen, found);
-
	if (found != NULL)
-
		DL_APPEND(found, unit);
-
	else
-
		HASH_ADD_KEYPTR(hh, j->universe, new_uid, uidlen, unit);
-

+
	return (rc);
}

static int
@@ -1132,12 +571,12 @@ pkg_jobs_try_remote_candidate(struct pkg_jobs *j, const char *pattern,
		if (pkg_emit_query_yesno(true, sbuf_data(qmsg))) {
			/* Change the origin of the local package */
			pkg_validate(p);
-
			HASH_FIND(hh, j->universe, uid, strlen(uid), unit);
+
			unit = pkg_jobs_universe_find(j->universe, uid);
			if (unit != NULL)
-
				pkg_jobs_change_uid(j, unit, fuid, strlen(fuid), false);
+
				pkg_jobs_universe_change_uid(j->universe, unit, fuid,
+
					strlen(fuid), false);

-
			rc = pkg_jobs_process_remote_pkg(j, p, true, false, true,
-
				NULL, true);
+
			rc = pkg_jobs_process_remote_pkg(j, p, false, NULL);
			if (rc == EPKG_OK) {
				/* Avoid freeing */
				p = NULL;
@@ -1169,8 +608,10 @@ pkg_jobs_guess_upgrade_candidate(struct pkg_jobs *j, const char *pattern)
	/* First of all, try to search a package with the same name */
	pos = strchr(pattern, '/');
	if (pos != NULL && pos[1] != '\0') {
-
		if (pkg_jobs_try_remote_candidate(j, pos + 1, opattern, MATCH_EXACT) == EPKG_OK)
+
		if (pkg_jobs_try_remote_candidate(j, pos + 1, opattern, MATCH_EXACT)
+
						== EPKG_OK)
			return (EPKG_OK);
+

		pos ++;
		pattern = pos;
	}
@@ -1211,8 +652,7 @@ pkg_jobs_guess_upgrade_candidate(struct pkg_jobs *j, const char *pattern)
}

static int
-
pkg_jobs_find_remote_pkg(struct pkg_jobs *j, const char *pattern,
-
		match_t m, bool root, bool recursive, bool add_request)
+
pkg_jobs_find_upgrade(struct pkg_jobs *j, const char *pattern, match_t m)
{
	struct pkg *p = NULL;
	struct pkgdb_it *it;
@@ -1223,22 +663,14 @@ pkg_jobs_find_remote_pkg(struct pkg_jobs *j, const char *pattern,
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
			PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;

-
	if (root && (j->flags & PKG_FLAG_FORCE) == PKG_FLAG_FORCE)
-
		force = true;
-

-
	if (((j->flags & PKG_FLAG_FORCE) == PKG_FLAG_FORCE) &&
-
	    ((j->flags & PKG_FLAG_RECURSIVE) == PKG_FLAG_RECURSIVE))
-
	    force = true;
-

-
	if (j->type == PKG_JOBS_UPGRADE && (j->flags & PKG_FLAG_FORCE) == PKG_FLAG_FORCE)
+
	if (j->flags & PKG_FLAG_FORCE)
		force = true;

	if ((it = pkgdb_repo_query(j->db, pattern, m, j->reponame)) == NULL)
		rc = EPKG_FATAL;

	while (it != NULL && pkgdb_it_next(it, &p, flags) == EPKG_OK) {
-
		rc = pkg_jobs_process_remote_pkg(j, p, root, force, recursive,
-
				NULL, add_request);
+
		rc = pkg_jobs_process_remote_pkg(j, p, force, NULL);
		if (rc == EPKG_FATAL)
			break;
		else if (rc == EPKG_OK)
@@ -1249,24 +681,24 @@ pkg_jobs_find_remote_pkg(struct pkg_jobs *j, const char *pattern,

	pkgdb_it_free(it);

-
	if (root && !found && rc != EPKG_INSTALLED) {
+
	if (!found && rc != EPKG_INSTALLED) {
		/*
		 * Here we need to ensure that this package has no
		 * reverse deps installed
		 */
-
		p = pkg_jobs_get_local_pkg(j, pattern, PKG_LOAD_BASIC|PKG_LOAD_RDEPS);
+
		p = pkg_jobs_universe_get_local(j->universe, pattern, PKG_LOAD_BASIC|PKG_LOAD_RDEPS);
		if (p == NULL)
			return (EPKG_FATAL);

-
		pkg_jobs_add_universe(j, p, true, false, NULL);
+
		pkg_jobs_universe_process(j->universe, p);

		while(pkg_rdeps(p, &rdep) == EPKG_OK) {
			struct pkg *rdep_package;

-
			rdep_package = pkg_jobs_get_local_pkg(j, rdep->uid,
+
			rdep_package = pkg_jobs_universe_get_local(j->universe, rdep->uid,
					PKG_LOAD_BASIC);
			if (rdep_package != NULL) {
-
				pkg_jobs_add_universe(j, rdep_package, true, false, NULL);
+
				pkg_jobs_universe_process(j->universe, rdep_package);
				/* It is not a top level package */
				return (EPKG_END);
			}
@@ -1320,13 +752,13 @@ pkg_jobs_find_remote_pattern(struct pkg_jobs *j, struct job_pattern *jp,
			 * For upgrade patterns we must ensure that a local package is
			 * installed as well.
			 */
-
			if (pkg_jobs_check_local_pkg (j, jp) != EPKG_OK) {
+
			if (pkg_jobs_check_local_pkg(j, jp) != EPKG_OK) {
				pkg_emit_error("%s is not installed, therefore upgrade is impossible",
						jp->pattern);
				return (EPKG_FATAL);
			}
		}
-
		rc = pkg_jobs_find_remote_pkg(j, jp->pattern, jp->match, true, true, true);
+
		rc = pkg_jobs_find_upgrade(j, jp->pattern, jp->match);
		*got_local = false;
	}
	else {
@@ -1346,8 +778,8 @@ pkg_jobs_find_remote_pattern(struct pkg_jobs *j, struct job_pattern *jp,
				}
			}
			pkg->type = PKG_FILE;
-
			rc = pkg_jobs_process_remote_pkg(j, pkg, true,
-
					j->flags & PKG_FLAG_FORCE, false, &unit, true);
+
			rc = pkg_jobs_process_remote_pkg(j, pkg, j->flags & PKG_FLAG_FORCE,
+
				&unit);

			if (rc == EPKG_OK)
				unit->jp = jp;
@@ -1365,71 +797,8 @@ pkg_jobs_find_remote_pattern(struct pkg_jobs *j, struct job_pattern *jp,
	return (rc);
}

-
static struct pkg *
-
pkg_jobs_get_local_pkg(struct pkg_jobs *j, const char *uid, unsigned flag)
-
{
-
	struct pkg *pkg = NULL;
-
	struct pkgdb_it *it;
-
	struct pkg_job_universe_item *unit;
-

-
	if (flag == 0) {
-
		if (!IS_DELETE(j))
-
			flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_RDEPS|PKG_LOAD_OPTIONS|
-
				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|
-
				PKG_LOAD_CONFLICTS;
-
		else
-
			flag = PKG_LOAD_BASIC|PKG_LOAD_RDEPS|PKG_LOAD_DEPS|PKG_LOAD_ANNOTATIONS;
-
	}
-

-
	HASH_FIND(hh, j->universe, uid, strlen(uid), unit);
-
	if (unit != NULL && unit->pkg->type == PKG_INSTALLED) {
-
		pkgdb_ensure_loaded(j->db, unit->pkg, flag);
-
		return (unit->pkg);
-
	}
-

-
	if ((it = pkgdb_query(j->db, uid, MATCH_EXACT)) == NULL)
-
		return (NULL);
-

-
	if (pkgdb_it_next(it, &pkg, flag) != EPKG_OK)
-
		pkg = NULL;
-

-
	pkgdb_it_free(it);
-

-
	return (pkg);
-
}
-

-
static struct pkg *
-
pkg_jobs_get_remote_pkg(struct pkg_jobs *j, const char *uid, unsigned flag)
-
{
-
	struct pkg *pkg = NULL;
-
	struct pkgdb_it *it;
-
	struct pkg_job_universe_item *unit;
-

-
	if (flag == 0) {
-
		flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_OPTIONS|
-
				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
-
				PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
-
	}
-

-
	HASH_FIND(hh, j->universe, uid, strlen(uid), unit);
-
	if (unit != NULL && unit->pkg->type != PKG_INSTALLED) {
-
		pkgdb_ensure_loaded(j->db, unit->pkg, flag);
-
		return (unit->pkg);
-
	}
-

-
	if ((it = pkgdb_repo_query(j->db, uid, MATCH_EXACT, j->reponame)) == NULL)
-
		return (NULL);
-

-
	if (pkgdb_it_next(it, &pkg, flag) != EPKG_OK)
-
		pkg = NULL;
-

-
	pkgdb_it_free(it);
-

-
	return (pkg);
-
}
-

-
static bool
-
pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive)
+
bool
+
pkg_jobs_need_upgrade(struct pkg *rp, struct pkg *lp)
{
	int ret, ret1, ret2;
	const char *lversion, *rversion, *larch, *rarch, *reponame, *origin;
@@ -1441,6 +810,10 @@ pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive)
	struct pkg_provide *lpr = NULL, *rpr = NULL;
	const ucl_object_t *an, *obj;

+
	/* If no local package, then rp is obviously need to be added */
+
	if (lp == NULL)
+
		return true;
+

	/* Do not upgrade locked packages */
	if (pkg_is_locked(lp)) {
		pkg_emit_locked(lp);
@@ -1588,218 +961,6 @@ pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive)
	return (false);
}

-
static bool
-
pkg_jobs_newer_than_local(struct pkg_jobs *j, struct pkg *rp, bool force)
-
{
-
	char *uid, *newversion, *oldversion, *reponame, *cksum;
-
	const ucl_object_t *an, *obj;
-
	struct pkg_job_universe_item *unit;
-
	int64_t oldsize;
-
	struct pkg *lp;
-
	bool automatic;
-
	int ret;
-

-
	pkg_get(rp, PKG_UNIQUEID, &uid,
-
	    PKG_REPONAME, &reponame, PKG_CKSUM, &cksum);
-
	lp = pkg_jobs_get_local_pkg(j, uid, 0);
-

-
	/* obviously yes because local doesn't exists */
-
	if (lp == NULL)
-
		return (true);
-

-
	pkg_get(lp, PKG_AUTOMATIC, &automatic,
-
	    PKG_VERSION, &oldversion,
-
	    PKG_FLATSIZE, &oldsize,
-
	    PKG_ANNOTATIONS, &obj);
-

-
	/* Add repo name to the annotation */
-
	an = pkg_object_find(obj, "repository");
-
	if (an != NULL)  {
-
		if (strcmp(reponame, ucl_object_tostring(an)) != 0)  {
-
			pkg_jobs_add_universe(j, lp, true, false, NULL);
-
			return (false);
-
		}
-
		else {
-
			pkg_set(lp, PKG_REPONAME, reponame, PKG_CKSUM, cksum);
-
		}
-
	}
-

-
	pkg_jobs_add_universe(j, lp, true, false, &unit);
-

-
	pkg_get(rp, PKG_VERSION, &newversion);
-
	pkg_set(rp, PKG_OLD_VERSION, oldversion,
-
	    PKG_OLD_FLATSIZE, oldsize,
-
	    PKG_AUTOMATIC, automatic);
-

-
	if (force) {
-
		unit->reinstall = rp;
-
		return (true);
-
	}
-

-
	ret = pkg_need_upgrade(rp, lp, false);
-

-
	return (ret);
-
}
-

-
static int
-
pkg_conflicts_add_missing(struct pkg_jobs *j, const char *uid)
-
{
-
	struct pkg *npkg;
-

-

-
	npkg = pkg_jobs_get_local_pkg(j, uid, 0);
-
	if (npkg == NULL) {
-
		npkg = pkg_jobs_get_remote_pkg(j, uid, 0);
-
		pkg_debug(2, "conflicts: add missing remote %s(%d)", uid);
-
	}
-
	else {
-
		pkg_debug(2, "conflicts: add missing local %s(%d)", uid);
-
	}
-

-
	if (npkg == NULL) {
-
		pkg_emit_error("cannot register conflict with non-existing %s",
-
				uid);
-
		return (EPKG_FATAL);
-
	}
-

-
	return pkg_jobs_add_universe(j, npkg, true, false, NULL);
-
}
-

-

-
static void
-
pkg_conflicts_register_universe(struct pkg_jobs *j,
-
		struct pkg_job_universe_item *u1,
-
		struct pkg_job_universe_item *u2, bool local_only,
-
		enum pkg_conflict_type type)
-
{
-

-
	pkg_conflicts_register(u1->pkg, u2->pkg, type);
-
}
-

-
static void
-
pkg_conflicts_add_from_pkgdb_local(const char *o1, const char *o2, void *ud)
-
{
-
	struct pkg_jobs *j = (struct pkg_jobs *)ud;
-
	struct pkg_job_universe_item *u1, *u2, *cur1, *cur2;
-
	struct pkg_conflict *c;
-
	const char *dig1, *dig2;
-

-
	HASH_FIND_STR(j->universe, o1, u1);
-
	HASH_FIND_STR(j->universe, o2, u2);
-

-
	if (u1 == NULL && u2 == NULL) {
-
		pkg_emit_error("cannot register conflict with non-existing %s and %s",
-
				o1, o2);
-
		return;
-
	}
-
	else if (u1 == NULL) {
-
		if (pkg_conflicts_add_missing(j, o1) != EPKG_OK)
-
			return;
-
		HASH_FIND_STR(j->universe, o1, u1);
-
	}
-
	else if (u2 == NULL) {
-
		if (pkg_conflicts_add_missing(j, o2) != EPKG_OK)
-
			return;
-
		HASH_FIND_STR(j->universe, o2, u2);
-
	}
-
	else {
-
		/* Maybe we have registered this conflict already */
-
		HASH_FIND(hh, u1->pkg->conflicts, o2, strlen(o2), c);
-
		if (c != NULL)
-
			return;
-
	}
-

-
	/*
-
	 * Here we have some unit but we do not know, where is a conflict, e.g.
-
	 * if we have several units U1 and U2 with the same uniqueid O that are in
-
	 * the conflict with some origin O' provided by U1' and U2'. So we can
-
	 * register the conflicts between all units in the chain.
-
	 */
-
	LL_FOREACH(u1, cur1) {
-
		LL_FOREACH(u2, cur2) {
-
			if (cur1->pkg->type == PKG_INSTALLED && cur2->pkg->type != PKG_INSTALLED) {
-
				pkg_get(cur1->pkg, PKG_DIGEST, &dig1);
-
				pkg_get(cur2->pkg, PKG_DIGEST, &dig2);
-
				pkg_conflicts_register_universe(j, cur1, cur2, true, PKG_CONFLICT_REMOTE_LOCAL);
-
				pkg_debug(2, "register conflict between local %s(%s) <-> remote %s(%s)",
-
						o1, dig1, o2, dig2);
-
				j->conflicts_registered ++;
-
			}
-
			else if (cur2->pkg->type == PKG_INSTALLED && cur1->pkg->type != PKG_INSTALLED) {
-
				pkg_get(cur1->pkg, PKG_DIGEST, &dig1);
-
				pkg_get(cur2->pkg, PKG_DIGEST, &dig2);
-
				pkg_conflicts_register_universe(j, cur1, cur2, true, PKG_CONFLICT_REMOTE_LOCAL);
-
				pkg_debug(2, "register conflict between local %s(%s) <-> remote %s(%s)",
-
						o2, dig2, o1, dig1);
-
				j->conflicts_registered ++;
-
			}
-
		}
-
	}
-
}
-

-
static void
-
pkg_conflicts_add_from_pkgdb_remote(const char *o1, const char *o2, void *ud)
-
{
-
	struct pkg_jobs *j = (struct pkg_jobs *)ud;
-
	struct pkg_job_universe_item *u1, *u2, *cur1, *cur2;
-
	struct pkg_conflict *c;
-
	const char *dig1, *dig2;
-

-
	HASH_FIND_STR(j->universe, o1, u1);
-
	HASH_FIND_STR(j->universe, o2, u2);
-

-
	/*
-
	 * In case of remote conflict we need to register it only between remote
-
	 * packets
-
	 */
-

-
	if (u1 == NULL || u2 == NULL) {
-
		pkg_emit_error("cannot register remote conflict with non-existing %s and %s",
-
				o1, o2);
-
		return;
-
	}
-
	else {
-
		/* Maybe we have registered this conflict already */
-
		HASH_FIND(hh, u1->pkg->conflicts, o2, strlen(o2), c);
-
		if (c != NULL)
-
			return;
-
	}
-

-
	LL_FOREACH(u1, cur1) {
-
		if (cur1->pkg->type != PKG_INSTALLED) {
-
			HASH_FIND(hh, cur1->pkg->conflicts, o2, strlen(o2), c);
-
			if (c == NULL) {
-
				LL_FOREACH(u2, cur2) {
-
					HASH_FIND(hh, cur2->pkg->conflicts, o1, strlen(o1), c);
-
					if (c == NULL && cur2->pkg->type != PKG_INSTALLED) {
-
						/* No need to update priorities */
-
						pkg_conflicts_register(cur1->pkg, cur2->pkg, PKG_CONFLICT_REMOTE_REMOTE);
-
						j->conflicts_registered ++;
-
						pkg_get(cur1->pkg, PKG_DIGEST, &dig1);
-
						pkg_get(cur2->pkg, PKG_DIGEST, &dig2);
-
						pkg_debug(2, "register conflict between remote %s(%s) <-> %s(%s)",
-
								o1, dig1, o2, dig2);
-
						break;
-
					}
-
				}
-
			}
-
		}
-
	}
-
}
-

-
int
-
pkg_conflicts_append_pkg(struct pkg *p, struct pkg_jobs *j)
-
{
-
	/* Now we can get conflicts only from pkgdb */
-
	return (pkgdb_integrity_append(j->db, p, pkg_conflicts_add_from_pkgdb_remote, j));
-
}
-

-
int
-
pkg_conflicts_integrity_check(struct pkg_jobs *j)
-
{
-
	return (pkgdb_integrity_check(j->db, pkg_conflicts_add_from_pkgdb_local, j));
-
}
-

static void
pkg_jobs_propagate_automatic(struct pkg_jobs *j)
{
@@ -1808,24 +969,21 @@ pkg_jobs_propagate_automatic(struct pkg_jobs *j)
	const char *uid;
	bool automatic;

-
	HASH_ITER(hh, j->universe, unit, utmp) {
-
		/* Rewind list */
-
		while (unit->prev->next != NULL)
-
			unit = unit->prev;
+
	HASH_ITER(hh, j->universe->items, unit, utmp) {
		if (unit->next == NULL) {
			/*
			 * For packages that are alone in the installation list
			 * we search them in the corresponding request
			 */
			pkg_get(unit->pkg, PKG_UNIQUEID, &uid);
-
			HASH_FIND_STR(j->request_add, uid, req);
+
			HASH_FIND_PTR(j->request_add, &unit, req);
			if (req == NULL && unit->pkg->type != PKG_INSTALLED) {
				automatic = 1;
				pkg_debug(2, "set automatic flag for %s", uid);
				pkg_set(unit->pkg, PKG_AUTOMATIC, automatic);
			}
			else {
-
				if (unit->reinstall == NULL || j->type == PKG_JOBS_INSTALL) {
+
				if (j->type == PKG_JOBS_INSTALL) {
					automatic = 0;
					pkg_set(unit->pkg, PKG_AUTOMATIC, automatic);
				}
@@ -1842,25 +1000,13 @@ pkg_jobs_propagate_automatic(struct pkg_jobs *j)
				if (cur->pkg->type == PKG_INSTALLED) {
					local = cur;
					pkg_get(local->pkg, PKG_AUTOMATIC, &automatic);
+
					break;
				}
			}
-
			if (local != NULL) {
-
				/*
-
				 * Turn off automatic22 flags for install job if
-
				 * a package was1 explicitly requested to be installed
-
				 */
-
				if (j->type == PKG_JOBS_INSTALL) {
-
					pkg_get(local->pkg, PKG_UNIQUEID, &uid);
-
					HASH_FIND_STR(j->request_add, uid, req);
-
					if (req != NULL)
-
						automatic = 0;
-
				}
-
				LL_FOREACH(unit, cur) {
-
					if (cur->pkg->type != PKG_INSTALLED) {
+
			if (local != NULL)
+
				LL_FOREACH(unit, cur)
+
					if (cur->pkg->type != PKG_INSTALLED)
						pkg_set(cur->pkg, PKG_AUTOMATIC, automatic);
-
					}
-
				}
-
			}
		}
	}
}
@@ -1882,10 +1028,10 @@ pkg_jobs_find_deinstall_request(struct pkg_job_universe_item *item,
		return (NULL);
	}

-
	HASH_FIND_STR(j->request_delete, uid, found);
+
	HASH_FIND_PTR(j->request_delete, &item, found);
	if (found == NULL) {
		while (pkg_deps(pkg, &d) == EPKG_OK) {
-
			HASH_FIND_STR(j->universe, d->uid, dep_item);
+
			dep_item = pkg_jobs_universe_find(j->universe, d->uid);
			if (dep_item) {
				found = pkg_jobs_find_deinstall_request(dep_item, j, rec_level + 1);
				if (found)
@@ -1949,7 +1095,7 @@ jobs_solve_deinstall(struct pkg_jobs *j)
				PKG_LOAD_BASIC|PKG_LOAD_RDEPS|PKG_LOAD_DEPS|PKG_LOAD_ANNOTATIONS) == EPKG_OK) {
			// Check if the pkg is locked
			pkg_get(pkg, PKG_UNIQUEID, &uid);
-
			pkg_jobs_add_universe(j, pkg, recursive, false, &unit);
+
			pkg_jobs_universe_process_item(j->universe, pkg, &unit);
			if(pkg_is_locked(pkg)) {
				pkg_emit_locked(pkg);
			}
@@ -1985,9 +1131,9 @@ jobs_solve_autoremove(struct pkg_jobs *j)
			== EPKG_OK) {
		// Check if the pkg is locked
		pkg_get(pkg, PKG_UNIQUEID, &uid);
-
		HASH_FIND_STR(j->universe, uid, unit);
+
		unit = pkg_jobs_universe_find(j->universe, uid);
		if (unit == NULL) {
-
			pkg_jobs_add_universe(j, pkg, false, false, &unit);
+
			pkg_jobs_universe_process_item(j->universe, pkg, &unit);
			if(pkg_is_locked(pkg)) {
				pkg_emit_locked(pkg);
			}
@@ -2138,11 +1284,10 @@ jobs_solve_install_upgrade(struct pkg_jobs *j)

				while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
					/* TODO: use repository priority here */
-
					pkg_jobs_add_universe(j, pkg, true, false, NULL);
+
					pkg_jobs_universe_process(j->universe, pkg);
					pkg_get(pkg, PKG_UNIQUEID, &uid, PKG_AUTOMATIC, &automatic);
					/* Do not test we ignore what doesn't exists remotely */
-
					pkg_jobs_find_remote_pkg(j, uid, MATCH_EXACT, !automatic,
-
							true, !automatic);
+
					pkg_jobs_find_upgrade(j, uid, MATCH_EXACT);
					pkg = NULL;
				}
				pkgdb_it_free(it);
@@ -2166,7 +1311,7 @@ jobs_solve_install_upgrade(struct pkg_jobs *j)
				 * Need to iterate request one more time to recurse depends
				 */
				HASH_ITER(hh, j->request_add, req, rtmp) {
-
					pkg_jobs_add_universe(j, req->item->pkg, true, true, NULL);
+
					pkg_jobs_universe_process(j->universe, req->item->pkg);
				}
			}
		}
@@ -2177,7 +1322,7 @@ jobs_solve_install_upgrade(struct pkg_jobs *j)
		 * request packages to the universe to find out any potential conflicts
		 */
		HASH_ITER(hh, j->request_add, req, rtmp) {
-
			pkg_jobs_add_universe(j, req->item->pkg, true, false, NULL);
+
			pkg_jobs_universe_process(j->universe, req->item->pkg);
		}
	}

@@ -2218,8 +1363,7 @@ jobs_solve_fetch(struct pkg_jobs *j)
			else {
				pkg_get(pkg, PKG_UNIQUEID, &uid);
				/* Do not test we ignore what doesn't exists remotely */
-
				pkg_jobs_find_remote_pkg(j, uid, MATCH_EXACT, false,
-
						j->flags & PKG_FLAG_RECURSIVE, true);
+
				pkg_jobs_find_upgrade(j, uid, MATCH_EXACT);
			}
			pkg = NULL;
		}
@@ -2227,8 +1371,7 @@ jobs_solve_fetch(struct pkg_jobs *j)
	} else {
		HASH_ITER(hh, j->patterns, jp, jtmp) {
			/* TODO: use repository priority here */
-
			if (pkg_jobs_find_remote_pkg(j, jp->pattern, jp->match, true,
-
					j->flags & PKG_FLAG_RECURSIVE, true) == EPKG_FATAL)
+
			if (pkg_jobs_find_upgrade(j, jp->pattern, jp->match) == EPKG_FATAL)
				pkg_emit_error("No packages matching '%s' have been found in the "
						"repositories", jp->pattern);
		}
@@ -2258,7 +1401,7 @@ pkg_jobs_apply_replacements(struct pkg_jobs *j)
		return;
	}

-
	LL_FOREACH(j->uid_replaces, r) {
+
	LL_FOREACH(j->universe->uid_replaces, r) {
		pkg_debug(4, "changing uid %s -> %s", r->old_uid, r->new_uid);
		sqlite3_bind_text(stmt, 1, r->new_uid, -1, SQLITE_TRANSIENT);
		sqlite3_bind_text(stmt, 2, r->old_uid, -1, SQLITE_TRANSIENT);
@@ -2366,14 +1509,10 @@ pkg_jobs_solve(struct pkg_jobs *j)
	DL_FOREACH(j->jobs, job) {
		struct pkg *p;

-
		if (job->items[0]->reinstall) {
-
			p = job->items[0]->reinstall;
-
		}
-
		else {
-
			p = job->items[0]->pkg;
-
			if (p->type != PKG_REMOTE)
-
				continue;
-
		}
+
		p = job->items[0]->pkg;
+
		if (p->type != PKG_REMOTE)
+
			continue;
+

		if (pkgdb_ensure_loaded(j->db, p, PKG_LOAD_FILES|PKG_LOAD_DIRS)
				== EPKG_FATAL) {
			j->need_fetch = true;
@@ -2440,9 +1579,7 @@ pkg_jobs_handle_install(struct pkg_solved *ps, struct pkg_jobs *j, bool handle_r
	int retcode = EPKG_FATAL;

	old = ps->items[1] ? ps->items[1]->pkg : NULL;
-
	new = (ps->items[0]->reinstall == NULL) ?
-
					ps->items[0]->pkg : /* Just a remote package */
-
					ps->items[0]->reinstall /* Package for reinstall */;
+
	new = ps->items[0]->pkg;

	pkg_get(new, PKG_UNIQUEID, &pkguid, PKG_AUTOMATIC, &automatic);
	if (old != NULL) {
@@ -2716,14 +1853,9 @@ pkg_jobs_fetch(struct pkg_jobs *j)
		if (ps->type != PKG_SOLVED_DELETE && ps->type != PKG_SOLVED_UPGRADE_REMOVE) {
			int64_t pkgsize;

-
			if (ps->items[0]->reinstall) {
-
				p = ps->items[0]->reinstall;
-
			}
-
			else {
-
				p = ps->items[0]->pkg;
-
				if (p->type != PKG_REMOTE)
-
					continue;
-
			}
+
			p = ps->items[0]->pkg;
+
			if (p->type != PKG_REMOTE)
+
				continue;

			pkg_get(p, PKG_PKGSIZE, &pkgsize, PKG_REPOPATH, &repopath);
			if (mirror) {
@@ -2771,14 +1903,9 @@ pkg_jobs_fetch(struct pkg_jobs *j)
	DL_FOREACH(j->jobs, ps) {
		if (ps->type != PKG_SOLVED_DELETE
						&& ps->type != PKG_SOLVED_UPGRADE_REMOVE) {
-
			if (ps->items[0]->reinstall) {
-
				p = ps->items[0]->reinstall;
-
			}
-
			else {
-
				p = ps->items[0]->pkg;
-
				if (p->type != PKG_REMOTE)
-
					continue;
-
			}
+
			p = ps->items[0]->pkg;
+
			if (p->type != PKG_REMOTE)
+
				continue;

			if (mirror) {
				if (pkg_repo_mirror_package(p, cachedir) != EPKG_OK)
added libpkg/pkg_jobs_conflicts.c
@@ -0,0 +1,339 @@
+
/*-
+
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
+
 * All rights reserved.
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 *
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#include <sys/param.h>
+
#include <sys/types.h>
+
#include <stdbool.h>
+
#include <stdlib.h>
+
#include <string.h>
+

+
#include "pkg.h"
+
#include "private/event.h"
+
#include "private/pkg.h"
+
#include "private/pkgdb.h"
+
#include "private/pkg_jobs.h"
+

+
struct pkg_conflict_chain {
+
	struct pkg_job_request *req;
+
	struct pkg_conflict_chain *next;
+
};
+

+
static int
+
pkg_conflicts_chain_cmp_cb(struct pkg_conflict_chain *a, struct pkg_conflict_chain *b)
+
{
+
	const char *vera, *verb;
+

+
	if (a->req->skip || b->req->skip) {
+
		return (a->req->skip - b->req->skip);
+
	}
+

+
	pkg_get(a->req->item->pkg, PKG_VERSION, &vera);
+
	pkg_get(b->req->item->pkg, PKG_VERSION, &verb);
+

+
	/* Inverse sort to get the maximum version as the first element */
+
	return (pkg_version_cmp(vera, verb));
+
}
+

+
static int
+
pkg_conflicts_request_resolve_chain(struct pkg *req, struct pkg_conflict_chain *chain)
+
{
+
	struct pkg_conflict_chain *elt, *selected = NULL;
+
	const char *name, *origin, *slash_pos;
+

+
	pkg_get(req, PKG_NAME, &name);
+
	/*
+
	 * First of all prefer pure origins, where the last element of
+
	 * an origin is pkg name
+
	 */
+
	LL_FOREACH(chain, elt) {
+
		pkg_get(elt->req->item->pkg, PKG_ORIGIN, &origin);
+
		slash_pos = strrchr(origin, '/');
+
		if (slash_pos != NULL) {
+
			if (strcmp(slash_pos + 1, name) == 0) {
+
				selected = elt;
+
				break;
+
			}
+
		}
+
	}
+

+
	if (selected == NULL) {
+
		/* XXX: add manual selection here */
+
		/* Sort list by version of package */
+
		LL_SORT(chain, pkg_conflicts_chain_cmp_cb);
+
		selected = chain;
+
	}
+

+
	pkg_get(selected->req->item->pkg, PKG_ORIGIN, &origin);
+
	pkg_debug(2, "select %s in the chain of conflicts for %s", origin, name);
+
	/* Disable conflicts from a request */
+
	LL_FOREACH(chain, elt) {
+
		if (elt != selected)
+
			elt->req->skip = true;
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
static void
+
pkg_conflicts_request_add_chain(struct pkg_conflict_chain **chain, struct pkg_job_request *req)
+
{
+
	struct pkg_conflict_chain *elt;
+

+
	elt = calloc(1, sizeof(struct pkg_conflict_chain));
+
	if (elt == NULL) {
+
		pkg_emit_errno("resolve_request_conflicts", "calloc: struct pkg_conflict_chain");
+
	}
+
	elt->req = req;
+
	LL_PREPEND(*chain, elt);
+
}
+

+
int
+
pkg_conflicts_request_resolve(struct pkg_jobs *j)
+
{
+
	struct pkg_job_request *req, *rtmp, *found;
+
	struct pkg_conflict *c, *ctmp;
+
	struct pkg_conflict_chain *chain;
+
	struct pkg_job_universe_item *unit;
+
	const char *origin;
+

+
	HASH_ITER(hh, j->request_add, req, rtmp) {
+
		chain = NULL;
+
		if (req->skip)
+
			continue;
+

+
		HASH_ITER(hh, req->item->pkg->conflicts, c, ctmp) {
+
			unit = pkg_jobs_universe_find(j->universe, pkg_conflict_uniqueid(c));
+
			if (unit != NULL) {
+
				HASH_FIND_PTR(j->request_add, unit, found);
+
				if (found && !found->skip) {
+
					pkg_conflicts_request_add_chain(&chain, found);
+
				}
+
			}
+
		}
+
		if (chain != NULL) {
+
			pkg_get(req->item->pkg, PKG_ORIGIN, &origin);
+
			/* Add package itself */
+
			pkg_conflicts_request_add_chain(&chain, req);
+

+
			if (pkg_conflicts_request_resolve_chain(req->item->pkg, chain) != EPKG_OK) {
+
				LL_FREE(chain, free);
+
				return (EPKG_FATAL);
+
			}
+
			LL_FREE(chain, free);
+
		}
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
void
+
pkg_conflicts_register(struct pkg *p1, struct pkg *p2, enum pkg_conflict_type type)
+
{
+
	struct pkg_conflict *c1, *c2, *test;
+
	const char *u1, *u2;
+

+
	pkg_get(p1, PKG_UNIQUEID, &u1);
+
	pkg_get(p2, PKG_UNIQUEID, &u2);
+

+
	pkg_conflict_new(&c1);
+
	pkg_conflict_new(&c2);
+
	if (c1 != NULL && c2 != NULL) {
+
		c1->type = c2->type = type;
+
		HASH_FIND_STR(p1->conflicts, u2, test);
+
		if (test == NULL) {
+
			sbuf_set(&c1->uniqueid, u2);
+
			HASH_ADD_KEYPTR(hh, p1->conflicts, pkg_conflict_uniqueid(c1), sbuf_size(c1->uniqueid), c1);
+
			pkg_debug(2, "registering conflict between %s and %s", u1, u2);
+
		}
+

+
		HASH_FIND_STR(p2->conflicts, u1, test);
+
		if (test == NULL) {
+
			sbuf_set(&c2->uniqueid, u1);
+
			HASH_ADD_KEYPTR(hh, p2->conflicts, pkg_conflict_uniqueid(c2), sbuf_size(c2->uniqueid), c2);
+
			pkg_debug(2, "registering conflict between %s and %s", u2, u1);
+
		}
+
	}
+
}
+

+

+
static int
+
pkg_conflicts_add_missing(struct pkg_jobs *j, const char *uid)
+
{
+
	struct pkg *npkg;
+

+

+
	npkg = pkg_jobs_universe_get_local(j->universe, uid, 0);
+
	if (npkg == NULL) {
+
		npkg = pkg_jobs_universe_get_remote(j->universe, uid, 0);
+
		pkg_debug(2, "conflicts: add missing remote %s(%d)", uid);
+
	}
+
	else {
+
		pkg_debug(2, "conflicts: add missing local %s(%d)", uid);
+
	}
+

+
	if (npkg == NULL) {
+
		pkg_emit_error("cannot register conflict with non-existing %s",
+
				uid);
+
		return (EPKG_FATAL);
+
	}
+

+
	return pkg_jobs_universe_process(j->universe, npkg);
+
}
+

+

+
static void
+
pkg_conflicts_register_universe(struct pkg_jobs *j,
+
		struct pkg_job_universe_item *u1,
+
		struct pkg_job_universe_item *u2, bool local_only,
+
		enum pkg_conflict_type type)
+
{
+

+
	pkg_conflicts_register(u1->pkg, u2->pkg, type);
+
}
+

+
static void
+
pkg_conflicts_add_from_pkgdb_local(const char *o1, const char *o2, void *ud)
+
{
+
	struct pkg_jobs *j = (struct pkg_jobs *)ud;
+
	struct pkg_job_universe_item *u1, *u2, *cur1, *cur2;
+
	struct pkg_conflict *c;
+
	const char *dig1, *dig2;
+

+
	u1 = pkg_jobs_universe_find(j->universe, o1);
+
	u2 = pkg_jobs_universe_find(j->universe, o2);
+

+
	if (u1 == NULL && u2 == NULL) {
+
		pkg_emit_error("cannot register conflict with non-existing %s and %s",
+
				o1, o2);
+
		return;
+
	}
+
	else if (u1 == NULL) {
+
		if (pkg_conflicts_add_missing(j, o1) != EPKG_OK)
+
			return;
+
		u1 = pkg_jobs_universe_find(j->universe, o1);
+
	}
+
	else if (u2 == NULL) {
+
		if (pkg_conflicts_add_missing(j, o2) != EPKG_OK)
+
			return;
+
		u2 = pkg_jobs_universe_find(j->universe, o2);
+
	}
+
	else {
+
		/* Maybe we have registered this conflict already */
+
		HASH_FIND(hh, u1->pkg->conflicts, o2, strlen(o2), c);
+
		if (c != NULL)
+
			return;
+
	}
+

+
	/*
+
	 * Here we have some unit but we do not know, where is a conflict, e.g.
+
	 * if we have several units U1 and U2 with the same uniqueid O that are in
+
	 * the conflict with some origin O' provided by U1' and U2'. So we can
+
	 * register the conflicts between all units in the chain.
+
	 */
+
	LL_FOREACH(u1, cur1) {
+
		LL_FOREACH(u2, cur2) {
+
			if (cur1->pkg->type == PKG_INSTALLED && cur2->pkg->type != PKG_INSTALLED) {
+
				pkg_get(cur1->pkg, PKG_DIGEST, &dig1);
+
				pkg_get(cur2->pkg, PKG_DIGEST, &dig2);
+
				pkg_conflicts_register_universe(j, cur1, cur2, true, PKG_CONFLICT_REMOTE_LOCAL);
+
				pkg_debug(2, "register conflict between local %s(%s) <-> remote %s(%s)",
+
						o1, dig1, o2, dig2);
+
				j->conflicts_registered ++;
+
			}
+
			else if (cur2->pkg->type == PKG_INSTALLED && cur1->pkg->type != PKG_INSTALLED) {
+
				pkg_get(cur1->pkg, PKG_DIGEST, &dig1);
+
				pkg_get(cur2->pkg, PKG_DIGEST, &dig2);
+
				pkg_conflicts_register_universe(j, cur1, cur2, true, PKG_CONFLICT_REMOTE_LOCAL);
+
				pkg_debug(2, "register conflict between local %s(%s) <-> remote %s(%s)",
+
						o2, dig2, o1, dig1);
+
				j->conflicts_registered ++;
+
			}
+
		}
+
	}
+
}
+

+
static void
+
pkg_conflicts_add_from_pkgdb_remote(const char *o1, const char *o2, void *ud)
+
{
+
	struct pkg_jobs *j = (struct pkg_jobs *)ud;
+
	struct pkg_job_universe_item *u1, *u2, *cur1, *cur2;
+
	struct pkg_conflict *c;
+
	const char *dig1, *dig2;
+

+
	u1 = pkg_jobs_universe_find(j->universe, o1);
+
	u2 = pkg_jobs_universe_find(j->universe, o2);
+

+
	/*
+
	 * In case of remote conflict we need to register it only between remote
+
	 * packets
+
	 */
+

+
	if (u1 == NULL || u2 == NULL) {
+
		pkg_emit_error("cannot register remote conflict with non-existing %s and %s",
+
				o1, o2);
+
		return;
+
	}
+
	else {
+
		/* Maybe we have registered this conflict already */
+
		HASH_FIND(hh, u1->pkg->conflicts, o2, strlen(o2), c);
+
		if (c != NULL)
+
			return;
+
	}
+

+
	LL_FOREACH(u1, cur1) {
+
		if (cur1->pkg->type != PKG_INSTALLED) {
+
			HASH_FIND(hh, cur1->pkg->conflicts, o2, strlen(o2), c);
+
			if (c == NULL) {
+
				LL_FOREACH(u2, cur2) {
+
					HASH_FIND(hh, cur2->pkg->conflicts, o1, strlen(o1), c);
+
					if (c == NULL && cur2->pkg->type != PKG_INSTALLED) {
+
						/* No need to update priorities */
+
						pkg_conflicts_register(cur1->pkg, cur2->pkg, PKG_CONFLICT_REMOTE_REMOTE);
+
						j->conflicts_registered ++;
+
						pkg_get(cur1->pkg, PKG_DIGEST, &dig1);
+
						pkg_get(cur2->pkg, PKG_DIGEST, &dig2);
+
						pkg_debug(2, "register conflict between remote %s(%s) <-> %s(%s)",
+
								o1, dig1, o2, dig2);
+
						break;
+
					}
+
				}
+
			}
+
		}
+
	}
+
}
+

+
int
+
pkg_conflicts_append_pkg(struct pkg *p, struct pkg_jobs *j)
+
{
+
	/* Now we can get conflicts only from pkgdb */
+
	return (pkgdb_integrity_append(j->db, p, pkg_conflicts_add_from_pkgdb_remote, j));
+
}
+

+
int
+
pkg_conflicts_integrity_check(struct pkg_jobs *j)
+
{
+
	return (pkgdb_integrity_check(j->db, pkg_conflicts_add_from_pkgdb_local, j));
+
}
added libpkg/pkg_jobs_universe.c
@@ -0,0 +1,770 @@
+
/* Copyright (c) 2014, Vsevolod Stakhov
+
 * All rights reserved.
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions are met:
+
 *       * Redistributions of source code must retain the above copyright
+
 *         notice, this list of conditions and the following disclaimer.
+
 *       * Redistributions in binary form must reproduce the above copyright
+
 *         notice, this list of conditions and the following disclaimer in the
+
 *         documentation and/or other materials provided with the distribution.
+
 *
+
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#include <sys/param.h>
+
#include <sys/types.h>
+

+
#include <assert.h>
+
#include <errno.h>
+
#include <libutil.h>
+
#include <stdbool.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <ctype.h>
+

+
#include "pkg.h"
+
#include "private/event.h"
+
#include "private/pkg.h"
+
#include "private/pkgdb.h"
+
#include "private/pkg_jobs.h"
+

+
#define IS_DELETE(j) ((j)->type == PKG_JOBS_DEINSTALL || (j)->type == PKG_JOBS_AUTOREMOVE)
+

+
struct pkg *
+
pkg_jobs_universe_get_local(struct pkg_jobs_universe *universe,
+
	const char *uid, unsigned flag)
+
{
+
	struct pkg *pkg = NULL;
+
	struct pkgdb_it *it;
+
	struct pkg_job_universe_item *unit;
+

+
	if (flag == 0) {
+
		if (!IS_DELETE(universe->j))
+
			flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_RDEPS|PKG_LOAD_OPTIONS|
+
				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|
+
				PKG_LOAD_CONFLICTS;
+
		else
+
			flag = PKG_LOAD_BASIC|PKG_LOAD_RDEPS|PKG_LOAD_DEPS|PKG_LOAD_ANNOTATIONS;
+
	}
+

+
	HASH_FIND(hh, universe->items, uid, strlen(uid), unit);
+
	if (unit != NULL && unit->pkg->type == PKG_INSTALLED) {
+
		pkgdb_ensure_loaded(universe->j->db, unit->pkg, flag);
+
		return (unit->pkg);
+
	}
+

+
	if ((it = pkgdb_query(universe->j->db, uid, MATCH_EXACT)) == NULL)
+
		return (NULL);
+

+
	if (pkgdb_it_next(it, &pkg, flag) != EPKG_OK)
+
		pkg = NULL;
+

+
	pkgdb_it_free(it);
+

+
	return (pkg);
+
}
+

+
struct pkg *
+
pkg_jobs_universe_get_remote(struct pkg_jobs_universe *universe,
+
	const char *uid, unsigned flag)
+
{
+
	struct pkg *pkg = NULL;
+
	struct pkgdb_it *it;
+
	struct pkg_job_universe_item *unit;
+

+
	if (flag == 0) {
+
		flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_OPTIONS|
+
				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
+
				PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
+
	}
+

+
	HASH_FIND(hh, universe->items, uid, strlen(uid), unit);
+
	if (unit != NULL && unit->pkg->type != PKG_INSTALLED) {
+
		pkgdb_ensure_loaded(universe->j->db, unit->pkg, flag);
+
		return (unit->pkg);
+
	}
+

+
	if ((it = pkgdb_repo_query(universe->j->db, uid, MATCH_EXACT,
+
		universe->j->reponame)) == NULL)
+
		return (NULL);
+

+
	if (pkgdb_it_next(it, &pkg, flag) != EPKG_OK)
+
		pkg = NULL;
+

+
	pkgdb_it_free(it);
+

+
	return (pkg);
+
}
+

+
/**
+
 * Check whether a package is in the universe already or add it
+
 * @return item or NULL
+
 */
+
int
+
pkg_jobs_universe_add_pkg(struct pkg_jobs_universe *universe, struct pkg *pkg,
+
		bool force, struct pkg_job_universe_item **found)
+
{
+
	struct pkg_job_universe_item *item, *tmp = NULL;
+
	const char *uid, *digest, *version, *name;
+
	struct pkg_job_seen *seen;
+

+
	pkg_get(pkg, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest,
+
			PKG_VERSION, &version, PKG_NAME, &name);
+
	if (digest == NULL) {
+
		pkg_debug(3, "no digest found for package %s (%s-%s)", uid,
+
				name, version);
+
		if (pkg_checksum_calculate(pkg, universe->j->db) != EPKG_OK) {
+
			*found = NULL;
+
			return (EPKG_FATAL);
+
		}
+
		pkg_get(pkg, PKG_DIGEST, &digest);
+
	}
+

+
	HASH_FIND_STR(universe->seen, digest, seen);
+
	if (seen != NULL && !force) {
+
		if (found != NULL)
+
			*found = seen->un;
+

+
		return (EPKG_END);
+
	}
+

+
	pkg_debug(2, "universe: add new %s pkg: %s, (%s-%s:%s)",
+
				(pkg->type == PKG_INSTALLED ? "local" : "remote"), uid,
+
				name, version, digest);
+

+
	item = calloc(1, sizeof (struct pkg_job_universe_item));
+
	if (item == NULL) {
+
		pkg_emit_errno("pkg_jobs_pkg_insert_universe", "calloc: struct pkg_job_universe_item");
+
		return (EPKG_FATAL);
+
	}
+

+
	item->pkg = pkg;
+

+
	HASH_FIND_STR(universe->items, uid, tmp);
+
	if (tmp == NULL)
+
		HASH_ADD_KEYPTR(hh, universe->items, uid, strlen(uid), item);
+

+
	DL_APPEND(tmp, item);
+

+
	if (seen == NULL) {
+
		seen = calloc(1, sizeof(struct pkg_job_seen));
+
		if (seen == NULL) {
+
			pkg_emit_errno("pkg_jobs_universe_add_pkg", "calloc: struct pkg_job_seen)");
+
			return (EPKG_FATAL);
+
		}
+
		seen->digest = digest;
+
		seen->un = item;
+
		HASH_ADD_KEYPTR(hh, universe->seen, seen->digest, strlen(seen->digest),
+
			seen);
+
	}
+

+
	universe->nitems++;
+

+
	if (found != NULL)
+
		*found = item;
+

+
	return (EPKG_OK);
+
}
+

+
#define DEPS_FLAG_REVERSE 0x1 << 1
+
#define DEPS_FLAG_MIRROR 0x1 << 2
+
#define DEPS_FLAG_FORCE_LOCAL 0x1 << 3
+
#define DEPS_FLAG_FORCE_MISSING 0x1 << 4
+
#define DEPS_FLAG_FORCE_UPGRADE 0x1 << 5
+

+
static int
+
pkg_jobs_universe_process_deps(struct pkg_jobs_universe *universe,
+
	struct pkg *pkg, unsigned flags)
+
{
+
	struct pkg_dep *d = NULL;
+
	int (*deps_func)(const struct pkg *pkg, struct pkg_dep **d);
+
	struct pkg_job_universe_item *unit;
+
	struct pkg *npkg, *rpkg;
+

+
	if (flags & DEPS_FLAG_REVERSE)
+
		deps_func = pkg_rdeps;
+
	else
+
		deps_func = pkg_deps;
+

+
	while (deps_func(pkg, &d) == EPKG_OK) {
+
		HASH_FIND_STR(universe->items, d->uid, unit);
+
		if (unit != NULL)
+
			continue;
+

+
		rpkg = NULL;
+
		npkg = NULL;
+
		if (!(flags & DEPS_FLAG_MIRROR))
+
			npkg = pkg_jobs_universe_get_local(universe, d->uid, 0);
+

+
		if (!(flags & DEPS_FLAG_FORCE_LOCAL)) {
+

+
			/* Check for remote dependencies */
+
			rpkg = pkg_jobs_universe_get_remote(universe, d->uid, 0);
+
			if (rpkg != NULL && !(flags & DEPS_FLAG_FORCE_UPGRADE)) {
+
				if (!pkg_jobs_need_upgrade(rpkg, npkg)) {
+
					/*
+
					 * We can do it safely here, as rpkg is definitely NOT in
+
					 * the universe
+
					 */
+
					pkg_free(rpkg);
+
					rpkg = NULL;
+
				}
+
			}
+
		}
+

+
		if (npkg == NULL && rpkg == NULL) {
+
			const char *name;
+

+
			pkg_get(pkg, PKG_NAME, &name);
+
			pkg_emit_error("%s has a missing dependency: %s",
+
				name, pkg_dep_get(d, PKG_DEP_NAME));
+

+
			if (flags & DEPS_FLAG_FORCE_MISSING)
+
				continue;
+

+
			return (EPKG_FATAL);
+
		}
+

+
		if (npkg != NULL)
+
			if (pkg_jobs_universe_process_item(universe, npkg, &unit) != EPKG_OK)
+
				continue;
+

+
		/* Explicitly request for a dependency for mirroring */
+
		if (flags & DEPS_FLAG_MIRROR)
+
			pkg_jobs_add_req(universe->j, d->uid, unit);
+

+
		if (rpkg != NULL) {
+
			if (npkg != NULL) {
+
				/* Save automatic flag */
+
				bool automatic;
+

+
				pkg_get(npkg, PKG_AUTOMATIC, &automatic);
+
				pkg_set(rpkg, PKG_AUTOMATIC, automatic);
+
			}
+

+
			pkg_jobs_universe_process_item(universe, rpkg, NULL);
+
		}
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkg_jobs_universe_process_conflicts(struct pkg_jobs_universe *universe,
+
	struct pkg *pkg)
+
{
+
	struct pkg_conflict *c = NULL;
+
	struct pkg_job_universe_item *unit;
+
	struct pkg *npkg;
+

+
	while (pkg_conflicts(pkg, &c) == EPKG_OK) {
+
		HASH_FIND_STR(universe->items, pkg_conflict_uniqueid(c), unit);
+
		if (unit != NULL)
+
			continue;
+

+
		/* Check local and remote conflicts */
+
		if (pkg->type == PKG_INSTALLED) {
+
			/* Installed packages can conflict with remote ones */
+
			npkg = pkg_jobs_universe_get_remote(universe, pkg_conflict_uniqueid(c), 0);
+
			if (npkg == NULL)
+
				continue;
+

+
			pkg_jobs_universe_process_item(universe, npkg, NULL);
+
		}
+
		else {
+
			/* Remote packages can conflict with remote and local */
+
			npkg = pkg_jobs_universe_get_local(universe, pkg_conflict_uniqueid(c), 0);
+
			if (npkg != NULL) {
+
				if (pkg_jobs_universe_process_item(universe, npkg, NULL) != EPKG_OK)
+
					continue;
+

+
				if (c->type != PKG_CONFLICT_REMOTE_LOCAL) {
+
					npkg = pkg_jobs_universe_get_remote(universe,
+
						pkg_conflict_uniqueid(c), 0);
+
					if (npkg == NULL)
+
						continue;
+

+
					pkg_jobs_universe_process_item(universe, npkg, NULL);
+
				}
+
			}
+
		}
+
	}
+

+
	return (EPKG_OK);
+
}
+

+

+
static int
+
pkg_jobs_universe_process_shlibs(struct pkg_jobs_universe *universe,
+
	struct pkg *pkg)
+
{
+
	struct pkg_shlib *shlib = NULL;
+
	struct pkg_job_universe_item *unit;
+
	struct pkg_job_provide *pr, *prhead;
+
	struct pkgdb_it *it;
+
	struct pkg *npkg, *rpkg;
+
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
+
				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
+
				PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
+

+
	while (pkg_shlibs_required(pkg, &shlib) == EPKG_OK) {
+
		HASH_FIND_STR(universe->provides, pkg_shlib_name(shlib), pr);
+
		if (pr != NULL)
+
			continue;
+

+
		/* Not found, search in the repos */
+
		it = pkgdb_repo_shlib_provide(universe->j->db,
+
			pkg_shlib_name(shlib), universe->j->reponame);
+
		if (it != NULL) {
+
			rpkg = NULL;
+
			prhead = NULL;
+
			while (pkgdb_it_next(it, &rpkg, flags) == EPKG_OK) {
+
				const char *digest, *uid;
+

+
				pkg_get(rpkg, PKG_DIGEST, &digest, PKG_UNIQUEID, &uid);
+
				/* Check for local packages */
+
				HASH_FIND_STR(universe->items, uid, unit);
+
				if (unit != NULL) {
+
					if (pkg_jobs_need_upgrade (rpkg, unit->pkg)) {
+
						/* Remote provide is newer, so we can add it */
+
						if (pkg_jobs_universe_process_item(universe, rpkg,
+
							&unit) != EPKG_OK)
+
							continue;
+

+
						rpkg = NULL;
+
					}
+
				}
+
				else {
+
					/* Maybe local package has just been not added */
+
					npkg = pkg_jobs_universe_get_local(universe, uid, 0);
+
					if (npkg != NULL) {
+
						if (pkg_jobs_universe_process_item(universe, npkg,
+
							&unit) != EPKG_OK)
+
							return (EPKG_FATAL);
+
						if (pkg_jobs_need_upgrade (rpkg, npkg)) {
+
							/* Remote provide is newer, so we can add it */
+
							if (pkg_jobs_universe_process_item(universe, rpkg,
+
								&unit) != EPKG_OK)
+
								continue;
+
						}
+
					}
+
				}
+

+
				/* Skip seen packages */
+
				if (unit == NULL) {
+
					struct pkg_job_seen *seen;
+

+
					if (digest == NULL) {
+
						pkg_debug(3, "no digest found for package %s", uid);
+
						if (pkg_checksum_calculate(pkg, universe->j->db) != EPKG_OK) {
+
							return (EPKG_FATAL);
+
						}
+
						pkg_get(pkg, PKG_DIGEST, &digest);
+
					}
+
					HASH_FIND_STR(universe->seen, digest, seen);
+
					if (seen == NULL) {
+
						pkg_jobs_universe_process_item(universe, rpkg,
+
							&unit);
+

+
						/* Reset package to avoid freeing */
+
						rpkg = NULL;
+
					}
+
					else {
+
						unit = seen->un;
+
					}
+
				}
+

+
				pr = calloc (1, sizeof (*pr));
+
				if (pr == NULL) {
+
					pkg_emit_errno("pkg_jobs_add_universe", "calloc: "
+
						"struct pkg_job_provide");
+
					return (EPKG_FATAL);
+
				}
+

+
				pr->un = unit;
+
				pr->provide = pkg_shlib_name(shlib);
+

+
				if (prhead == NULL) {
+
					DL_APPEND(prhead, pr);
+
					HASH_ADD_KEYPTR(hh, universe->provides, pr->provide,
+
						strlen(pr->provide), prhead);
+
				}
+
				else {
+
					DL_APPEND(prhead, pr);
+
				}
+
			}
+
			pkgdb_it_free(it);
+
			if (prhead == NULL) {
+
				const char *name;
+

+
				pkg_get(pkg, PKG_NAME, &name);
+
				pkg_debug(1, "cannot find packages that provide %s required for %s",
+
					pkg_shlib_name(shlib), name);
+
				/*
+
				 * XXX: this is not normal but it is very common for the existing
+
				 * repos, hence we just ignore this stale dependency
+
				 */
+
			}
+
		}
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
int
+
pkg_jobs_universe_process_item(struct pkg_jobs_universe *universe, struct pkg *pkg,
+
		struct pkg_job_universe_item **result)
+
{
+
	unsigned flags = 0, job_flags;
+
	int rc = EPKG_OK;
+
	pkg_jobs_t type = universe->j->type;
+

+
	job_flags = universe->j->flags;
+

+
	/* Add pkg itself */
+
	rc = pkg_jobs_universe_add_pkg(universe, pkg, job_flags & PKG_FLAG_FORCE,
+
		result);
+
	if (rc == EPKG_END)
+
		return (EPKG_OK);
+
	else if (rc != EPKG_OK)
+
		return (rc);
+

+
	/* Convert jobs flags to dependency logical flags */
+
	if (job_flags & PKG_FLAG_FORCE_MISSING)
+
		flags |= DEPS_FLAG_FORCE_MISSING;
+
	if (job_flags & PKG_FLAG_FORCE)
+
		flags |= DEPS_FLAG_FORCE_UPGRADE;
+

+
	switch(type) {
+
	case PKG_JOBS_FETCH:
+
		if (job_flags & PKG_FLAG_RECURSIVE) {
+
			flags |= DEPS_FLAG_MIRROR;
+
			/* For fetch jobs we worry about depends only */
+
			rc = pkg_jobs_universe_process_deps(universe, pkg, flags);
+
		}
+
		break;
+
	case PKG_JOBS_INSTALL:
+
	case PKG_JOBS_UPGRADE:
+
		/* Handle depends */
+
		rc = pkg_jobs_universe_process_deps(universe, pkg, flags);
+
		if (rc != EPKG_OK)
+
			return (rc);
+
		/* Handle reverse depends */
+
		rc = pkg_jobs_universe_process_deps(universe, pkg,
+
			flags|DEPS_FLAG_REVERSE);
+
		if (rc != EPKG_OK)
+
				return (rc);
+
		/* Handle conflicts */
+
		rc = pkg_jobs_universe_process_conflicts(universe, pkg);
+
		if (rc != EPKG_OK)
+
			return (rc);
+
		/* For remote packages we should also handle shlib deps */
+
		if (pkg->type != PKG_INSTALLED) {
+
			rc = pkg_jobs_universe_process_shlibs(universe, pkg);
+
			if (rc != EPKG_OK)
+
				return (rc);
+
		}
+
		break;
+
	case PKG_JOBS_DEINSTALL:
+
	case PKG_JOBS_AUTOREMOVE:
+
		/* For delete jobs we worry only about local reverse deps */
+
		flags |= DEPS_FLAG_REVERSE|DEPS_FLAG_FORCE_LOCAL;
+
		if (!(job_flags & PKG_FLAG_FORCE))
+
			rc = pkg_jobs_universe_process_deps(universe, pkg, flags);
+
		break;
+
	}
+

+
	return (rc);
+
}
+

+
int
+
pkg_jobs_universe_process(struct pkg_jobs_universe *universe,
+
	struct pkg *pkg)
+
{
+
	return (pkg_jobs_universe_process_item(universe, pkg, NULL));
+
}
+

+
#define RECURSION_LIMIT 1024
+

+
static void
+
pkg_jobs_update_universe_item_priority(struct pkg_jobs_universe *universe,
+
		struct pkg_job_universe_item *item, int priority,
+
		enum pkg_priority_update_type type)
+
{
+
	const char *uid, *digest;
+
	struct pkg_dep *d = NULL;
+
	struct pkg_conflict *c = NULL;
+
	struct pkg_job_universe_item *found, *cur, *it;
+
	const char *is_local;
+
	int maxpri;
+

+
	int (*deps_func)(const struct pkg *pkg, struct pkg_dep **d);
+
	int (*rdeps_func)(const struct pkg *pkg, struct pkg_dep **d);
+

+
	if (priority > RECURSION_LIMIT) {
+
		pkg_debug(1, "recursion limit has been reached, something is bad"
+
					" with dependencies/conflicts graph");
+
		return;
+
	}
+
	else if (priority + 10 > RECURSION_LIMIT) {
+
		pkg_get(item->pkg, PKG_UNIQUEID, &uid);
+
		pkg_debug(2, "approaching recursion limit at %d, while processing of"
+
					" package %s", priority, uid);
+
	}
+

+
	LL_FOREACH(item, it) {
+

+
		pkg_get(it->pkg, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest);
+
		if ((item->next != NULL || item->prev != NULL) &&
+
				it->pkg->type != PKG_INSTALLED &&
+
				(type == PKG_PRIORITY_UPDATE_CONFLICT ||
+
				 type == PKG_PRIORITY_UPDATE_DELETE)) {
+
			/*
+
			 * We do not update priority of a remote part of conflict, as we know
+
			 * that remote packages should not contain conflicts (they should be
+
			 * resolved in request prior to calling of this function)
+
			 */
+
			pkg_debug(4, "skip update priority for %s-%s", uid, digest);
+
			continue;
+
		}
+
		if (it->priority > priority)
+
			continue;
+

+
		is_local = it->pkg->type == PKG_INSTALLED ? "local" : "remote";
+
		pkg_debug(2, "universe: update %s priority of %s(%s): %d -> %d, reason: %d",
+
				is_local, uid, digest, it->priority, priority, type);
+
		it->priority = priority;
+

+
		if (type == PKG_PRIORITY_UPDATE_DELETE) {
+
			/*
+
			 * For delete requests we inverse deps and rdeps logic
+
			 */
+
			deps_func = pkg_rdeps;
+
			rdeps_func = pkg_deps;
+
		}
+
		else {
+
			deps_func = pkg_deps;
+
			rdeps_func = pkg_rdeps;
+
		}
+

+
		while (deps_func(it->pkg, &d) == EPKG_OK) {
+
			HASH_FIND_STR(universe->items, d->uid, found);
+
			if (found != NULL) {
+
				LL_FOREACH(found, cur) {
+
					if (cur->priority < priority + 1)
+
						pkg_jobs_update_universe_item_priority(universe, cur,
+
								priority + 1, type);
+
				}
+
			}
+
		}
+

+
		d = NULL;
+
		maxpri = priority;
+
		while (rdeps_func(it->pkg, &d) == EPKG_OK) {
+
			HASH_FIND_STR(universe->items, d->uid, found);
+
			if (found != NULL) {
+
				LL_FOREACH(found, cur) {
+
					if (cur->priority >= maxpri) {
+
						maxpri = cur->priority + 1;
+
					}
+
				}
+
			}
+
		}
+
		if (maxpri != priority) {
+
			pkg_jobs_update_universe_item_priority(universe, it,
+
					maxpri, type);
+
			return;
+
		}
+
		if (it->pkg->type != PKG_INSTALLED) {
+
			while (pkg_conflicts(it->pkg, &c) == EPKG_OK) {
+
				HASH_FIND_STR(universe->items, pkg_conflict_uniqueid(c), found);
+
				if (found != NULL) {
+
					LL_FOREACH(found, cur) {
+
						if (cur->pkg->type == PKG_INSTALLED) {
+
							/*
+
							 * Move delete requests to be done before installing
+
							 */
+
							if (cur->priority <= it->priority)
+
								pkg_jobs_update_universe_item_priority(universe, cur,
+
									it->priority + 1, PKG_PRIORITY_UPDATE_CONFLICT);
+
						}
+
					}
+
				}
+
			}
+
		}
+
	}
+
}
+

+
void
+
pkg_jobs_update_conflict_priority(struct pkg_jobs_universe *universe,
+
	struct pkg_solved *req)
+
{
+
	struct pkg_conflict *c = NULL;
+
	struct pkg *lp = req->items[1]->pkg;
+
	struct pkg_job_universe_item *found, *cur, *rit = NULL;
+

+
	while (pkg_conflicts(lp, &c) == EPKG_OK) {
+
		rit = NULL;
+
		HASH_FIND_STR(universe->items, pkg_conflict_uniqueid(c), found);
+
		assert(found != NULL);
+

+
		LL_FOREACH(found, cur) {
+
			if (cur->pkg->type != PKG_INSTALLED) {
+
				rit = cur;
+
				break;
+
			}
+
		}
+

+
		assert(rit != NULL);
+
		if (rit->priority >= req->items[1]->priority) {
+
			pkg_jobs_update_universe_item_priority(universe, req->items[1],
+
					rit->priority + 1, PKG_PRIORITY_UPDATE_CONFLICT);
+
			/*
+
			 * Update priorities for a remote part as well
+
			 */
+
			pkg_jobs_update_universe_item_priority(universe, req->items[0],
+
					req->items[0]->priority, PKG_PRIORITY_UPDATE_REQUEST);
+
		}
+
	}
+
}
+

+

+
void
+
pkg_jobs_update_universe_priority(struct pkg_jobs_universe *universe,
+
	struct pkg_job_universe_item *it, enum pkg_priority_update_type type)
+
{
+
	pkg_jobs_update_universe_item_priority(universe, it, 0, type);
+
}
+

+
static void
+
pkg_jobs_universe_provide_free(struct pkg_job_provide *pr)
+
{
+
	struct pkg_job_provide *cur, *tmp;
+

+
	DL_FOREACH_SAFE(pr, cur, tmp) {
+
		free (cur);
+
	}
+
}
+

+
static void
+
pkg_jobs_universe_replacement_free(struct pkg_job_replace *r)
+
{
+
	free(r->new_uid);
+
	free(r->old_uid);
+
	free(r);
+
}
+

+
void
+
pkg_jobs_universe_free(struct pkg_jobs_universe *universe)
+
{
+
	struct pkg_job_universe_item *un, *untmp, *cur, *curtmp;
+

+
	HASH_ITER(hh, universe->items, un, untmp) {
+
		HASH_DEL(universe->items, un);
+

+
		LL_FOREACH_SAFE(un, cur, curtmp) {
+
			pkg_free(cur->pkg);
+
			free(cur);
+
		}
+
	}
+
	HASH_FREE(universe->seen, free);
+
	HASH_FREE(universe->provides, pkg_jobs_universe_provide_free);
+
	LL_FREE(universe->uid_replaces, pkg_jobs_universe_replacement_free);
+
}
+

+

+
struct pkg_jobs_universe *
+
pkg_jobs_universe_new(struct pkg_jobs *j)
+
{
+
	struct pkg_jobs_universe *universe;
+

+
	universe = calloc(1, sizeof(struct pkg_jobs_universe));
+
	if (universe == NULL) {
+
		pkg_emit_errno("pkg_jobs_universe_new", "calloc");
+
		return (NULL);
+
	}
+

+
	universe->j = j;
+

+
	return (universe);
+
}
+

+
struct pkg_job_universe_item *
+
pkg_jobs_universe_find(struct pkg_jobs_universe *universe, const char *uid)
+
{
+
	struct pkg_job_universe_item *unit;
+

+
	HASH_FIND_STR(universe->items, uid, unit);
+

+
	return (unit);
+
}
+

+
struct pkg_job_seen *
+
pkg_jobs_universe_seen(struct pkg_jobs_universe *universe, const char *digest)
+
{
+
	struct pkg_job_seen *seen;
+

+
	HASH_FIND_STR(universe->seen, digest, seen);
+

+
	return (seen);
+
}
+

+
void
+
pkg_jobs_universe_change_uid(struct pkg_jobs_universe *universe,
+
	struct pkg_job_universe_item *unit,
+
	const char *new_uid, size_t uidlen, bool update_rdeps)
+
{
+
	struct pkg_dep *rd = NULL, *d = NULL;
+
	struct pkg_job_universe_item *found;
+
	struct pkg *lp;
+
	const char *old_uid;
+
	struct pkg_job_replace *replacement;
+

+
	pkg_get(unit->pkg, PKG_UNIQUEID, &old_uid);
+

+
	if (update_rdeps) {
+
		/* For all rdeps update deps accordingly */
+
		while (pkg_rdeps(unit->pkg, &rd) == EPKG_OK) {
+
			found = pkg_jobs_universe_find(universe, rd->uid);
+
			if (found == NULL) {
+
				lp = pkg_jobs_universe_get_local(universe, rd->uid, 0);
+
				pkg_jobs_universe_process_item(universe, lp, &found);
+
			}
+

+
			if (found != NULL) {
+
				while (pkg_deps(found->pkg, &d) == EPKG_OK) {
+
					if (strcmp(d->uid, old_uid) == 0) {
+
						free(d->uid);
+
						d->uid = strdup(new_uid);
+
					}
+
				}
+
			}
+
		}
+
	}
+

+
	replacement = calloc(1, sizeof(*replacement));
+
	if (replacement != NULL) {
+
		replacement->old_uid = strdup(old_uid);
+
		replacement->new_uid = strdup(new_uid);
+
		LL_PREPEND(universe->uid_replaces, replacement);
+
	}
+

+
	HASH_DELETE(hh, universe->items, unit);
+
	pkg_set(unit->pkg, PKG_UNIQUEID, new_uid);
+
	HASH_FIND(hh, universe->items, new_uid, uidlen, found);
+
	if (found != NULL)
+
		DL_APPEND(found, unit);
+
	else
+
		HASH_ADD_KEYPTR(hh, universe->items, new_uid, uidlen, unit);
+

+
}
modified libpkg/pkg_solve.c
@@ -41,6 +41,7 @@
#include "private/event.h"
#include "private/pkg.h"
#include "private/pkgdb.h"
+
#include "private/pkg_jobs.h"

struct pkg_solve_item;

@@ -56,8 +57,7 @@ struct pkg_solve_variable {
		struct _pkg_solve_var_rule *next;
	} *rules;
	int nrules;
-
	UT_hash_handle hd;
-
	UT_hash_handle ho;
+
	UT_hash_handle hh;
	struct pkg_solve_variable *next, *prev;
};

@@ -79,7 +79,8 @@ struct pkg_solve_problem {
	unsigned int rules_count;
	struct pkg_solve_rule *rules;
	struct pkg_solve_variable *variables_by_uid;
-
	struct pkg_solve_variable *variables_by_digest;
+
	struct pkg_solve_variable *variables;
+
	size_t nvars;
};

struct pkg_solve_impl_graph {
@@ -95,6 +96,8 @@ struct pkg_solve_impl_graph {
#define PKG_SOLVE_CHECK_ITEM(item)				\
	((item)->var->to_install ^ (item)->inverse)

+
#define PKG_SOLVE_VAR_NEXT(a, e) ((e) == NULL ? &a[0] : (e + 1))
+

/**
 * Updates rules related to a single variable
 * @param var
@@ -179,12 +182,15 @@ pkg_solve_propagate_units(struct pkg_solve_problem *problem,
	struct pkg_solve_item *it, *unresolved = NULL;
	int solved_vars;
	struct _pkg_solve_var_rule *rul;
-
	struct pkg_solve_variable *var, *tvar;
+
	struct pkg_solve_variable *var;
	bool ret;
+
	size_t i;

	do {
		solved_vars = 0;
-
		HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
+
		var = NULL;
+
		for (i = 0; i < problem->nvars; i ++) {
+
			var = &problem->variables[i];
check_again:
			/* Check for direct conflicts */
			LL_FOREACH(var->rules, rul) {
@@ -287,11 +293,14 @@ check_again:
static bool
pkg_solve_propagate_pure(struct pkg_solve_problem *problem)
{
-
	struct pkg_solve_variable *var, *tvar;
+
	struct pkg_solve_variable *var;
	struct _pkg_solve_var_rule *rul;
	struct pkg_solve_item *it;
+
	size_t i;

-
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
+
	var = NULL;
+
	for (i = 0; i < problem->nvars; i ++) {
+
		var = &problem->variables[i];
		if (var->nrules == 0) {
			/* This variable is independent and should not change its state */
			assert (var->rules == NULL);
@@ -378,6 +387,7 @@ bool
pkg_solve_sat_problem(struct pkg_solve_problem *problem)
{
	int propagated;
+
	size_t i;
	struct pkg_solve_variable *var, *tvar;
	int64_t unresolved = 0, iters = 0;
	bool rc, backtrack = false, free_var;
@@ -406,18 +416,21 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
	elt = solver_tree;

	/* DPLL Algorithm */
-
	DL_FOREACH2(problem->variables_by_digest, var, hd.next) {
+
	var = NULL;
+
	for (i = 0; i < problem->nvars; i ++) {
+
		var = &problem->variables[i];
		if (!var->resolved) {

			if (backtrack) {
				/* Shift var back */
				var = tvar;
				backtrack = false;
+
				i --;
			}

			if (elt == NULL) {
				/* Add new element to the backtracking queue */
-
				elt = malloc (sizeof (*elt));
+
				elt = malloc(sizeof (*elt));
				if (elt == NULL) {
					pkg_emit_errno("malloc", "_solver_tree_elt");
					LL_FREE(solver_tree, free);
@@ -538,27 +551,18 @@ pkg_solve_rule_new(void)
	return (result);
}

-
static struct pkg_solve_variable *
-
pkg_solve_variable_new(struct pkg_job_universe_item *item)
+
static void
+
pkg_solve_variable_set(struct pkg_solve_variable *var,
+
	struct pkg_job_universe_item *item)
{
-
	struct pkg_solve_variable *result;
	const char *digest, *uid;

-
	result = calloc(1, sizeof(struct pkg_solve_variable));
-

-
	if(result == NULL) {
-
		pkg_emit_errno("calloc", "pkg_solve_variable");
-
		return (NULL);
-
	}
-

-
	result->unit = item;
+
	var->unit = item;
	pkg_get(item->pkg, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest);
	/* XXX: Is it safe to save a ptr here ? */
-
	result->digest = digest;
-
	result->uid = uid;
-
	result->prev = result;
-

-
	return (result);
+
	var->digest = digest;
+
	var->uid = uid;
+
	var->prev = var;
}

static void
@@ -572,83 +576,26 @@ pkg_solve_rule_free(struct pkg_solve_rule *rule)
	free(rule);
}

+

void
pkg_solve_problem_free(struct pkg_solve_problem *problem)
{
	struct pkg_solve_rule *r, *rtmp;
-
	struct pkg_solve_variable *v, *vtmp;
+
	struct pkg_solve_variable *v;
	struct _pkg_solve_var_rule *vrule, *vrtmp;

	LL_FOREACH_SAFE(problem->rules, r, rtmp) {
		pkg_solve_rule_free(r);
	}
-
	HASH_ITER(hd, problem->variables_by_digest, v, vtmp) {
-
		HASH_DELETE(hd, problem->variables_by_digest, v);
+

+
	v = NULL;
+
	while((v = PKG_SOLVE_VAR_NEXT(problem->variables, v))) {
+
		HASH_DELETE(hh, problem->variables_by_uid, v);
		LL_FOREACH_SAFE(v->rules, vrule, vrtmp) {
			free(vrule);
		}
-
		free(v);
	}
-
}
-

-
static int
-
pkg_solve_add_universe_variable(struct pkg_solve_problem *problem,
-
		const char *uid, struct pkg_solve_variable **var)
-
{
-
	struct pkg_job_universe_item *unit, *cur;
-
	struct pkg_solve_variable *nvar, *tvar = NULL, *found;
-
	const char *digest;
-
	struct pkg_jobs *j = problem->j;
-

-
	HASH_FIND_STR(j->universe, uid, unit);
-
	/* If there is no package in universe, refuse continue */
-
	if (unit == NULL) {
-
		pkg_debug(2, "package %s is not found in universe", uid);
-
		return (EPKG_FATAL);
-
	}
-
	/* Need to add a variable */
-
	nvar = pkg_solve_variable_new(unit);
-
	if (nvar == NULL)
-
		return (EPKG_FATAL);
-

-
	HASH_ADD_KEYPTR(hd, problem->variables_by_digest, nvar->digest,
-
			strlen(nvar->digest), nvar);
-

-
	/*
-
	 * Now we check the uid variable and if there is no such uid then
-
	 * we need to add the whole conflict chain to it
-
	 */
-
	HASH_FIND(ho, problem->variables_by_uid, uid, strlen(uid), found);
-
	if (found == NULL) {
-
		HASH_ADD_KEYPTR(ho, problem->variables_by_uid, nvar->uid,
-
				strlen(nvar->uid), nvar);
-
		pkg_debug(4, "solver: add variable from universe with uid %s", nvar->uid);
-

-
		/* Rewind to the beginning of the list */
-
		while (unit->prev->next != NULL)
-
			unit = unit->prev;
-

-
		LL_FOREACH (unit, cur) {
-
			pkg_get(cur->pkg, PKG_DIGEST, &digest);
-
			HASH_FIND(hd, problem->variables_by_digest, digest,
-
					strlen(digest), found);
-
			if (found == NULL) {
-
				/* Add all alternatives as independent variables */
-
				tvar = pkg_solve_variable_new(cur);
-
				if (tvar == NULL)
-
					return (EPKG_FATAL);
-
				DL_APPEND(nvar, tvar);
-
				HASH_ADD_KEYPTR(hd, problem->variables_by_digest, tvar->digest,
-
						strlen(tvar->digest), tvar);
-
				pkg_debug (4, "solver: add another variable with uid %s and digest %s",
-
						tvar->uid, tvar->digest);
-
			}
-
		}
-
	}
-

-
	*var = nvar;
-

-
	return (EPKG_OK);
+
	free(problem->variables);
}

static int
@@ -689,9 +636,8 @@ pkg_solve_handle_provide (struct pkg_solve_problem *problem,
{
	struct pkg_solve_item *it = NULL;
	const char *uid, *digest;
-
	struct pkg_solve_variable *var;
-
	struct pkg_job_universe_item *un, *cur;
-
	struct pkg_shlib *sh;
+
	struct pkg_solve_variable *var, *curvar;
+
	struct pkg_job_universe_item *un;

	/* Find the first package in the universe list */
	un = pr->un;
@@ -699,22 +645,13 @@ pkg_solve_handle_provide (struct pkg_solve_problem *problem,
		un = un->prev;
	}

-
	LL_FOREACH(un, cur) {
-
		/* For each provide */
-
		pkg_get(un->pkg, PKG_DIGEST, &digest, PKG_UNIQUEID, &uid);
-
		HASH_FIND(hd, problem->variables_by_digest, digest,
-
				strlen(digest), var);
-
		if (var == NULL) {
-
			if (pkg_solve_add_universe_variable(problem, uid,
-
					&var) != EPKG_OK)
-
				continue;
-
		}
-
		/* Check if we have the specified require provided by this package */
-
		HASH_FIND_STR(un->pkg->provides, pr->provide, sh);
-
		if (sh == NULL)
-
			continue;
+
	/* Find the corresponding variables chain */
+
	pkg_get(un->pkg, PKG_DIGEST, &digest, PKG_UNIQUEID, &uid);
+
	HASH_FIND_STR(problem->variables_by_uid, uid, var);

-
		it = pkg_solve_item_new(var);
+
	LL_FOREACH(var, curvar) {
+
		/* For each provide */
+
		it = pkg_solve_item_new(curvar);
		if (it == NULL)
			return (EPKG_FATAL);

@@ -727,270 +664,337 @@ pkg_solve_handle_provide (struct pkg_solve_problem *problem,
}

static int
-
pkg_solve_add_pkg_rule(struct pkg_solve_problem *problem,
-
		struct pkg_solve_variable *pvar, bool conflicting)
+
pkg_solve_add_depend_rule(struct pkg_solve_problem *problem,
+
		struct pkg_solve_variable *var,
+
		struct pkg_dep *dep)
{
-
	struct pkg_dep *dep, *dtmp;
-
	struct pkg_conflict *conflict, *ctmp;
-
	struct pkg *pkg;
-
	struct pkg_solve_rule *rule;
+
	const char *uid;
+
	struct pkg_solve_variable *depvar, *curvar;
+
	struct pkg_solve_rule *rule = NULL;
	struct pkg_solve_item *it = NULL;
-
	struct pkg_solve_variable *var, *tvar, *cur_var;
-
	struct pkg_shlib *shlib = NULL;
-
	struct pkg_job_provide *pr, *prhead;
	int cnt;
-
	struct pkg_jobs *j = problem->j;

+
	uid = dep->uid;
+
	HASH_FIND_STR(problem->variables_by_uid, uid, depvar);
+
	if (depvar == NULL) {
+
		pkg_debug(2, "cannot find variable dependency %s", uid);
+
		return (EPKG_END);
+
	}
+
	/* Dependency rule: (!A | B) */
+
	rule = pkg_solve_rule_new();
+
	if (rule == NULL)
+
		return (EPKG_FATAL);
+
	/* !A */
+
	it = pkg_solve_item_new(var);
+
	if (it == NULL)
+
		return (EPKG_FATAL);
+

+
	it->inverse = true;
+
	RULE_ITEM_PREPEND(rule, it);
+
	/* B1 | B2 | ... */
+
	cnt = 1;
+
	LL_FOREACH(depvar, curvar) {
+
		it = pkg_solve_item_new(curvar);
+
		if (it == NULL)
+
			return (EPKG_FATAL);
+

+
		it->inverse = false;
+
		RULE_ITEM_PREPEND(rule, it);
+
		cnt ++;
+
	}
+
	pkg_solve_add_var_rules (depvar, rule->items, cnt, true, "dependency");
+
	pkg_solve_add_var_rules (var, rule->items, cnt, false, "dependency");
+

+
	LL_PREPEND(problem->rules, rule);
+
	problem->rules_count ++;
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkg_solve_add_conflict_rule(struct pkg_solve_problem *problem,
+
		struct pkg *pkg,
+
		struct pkg_solve_variable *var,
+
		struct pkg_conflict *conflict)
+
{
	const char *uid;
+
	struct pkg_solve_variable *confvar, *curvar;
+
	struct pkg_solve_rule *rule = NULL;
+
	struct pkg_solve_item *it = NULL;

-
	/* Go through all deps in all variables*/
-
	LL_FOREACH(pvar, cur_var) {
-
		pkg = cur_var->unit->pkg;
-
		HASH_ITER(hh, pkg->deps, dep, dtmp) {
-
			rule = NULL;
-
			it = NULL;
-
			var = NULL;
-

-
			uid = dep->uid;
-
			HASH_FIND(ho, problem->variables_by_uid, uid, strlen(uid), var);
-
			if (var == NULL) {
-
				if (pkg_solve_add_universe_variable(problem, uid, &var) != EPKG_OK)
+
	uid = pkg_conflict_uniqueid(conflict);
+
	HASH_FIND_STR(problem->variables_by_uid, uid, confvar);
+
	if (confvar == NULL) {
+
		pkg_debug(2, "cannot find conflict %s", uid);
+
		return (EPKG_END);
+
	}
+

+
	/* Add conflict rule from each of the alternative */
+
	LL_FOREACH(confvar, curvar) {
+
		if (conflict->type == PKG_CONFLICT_REMOTE_LOCAL) {
+
			/* Skip unappropriate packages */
+
			if (pkg->type == PKG_INSTALLED) {
+
				if (curvar->unit->pkg->type == PKG_INSTALLED)
					continue;
			}
-
			/* Dependency rule: (!A | B) */
-
			rule = pkg_solve_rule_new();
-
			if (rule == NULL)
-
				goto err;
-
			/* !A */
-
			it = pkg_solve_item_new(cur_var);
-
			if (it == NULL)
-
				goto err;
-

-
			it->inverse = true;
-
			RULE_ITEM_PREPEND(rule, it);
-
			/* B1 | B2 | ... */
-
			cnt = 1;
-
			LL_FOREACH(var, tvar) {
-
				it = pkg_solve_item_new(tvar);
-
				if (it == NULL)
-
					goto err;
-

-
				it->inverse = false;
-
				RULE_ITEM_PREPEND(rule, it);
-
				cnt ++;
+
			else {
+
				if (curvar->unit->pkg->type != PKG_INSTALLED)
+
					continue;
			}
-
			pkg_solve_add_var_rules (var, rule->items, cnt, true, "dependency");
-
			pkg_solve_add_var_rules (cur_var, rule->items, cnt, false, "dependency");
+
		}
+
		else if (conflict->type == PKG_CONFLICT_REMOTE_REMOTE) {
+
			if (pkg->type == PKG_INSTALLED)
+
				continue;
+

+
			if (curvar->unit->pkg->type == PKG_INSTALLED)
+
				continue;
+
		}
+

+
		/* Conflict rule: (!A | !Bx) */
+
		rule = pkg_solve_rule_new();
+
		if (rule == NULL)
+
			return (EPKG_FATAL);
+
		/* !A */
+
		it = pkg_solve_item_new(var);
+
		if (it == NULL)
+
			return (EPKG_FATAL);
+

+
		it->inverse = true;
+
		RULE_ITEM_PREPEND(rule, it);
+
		/* !Bx */
+
		it = pkg_solve_item_new(curvar);
+
		if (it == NULL)
+
			return (EPKG_FATAL);
+

+
		it->inverse = true;
+
		RULE_ITEM_PREPEND(rule, it);
+

+
		LL_PREPEND(problem->rules, rule);
+
		problem->rules_count ++;
+
		pkg_solve_add_var_rules (curvar, rule->items, 2, false,
+
			"explicit conflict");
+
		pkg_solve_add_var_rules (var, rule->items, 2, false,
+
			"explicit conflict");
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkg_solve_add_require_rule(struct pkg_solve_problem *problem,
+
		struct pkg_solve_variable *var,
+
		struct pkg_shlib *shlib)
+
{
+
	struct pkg_solve_rule *rule;
+
	struct pkg_solve_item *it = NULL;
+
	struct pkg_job_provide *pr, *prhead;
+
	int cnt;
+

+
	HASH_FIND_STR(problem->j->universe->provides, pkg_shlib_name(shlib), prhead);
+
	if (prhead != NULL) {
+
		/* Require rule !A | P1 | P2 | P3 ... */
+
		rule = pkg_solve_rule_new();
+
		if (rule == NULL)
+
			return (EPKG_FATAL);
+
		/* !A */
+
		it = pkg_solve_item_new(var);
+
		if (it == NULL)
+
			return (EPKG_FATAL);
+

+
		it->inverse = true;
+
		RULE_ITEM_PREPEND(rule, it);
+
		/* B1 | B2 | ... */
+
		cnt = 1;
+
		LL_FOREACH(prhead, pr) {
+
			if (pkg_solve_handle_provide (problem, pr, rule,
+
				&cnt) != EPKG_OK)
+
				return (EPKG_FATAL);
+
		}
+

+
		if (cnt > 1) {
+
			pkg_solve_add_var_rules (var, rule->items, cnt, false, "provide");

			LL_PREPEND(problem->rules, rule);
			problem->rules_count ++;
		}
+
		else {
+
			/* Missing dependencies... */
+
			free(it);
+
			free(rule);
+
		}
+
	}
+
	else {
+
		/*
+
		 * XXX:
+
		 * This is terribly broken now so ignore till provides/requires
+
		 * are really fixed.
+
		 */
+
		pkg_debug(1, "solver: cannot find provide for required shlib %s",
+
			pkg_shlib_name(shlib));
+
	}

-
		/* Go through all conflicts */
-
		HASH_ITER(hh, pkg->conflicts, conflict, ctmp) {
-
			rule = NULL;
-
			it = NULL;
-
			var = NULL;
-

-
			uid = pkg_conflict_uniqueid(conflict);
-
			HASH_FIND(ho, problem->variables_by_uid, uid, strlen(uid), var);
-
			if (var == NULL) {
-
				if (pkg_solve_add_universe_variable(problem, uid, &var) != EPKG_OK)
-
					continue;
-
			}
-
			/* Return the uid to the package's uid and not conflict */
-
			pkg_get(pkg, PKG_UNIQUEID, &uid);
-
			/* Add conflict rule from each of the alternative */
-
			LL_FOREACH(var, tvar) {
-
				if (conflict->type == PKG_CONFLICT_REMOTE_LOCAL) {
-
					/* Skip unappropriate packages */
-
					if (pkg->type == PKG_INSTALLED) {
-
						if (tvar->unit->pkg->type == PKG_INSTALLED)
-
							continue;
-
					}
-
					else {
-
						if (tvar->unit->pkg->type != PKG_INSTALLED)
-
							continue;
-
					}
-
				}
-
				else if (conflict->type == PKG_CONFLICT_REMOTE_REMOTE) {
-
					if (pkg->type == PKG_INSTALLED)
-
						continue;
+
	return (EPKG_OK);
+
}

-
					if (tvar->unit->pkg->type == PKG_INSTALLED)
-
						continue;
-
				}
+
static int
+
pkg_solve_add_unary_rule(struct pkg_solve_problem *problem,
+
	struct pkg_solve_variable *var, bool inverse)
+
{
+
	struct pkg_solve_rule *rule;
+
	struct pkg_solve_item *it = NULL;

-
				/* Conflict rule: (!A | !Bx) */
-
				rule = pkg_solve_rule_new();
-
				if (rule == NULL)
-
					goto err;
-
				/* !A */
-
				it = pkg_solve_item_new(cur_var);
-
				if (it == NULL)
-
					goto err;
-

-
				it->inverse = true;
-
				RULE_ITEM_PREPEND(rule, it);
-
				/* !Bx */
-
				it = pkg_solve_item_new(tvar);
-
				if (it == NULL)
-
					goto err;
-

-
				it->inverse = true;
-
				RULE_ITEM_PREPEND(rule, it);
-

-
				LL_PREPEND(problem->rules, rule);
-
				problem->rules_count ++;
-
				pkg_solve_add_var_rules (tvar, rule->items, 2, false,
-
						"explicit conflict");
-
				pkg_solve_add_var_rules (cur_var, rule->items, 2, false,
-
						"explicit conflict");
-
			}
+
	pkg_debug(4, "solver: add variable from %s request with uid %s-%s",
+
		inverse ? "delete" : "install", var->uid, var->digest);
+

+
	it = pkg_solve_item_new(var);
+
	if (it == NULL)
+
		return (EPKG_FATAL);
+

+
	it->inverse = inverse;
+

+
	rule = pkg_solve_rule_new();
+
	if (rule == NULL)
+
		return (EPKG_FATAL);
+

+
	/* Requests are unary rules */
+
	RULE_ITEM_PREPEND(rule, it);
+
	pkg_solve_add_var_rules(var, it, 1, false, "unary request");
+
	LL_PREPEND(problem->rules, rule);
+
	problem->rules_count ++;
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkg_solve_add_chain_rule(struct pkg_solve_problem *problem,
+
	struct pkg_solve_variable *var)
+
{
+
	struct pkg_solve_variable *curvar;
+
	struct pkg_solve_rule *rule;
+
	struct pkg_solve_item *it = NULL;
+

+
	LL_FOREACH(var->next, curvar) {
+
		/* Conflict rule: (!Ax | !Ay) */
+
		rule = pkg_solve_rule_new();
+
		if (rule == NULL)
+
			return (EPKG_FATAL);
+
		/* !Ax */
+
		it = pkg_solve_item_new(var);
+
		if (it == NULL)
+
			return (EPKG_FATAL);
+

+
		it->inverse = true;
+
		RULE_ITEM_PREPEND(rule, it);
+
		/* !Ay */
+
		it = pkg_solve_item_new(curvar);
+
		if (it == NULL)
+
			return (EPKG_FATAL);
+

+
		it->inverse = true;
+
		RULE_ITEM_PREPEND(rule, it);
+

+
		LL_PREPEND(problem->rules, rule);
+
		problem->rules_count ++;
+

+
		pkg_solve_add_var_rules (curvar, rule->items, 2, false, "chain conflict");
+
		pkg_solve_add_var_rules (var, rule->items, 2, false, "chain conflict");
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkg_solve_process_universe_variable(struct pkg_solve_problem *problem,
+
		struct pkg_solve_variable *var)
+
{
+
	struct pkg_dep *dep, *dtmp;
+
	struct pkg_conflict *conflict, *ctmp;
+
	struct pkg *pkg;
+
	struct pkg_solve_variable *cur_var;
+
	struct pkg_shlib *shlib = NULL;
+
	struct pkg_jobs *j = problem->j;
+
	struct pkg_job_request *jreq;
+
	bool chain_added = false;
+

+
	LL_FOREACH(var, cur_var) {
+
		pkg = cur_var->unit->pkg;
+

+
		/* Depends */
+
		HASH_ITER(hh, pkg->deps, dep, dtmp) {
+
			if (pkg_solve_add_depend_rule(problem, cur_var, dep) != EPKG_OK)
+
				continue;
+
		}
+

+
		/* Conflicts */
+
		HASH_ITER(hh, pkg->conflicts, conflict, ctmp) {
+
			if (pkg_solve_add_conflict_rule(problem, pkg, cur_var, conflict) !=
+
							EPKG_OK)
+
				continue;
		}

-
		/* Check required shlibs */
+
		/* Shlibs */
		shlib = NULL;
		if (pkg->type != PKG_INSTALLED) {
			while (pkg_shlibs_required(pkg, &shlib) == EPKG_OK) {
-
				rule = NULL;
-
				it = NULL;
-
				var = NULL;
-
				HASH_FIND_STR(j->provides, pkg_shlib_name(shlib), prhead);
-
				if (prhead != NULL) {
-
					/* Require rule !A | P1 | P2 | P3 ... */
-
					rule = pkg_solve_rule_new();
-
					if (rule == NULL)
-
						goto err;
-
					/* !A */
-
					it = pkg_solve_item_new(cur_var);
-
					if (it == NULL)
-
						goto err;
-

-
					it->inverse = true;
-
					RULE_ITEM_PREPEND(rule, it);
-
					/* B1 | B2 | ... */
-
					cnt = 1;
-
					LL_FOREACH(prhead, pr) {
-
						if (pkg_solve_handle_provide (problem, pr, rule,
-
								&cnt) != EPKG_OK)
-
							goto err;
-
					}
-

-
					if (cnt > 1) {
-
						pkg_solve_add_var_rules (var, rule->items, cnt, true, "provide");
-
						pkg_solve_add_var_rules (cur_var, rule->items, cnt, false, "provide");
-

-
						LL_PREPEND(problem->rules, rule);
-
						problem->rules_count ++;
-
					}
-
					else {
-
						/* Missing dependencies... */
-
						free(it);
-
						free(rule);
-
					}
-
				}
-
				else {
-
					/*
-
					 * XXX:
-
					 * This is terribly broken now so ignore till provides/requires
-
					 * are really fixed.
-
					 */
-
					pkg_debug(1, "solver: cannot find provide for required shlib %s",
-
							pkg_shlib_name(shlib));
-
				}
+
				if (pkg_solve_add_require_rule(problem, cur_var, shlib) != EPKG_OK)
+
					continue;
			}
		}

-
		if (conflicting) {
-
			/*
-
			 * If this var chain contains mutually conflicting vars
-
			 * we need to register conflicts with all following
-
			 * vars
-
			 */
-
			var = cur_var->next;
-
			if (var != NULL) {
-
				LL_FOREACH(var, tvar) {
-
					/* Conflict rule: (!Ax | !Ay) */
-
					rule = pkg_solve_rule_new();
-
					if (rule == NULL)
-
						goto err;
-
					/* !Ax */
-
					it = pkg_solve_item_new(cur_var);
-
					if (it == NULL)
-
						goto err;
-

-
					it->inverse = true;
-
					RULE_ITEM_PREPEND(rule, it);
-
					/* !Ay */
-
					it = pkg_solve_item_new(tvar);
-
					if (it == NULL)
-
						goto err;
-

-
					it->inverse = true;
-
					RULE_ITEM_PREPEND(rule, it);
-

-
					LL_PREPEND(problem->rules, rule);
-
					problem->rules_count ++;
-

-
					pkg_solve_add_var_rules (tvar, rule->items, 2, false, "chain conflict");
-
					pkg_solve_add_var_rules (cur_var, rule->items, 2, false, "chain conflict");
-
				}
-
			}
+
		/* Request */
+
		HASH_FIND_PTR(j->request_add, &cur_var->unit, jreq);
+
		if (jreq != NULL)
+
			pkg_solve_add_unary_rule(problem, cur_var, false);
+
		HASH_FIND_PTR(j->request_delete, &cur_var->unit, jreq);
+
		if (jreq != NULL)
+
			pkg_solve_add_unary_rule(problem, cur_var, true);
+

+
		/*
+
		 * If this var chain contains mutually conflicting vars
+
		 * we need to register conflicts with all following
+
		 * vars
+
		 */
+
		if (!chain_added && cur_var->next != NULL) {
+
			if (pkg_solve_add_chain_rule(problem, cur_var) != EPKG_OK)
+
				continue;
+

+
			chain_added = true;
		}
	}

	return (EPKG_OK);
err:
-
	if (it != NULL)
-
		free(it);
-
	if (var != NULL)
-
		free(var);
-
	if (rule != NULL)
-
		pkg_solve_rule_free(rule);
	return (EPKG_FATAL);
}

static int
-
pkg_solve_add_universe_item(struct pkg_job_universe_item *un,
-
		struct pkg_solve_problem *problem)
+
pkg_solve_add_variable(struct pkg_job_universe_item *un,
+
		struct pkg_solve_problem *problem, size_t *n)
{
	struct pkg_job_universe_item *ucur;
-
	struct pkg_solve_variable *var = NULL, *tvar;
+
	struct pkg_solve_variable *var = NULL, *tvar = NULL;
	const char *uid, *digest;

-
	/* Rewind universe pointer */
-
	while (un->prev->next != NULL)
-
		un = un->prev;
-

	LL_FOREACH(un, ucur) {
+
		assert(*n < problem->nvars);
+

		pkg_get(ucur->pkg, PKG_UNIQUEID, &uid, PKG_DIGEST, &digest);
-
		HASH_FIND(hd, problem->variables_by_digest, digest, strlen(digest), var);
-
		if (var == NULL) {
-
			/* Add new variable */
-
			var = pkg_solve_variable_new(ucur);
-
			if (var == NULL)
-
				return (EPKG_FATAL);
-
			HASH_ADD_KEYPTR(hd, problem->variables_by_digest,
-
					var->digest, strlen(var->digest), var);
-

-
			/* Check uid */
-
			HASH_FIND(ho, problem->variables_by_uid, uid, strlen(uid), tvar);
-
			if (tvar == NULL) {
-
				pkg_debug(4, "solver: add variable from universe with uid %s", var->uid);
-
				HASH_ADD_KEYPTR(ho, problem->variables_by_uid,
-
						var->uid, strlen(var->uid), var);
-
			}
-
			else {
-
				/* Insert a variable to a chain */
-
				DL_APPEND(tvar, var);
-
			}
+
		/* Add new variable */
+
		var = &problem->variables[*n];
+
		pkg_solve_variable_set(var, ucur);
+

+
		if (tvar == NULL) {
+
			pkg_debug(4, "solver: add variable from universe with uid %s", var->uid);
+
			HASH_ADD_KEYPTR(hh, problem->variables_by_uid,
+
				var->uid, strlen(var->uid), var);
+
			tvar = var;
+
		}
+
		else {
+
			/* Insert a variable to a chain */
+
			DL_APPEND(tvar, var);
		}
+
		(*n) ++;
	}
-
	HASH_FIND(ho, problem->variables_by_uid, uid, strlen(uid), var);
-
	/* Now `var' contains a variables chain related to this uid */
-
	if (pkg_solve_add_pkg_rule(problem, var, true) == EPKG_FATAL)
-
		return (EPKG_FATAL);

	return (EPKG_OK);
}
@@ -999,12 +1003,8 @@ struct pkg_solve_problem *
pkg_solve_jobs_to_sat(struct pkg_jobs *j)
{
	struct pkg_solve_problem *problem;
-
	struct pkg_job_request *jreq, *jtmp;
-
	struct pkg_solve_rule *rule;
-
	struct pkg_solve_item *it;
	struct pkg_job_universe_item *un, *utmp;
-
	struct pkg_solve_variable *var;
-
	const char *digest;
+
	size_t i = 0;

	problem = calloc(1, sizeof(struct pkg_solve_problem));

@@ -1014,81 +1014,36 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
	}

	problem->j = j;
+
	problem->nvars = j->universe->nitems;
+
	problem->variables = calloc(problem->nvars, sizeof(struct pkg_solve_variable));

-
	/* Add requests */
-
	HASH_ITER(hh, j->request_add, jreq, jtmp) {
-
		if (jreq->skip)
-
			continue;
-

-
		rule = NULL;
-
		it = NULL;
-
		var = NULL;
-

-
		if (pkg_solve_add_universe_item(jreq->item, problem) == EPKG_FATAL)
-
			goto err;
-

-
		pkg_get(jreq->item->pkg, PKG_DIGEST, &digest);
-
		HASH_FIND(hd, problem->variables_by_digest, digest, strlen(digest), var);
-

-
		if (var == NULL) {
-
			pkg_emit_error("solver: variable has not been added, internal error");
-
			goto err;
-
		}
-

-
		pkg_debug(4, "solver: add variable from install request with uid %s-%s",
-
						var->uid, var->digest);
-

-
		it = pkg_solve_item_new(var);
-
		if (it == NULL)
-
			goto err;
-

-
		rule = pkg_solve_rule_new();
-
		if (rule == NULL)
-
			goto err;
-

-
		/* Requests are unary rules */
-
		RULE_ITEM_PREPEND(rule, it);
-
		pkg_solve_add_var_rules (var, it, 1, false, "unary add");
-
		LL_PREPEND(problem->rules, rule);
-
		problem->rules_count ++;
+
	if (problem->variables == NULL) {
+
		pkg_emit_errno("calloc", "variables");
+
		return (NULL);
	}
-
	HASH_ITER(hh, j->request_delete, jreq, jtmp) {
-
		if (jreq->skip)
-
			continue;

-
		rule = NULL;
-
		it = NULL;
-
		var = NULL;
-

-
		if (pkg_solve_add_universe_item(jreq->item, problem) == EPKG_FATAL)
+
	/* Parse universe */
+
	HASH_ITER(hh, j->universe->items, un, utmp) {
+
		/* Add corresponding variables */
+
		if (pkg_solve_add_variable(un, problem, &i)
+
						== EPKG_FATAL)
			goto err;
+
	}

-
		pkg_get(jreq->item->pkg, PKG_DIGEST, &digest);
-
		HASH_FIND(hd, problem->variables_by_digest, digest, strlen(digest), var);
+
	/* Add rules for all conflict chains */
+
	HASH_ITER(hh, j->universe->items, un, utmp) {
+
		const char *uid;
+
		struct pkg_solve_variable *var;

+
		pkg_get(un->pkg, PKG_UNIQUEID, &uid);
+
		HASH_FIND_STR(problem->variables_by_uid, uid, var);
		if (var == NULL) {
-
			pkg_emit_error("solver: variable has not been added, internal error");
+
			pkg_emit_error("internal solver error: variable %s is not found",
+
				uid);
			goto err;
		}
-

-
		pkg_debug(4, "solver: add variable from delete request with uid %s-%s",
-
				var->uid, var->digest);
-

-
		it = pkg_solve_item_new(var);
-
		if (it == NULL)
+
		if (pkg_solve_process_universe_variable(problem, var) != EPKG_OK)
			goto err;
-

-
		it->inverse = true;
-

-
		rule = pkg_solve_rule_new();
-
		if (rule == NULL)
-
			goto err;
-

-
		/* Requests are unary rules */
-
		RULE_ITEM_PREPEND(rule, it);
-
		pkg_solve_add_var_rules (var, it, 1, false, "unary delete");
-
		LL_PREPEND(problem->rules, rule);
-
		problem->rules_count ++;
	}

	if (problem->rules_count == 0) {
@@ -1096,25 +1051,9 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
		return (problem);
	}

-
	/* Parse universe */
-
	HASH_ITER(hh, j->universe, un, utmp) {
-
		rule = NULL;
-
		it = NULL;
-
		var = NULL;
-

-
		/* Add corresponding variables */
-
		if (pkg_solve_add_universe_item(un, problem) == EPKG_FATAL)
-
			goto err;
-
	}
-

	return (problem);
+

err:
-
	if (it != NULL)
-
		free(it);
-
	if (var != NULL)
-
		free(var);
-
	if (rule != NULL)
-
		pkg_solve_rule_free(rule);
	return (NULL);
}

@@ -1128,21 +1067,21 @@ int
pkg_solve_dimacs_export(struct pkg_solve_problem *problem, FILE *f)
{
	struct pkg_solve_ordered_variable *ordered_variables = NULL, *nord;
-
	struct pkg_solve_variable *var, *vtmp;
+
	struct pkg_solve_variable *var;
	struct pkg_solve_rule *rule;
	struct pkg_solve_item *it;
	int cur_ord = 1;

	/* Order variables */
-
	HASH_ITER(hd, problem->variables_by_digest, var, vtmp) {
+
	var = NULL;
+
	while ((var = PKG_SOLVE_VAR_NEXT(problem->variables, var))) {
		nord = calloc(1, sizeof(struct pkg_solve_ordered_variable));
		nord->order = cur_ord ++;
		nord->var = var;
		HASH_ADD_PTR(ordered_variables, var, nord);
	}

-
	fprintf(f, "p cnf %d %d\n", HASH_CNT(hd, problem->variables_by_digest),
-
			problem->rules_count);
+
	fprintf(f, "p cnf %d %d\n", (int)problem->nvars, problem->rules_count);

	LL_FOREACH(problem->rules, rule) {
		LL_FOREACH(rule->items, it) {
@@ -1177,13 +1116,6 @@ pkg_solve_insert_res_job (struct pkg_solve_variable *var,
			del_var = cur_var;
			seen_del ++;
		}
-
		else if (cur_var->to_install && cur_var->unit->reinstall) {
-
			pkg_set(cur_var->unit->pkg, PKG_REASON, "forced reinstall");
-
			add_var = cur_var;
-
			seen_add ++;
-
			del_var = cur_var;
-
			seen_del = 1;
-
		}
	}
	if (seen_add > 1) {
		pkg_emit_error("internal solver error: more than two packages to install(%d) "
@@ -1251,9 +1183,9 @@ pkg_solve_insert_res_job (struct pkg_solve_variable *var,
int
pkg_solve_sat_to_jobs(struct pkg_solve_problem *problem)
{
-
	struct pkg_solve_variable *var, *vtmp;
+
	struct pkg_solve_variable *var, *tvar;

-
	HASH_ITER(ho, problem->variables_by_uid, var, vtmp) {
+
	HASH_ITER(hh, problem->variables_by_uid, var, tvar) {
		if (!var->resolved)
			return (EPKG_FATAL);

@@ -1268,7 +1200,7 @@ int
pkg_solve_parse_sat_output(FILE *f, struct pkg_solve_problem *problem, struct pkg_jobs *j)
{
	struct pkg_solve_ordered_variable *ordered_variables = NULL, *nord;
-
	struct pkg_solve_variable *var, *vtmp;
+
	struct pkg_solve_variable *var;
	int cur_ord = 1, ret = EPKG_OK;
	char *line = NULL, *var_str, *begin;
	size_t linecap = 0;
@@ -1276,7 +1208,8 @@ pkg_solve_parse_sat_output(FILE *f, struct pkg_solve_problem *problem, struct pk
	bool got_sat = false, done = false;

	/* Order variables */
-
	HASH_ITER(hd, problem->variables_by_digest, var, vtmp) {
+
	var = NULL;
+
	while ((var = PKG_SOLVE_VAR_NEXT(problem->variables, var))) {
		nord = calloc(1, sizeof(struct pkg_solve_ordered_variable));
		nord->order = cur_ord ++;
		nord->var = var;
modified libpkg/private/pkg.h
@@ -209,76 +209,6 @@ struct pkg_option {
	UT_hash_handle	hh;
};

-
struct pkg_job_universe_item {
-
	struct pkg *pkg;
-
	struct job_pattern *jp;
-
	int priority;
-
	struct pkg *reinstall;
-
	UT_hash_handle hh;
-
	struct pkg_job_universe_item *next, *prev;
-
};
-

-
struct pkg_job_request {
-
	struct pkg_job_universe_item *item;
-
	bool skip;
-
	UT_hash_handle hh;
-
};
-

-
struct pkg_solved {
-
	struct pkg_job_universe_item *items[2];
-
	pkg_solved_t type;
-
	bool already_deleted;
-
	struct pkg_solved *prev, *next;
-
};
-

-
struct pkg_job_seen {
-
	struct pkg_job_universe_item *un;
-
	const char *digest;
-
	UT_hash_handle hh;
-
};
-

-
struct pkg_job_provide {
-
	struct pkg_job_universe_item *un;
-
	const char *provide;
-
	struct pkg_job_provide *next, *prev;
-
	UT_hash_handle hh;
-
};
-

-
struct pkg_job_replace {
-
	char *new_uid;
-
	char *old_uid;
-
	struct pkg_job_replace *next;
-
};
-

-
struct pkg_jobs {
-
	struct pkg_job_universe_item *universe;
-
	struct pkg_job_request	*request_add;
-
	struct pkg_job_request	*request_delete;
-
	struct pkg_solved *jobs;
-
	struct pkg_job_seen *seen;
-
	struct pkgdb	*db;
-
	struct pkg_job_provide *provides;
-
	struct pkg_job_replace *uid_replaces;
-
	pkg_jobs_t	 type;
-
	pkg_flags	 flags;
-
	int		 solved;
-
	int count;
-
	int total;
-
	int conflicts_registered;
-
	bool need_fetch;
-
	const char *reponame;
-
	const char *destdir;
-
	struct job_pattern *patterns;
-
};
-

-
struct job_pattern {
-
	char		*pattern;
-
	char		*path;
-
	match_t		match;
-
	bool		is_file;
-
	UT_hash_handle hh;
-
};
-

struct pkg_user {
	char		 name[MAXLOGNAME];
	char		 uidstr[8192];/* taken from pw_util.c */
@@ -551,12 +481,6 @@ const char* packing_format_to_string(pkg_formats format);
int pkg_delete_files(struct pkg *pkg, unsigned force);
int pkg_delete_dirs(struct pkgdb *db, struct pkg *pkg);

-
int pkg_conflicts_request_resolve(struct pkg_jobs *j);
-
int pkg_conflicts_append_pkg(struct pkg *p, struct pkg_jobs *j);
-
int pkg_conflicts_integrity_check(struct pkg_jobs *j);
-
void pkg_conflicts_register(struct pkg *p1, struct pkg *p2,
-
		enum pkg_conflict_type type);
-

typedef void (*conflict_func_cb)(const char *, const char *, void *);
int pkgdb_integrity_append(struct pkgdb *db, struct pkg *p,
		conflict_func_cb cb, void *cbdata);
added libpkg/private/pkg_jobs.h
@@ -0,0 +1,225 @@
+
/* Copyright (c) 2014, Vsevolod Stakhov
+
 * All rights reserved.
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions are met:
+
 *       * Redistributions of source code must retain the above copyright
+
 *         notice, this list of conditions and the following disclaimer.
+
 *       * Redistributions in binary form must reproduce the above copyright
+
 *         notice, this list of conditions and the following disclaimer in the
+
 *         documentation and/or other materials provided with the distribution.
+
 *
+
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+
#ifndef PKG_JOBS_H_
+
#define PKG_JOBS_H_
+

+
#include <sys/cdefs.h>
+
#include <sys/sbuf.h>
+
#include <sys/types.h>
+

+
#include <stdbool.h>
+
#include <uthash.h>
+
#include <utlist.h>
+
#include <ucl.h>
+

+
#include "private/utils.h"
+
#include "private/pkg.h"
+
#include "pkg.h"
+

+
struct pkg_jobs;
+
struct job_pattern;
+

+
struct pkg_job_universe_item {
+
	struct pkg *pkg;
+
	struct job_pattern *jp;
+
	int priority;
+
	UT_hash_handle hh;
+
	struct pkg_job_universe_item *next, *prev;
+
};
+

+
struct pkg_job_request {
+
	struct pkg_job_universe_item *item;
+
	bool skip;
+
	UT_hash_handle hh;
+
};
+

+
struct pkg_solved {
+
	struct pkg_job_universe_item *items[2];
+
	pkg_solved_t type;
+
	bool already_deleted;
+
	struct pkg_solved *prev, *next;
+
};
+

+
struct pkg_job_seen {
+
	struct pkg_job_universe_item *un;
+
	const char *digest;
+
	UT_hash_handle hh;
+
};
+

+
struct pkg_job_provide {
+
	struct pkg_job_universe_item *un;
+
	const char *provide;
+
	struct pkg_job_provide *next, *prev;
+
	UT_hash_handle hh;
+
};
+

+
struct pkg_job_replace {
+
	char *new_uid;
+
	char *old_uid;
+
	struct pkg_job_replace *next;
+
};
+

+

+
struct pkg_jobs_universe {
+
	struct pkg_job_universe_item *items;
+
	struct pkg_job_seen *seen;
+
	struct pkg_job_provide *provides;
+
	struct pkg_job_replace *uid_replaces;
+
	struct pkg_jobs *j;
+
	size_t nitems;
+
};
+

+
struct pkg_jobs {
+
	struct pkg_jobs_universe *universe;
+
	struct pkg_job_request	*request_add;
+
	struct pkg_job_request	*request_delete;
+
	struct pkg_solved *jobs;
+
	struct pkgdb	*db;
+
	pkg_jobs_t	 type;
+
	pkg_flags	 flags;
+
	int		 solved;
+
	int count;
+
	int total;
+
	int conflicts_registered;
+
	bool need_fetch;
+
	const char *reponame;
+
	const char *destdir;
+
	struct job_pattern *patterns;
+
};
+

+
struct job_pattern {
+
	char		*pattern;
+
	char		*path;
+
	match_t		match;
+
	bool		is_file;
+
	UT_hash_handle hh;
+
};
+

+
enum pkg_priority_update_type {
+
	PKG_PRIORITY_UPDATE_REQUEST = 0,
+
	PKG_PRIORITY_UPDATE_UNIVERSE,
+
	PKG_PRIORITY_UPDATE_CONFLICT,
+
	PKG_PRIORITY_UPDATE_DELETE
+
};
+

+
/*
+
 * Update priorities for all items related with the specified item
+
 */
+
void pkg_jobs_update_universe_priority(struct pkg_jobs_universe *universe,
+
	struct pkg_job_universe_item *it, enum pkg_priority_update_type type);
+
/*
+
 * Update priority as the conflict was found
+
 */
+
void pkg_jobs_update_conflict_priority(struct pkg_jobs_universe *universe,
+
	struct pkg_solved *req);
+

+
/*
+
 * Free universe
+
 */
+
void pkg_jobs_universe_free(struct pkg_jobs_universe *universe);
+

+
/*
+
 * Create universe for jobs
+
 */
+
struct pkg_jobs_universe * pkg_jobs_universe_new(struct pkg_jobs *j);
+

+
/*
+
 * Add a package to the universe
+
 */
+
int pkg_jobs_universe_process(struct pkg_jobs_universe *universe,
+
	struct pkg *pkg);
+

+
/*
+
 * Add a package to the universe and store resulting item in `result`
+
 */
+
int pkg_jobs_universe_process_item(struct pkg_jobs_universe *universe,
+
	struct pkg *pkg, struct pkg_job_universe_item **result);
+

+
/*
+
 * Add a universe item with package to the request
+
 */
+
void pkg_jobs_add_req(struct pkg_jobs *j, const char *uid,
+
	struct pkg_job_universe_item *item);
+

+
/*
+
 * Check if the specified digest was seen in the universe
+
 */
+
struct pkg_job_seen* pkg_jobs_universe_seen(struct pkg_jobs_universe *universe,
+
	const char *digest);
+

+
/*
+
 * Search for an entry corresponding to the uid in the universe
+
 */
+
struct pkg_job_universe_item* pkg_jobs_universe_find(struct pkg_jobs_universe
+
	*universe, const char *uid);
+

+
/*
+
 * Add a single package to the universe
+
 */
+
int pkg_jobs_universe_add_pkg(struct pkg_jobs_universe *universe,
+
	struct pkg *pkg, bool force, struct pkg_job_universe_item **found);
+

+
/*
+
 * Change uid for universe item
+
 */
+
void pkg_jobs_universe_change_uid(struct pkg_jobs_universe *universe,
+
	struct pkg_job_universe_item *unit,
+
	const char *new_uid, size_t uidlen, bool update_rdeps);
+

+
/*
+
 * Find remote package in db or universe
+
 */
+
struct pkg* pkg_jobs_universe_get_remote(struct pkg_jobs_universe *universe,
+
	const char *uid, unsigned flag);
+

+
/*
+
 * Find local package in db or universe
+
 */
+
struct pkg* pkg_jobs_universe_get_local(struct pkg_jobs_universe *universe,
+
	const char *uid, unsigned flag);
+

+
/*
+
 * Resolve conflicts in request
+
 */
+
int pkg_conflicts_request_resolve(struct pkg_jobs *j);
+

+
/*
+
 * Append conflicts to a package
+
 */
+
int pkg_conflicts_append_pkg(struct pkg *p, struct pkg_jobs *j);
+
/*
+
 * Perform integrity check for the jobs specified
+
 */
+
int pkg_conflicts_integrity_check(struct pkg_jobs *j);
+
/*
+
 * Register a conflict between two packages
+
 */
+
void pkg_conflicts_register(struct pkg *p1, struct pkg *p2,
+
		enum pkg_conflict_type type);
+

+
/*
+
 * Check whether `rp` is an upgrade for `lp`
+
 */
+
bool pkg_jobs_need_upgrade(struct pkg *rp, struct pkg *lp);
+

+
#endif /* PKG_JOBS_H_ */