Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'master' into indexfile
Matthew Seaman committed 12 years ago
commit edb8f16bf401316a1cb4c7803a5a27262ea8295f
parent 6b5b649
24 files changed +1177 -363
modified configure.ac
@@ -2,16 +2,17 @@ m4_define([maj_ver], [1])
m4_define([med_ver], [3])
m4_define([min_ver], [0])
m4_define([api_ver], [m4_eval(maj_ver * 1000000 + med_ver * 1000 + min_ver)])
-
m4_define([pkg_version], [maj_ver.med_ver.min_ver.a4])
+
m4_define([pkg_version], [maj_ver.med_ver.min_ver.a5])

AC_INIT([pkg],[pkg_version],[https://github.com/freebsd/pkg],[pkg])
AC_CONFIG_SRCDIR([configure.ac])
AM_INIT_AUTOMAKE([1.11 foreign -Wno-portability no-dist-gzip dist-xz])
-
#AM_SILENT_RULES([yes])
+
AM_SILENT_RULES([yes])

AC_PROG_CC_C99
LT_INIT()
AC_CONFIG_MACRO_DIR([m4])
+
AX_CFLAGS_WARN_ALL

AC_C_CONST
AC_TYPE_SIZE_T
modified docs/pkg-which.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd March 25, 2013
+
.Dd March 26, 2014
.Dt PKG-WHICH 8
.Os
.Sh NAME
@@ -41,6 +41,8 @@ Shows the origin of the package instead of name-version
Treat
.Ao file Ac
as a glob pattern.
+
.It Fl p
+
Search for the filename in PATH.
.El
.Sh ENVIRONMENT
The following environment variables affect the execution of
modified libpkg/pkg.c
@@ -1131,47 +1131,47 @@ void
pkg_list_free(struct pkg *pkg, pkg_list list)  {
	switch (list) {
	case PKG_DEPS:
-
		HASH_FREE(pkg->deps, pkg_dep, pkg_dep_free);
+
		HASH_FREE(pkg->deps, pkg_dep_free);
		pkg->flags &= ~PKG_LOAD_DEPS;
		break;
	case PKG_RDEPS:
-
		HASH_FREE(pkg->rdeps, pkg_dep, pkg_dep_free);
+
		HASH_FREE(pkg->rdeps, pkg_dep_free);
		pkg->flags &= ~PKG_LOAD_RDEPS;
		break;
	case PKG_LICENSES:
-
		HASH_FREE(pkg->licenses, pkg_license, pkg_license_free);
+
		HASH_FREE(pkg->licenses, pkg_license_free);
		pkg->flags &= ~PKG_LOAD_LICENSES;
		break;
	case PKG_OPTIONS:
-
		HASH_FREE(pkg->options, pkg_option, pkg_option_free);
+
		HASH_FREE(pkg->options, pkg_option_free);
		pkg->flags &= ~PKG_LOAD_OPTIONS;
		break;
	case PKG_CATEGORIES:
-
		HASH_FREE(pkg->categories, pkg_category, free);
+
		HASH_FREE(pkg->categories, free);
		pkg->flags &= ~PKG_LOAD_CATEGORIES;
		break;
	case PKG_FILES:
-
		HASH_FREE(pkg->files, pkg_file, pkg_file_free);
+
		HASH_FREE(pkg->files, pkg_file_free);
		pkg->flags &= ~PKG_LOAD_FILES;
		break;
	case PKG_DIRS:
-
		HASH_FREE(pkg->dirs, pkg_dir, pkg_dir_free);
+
		HASH_FREE(pkg->dirs, pkg_dir_free);
		pkg->flags &= ~PKG_LOAD_DIRS;
		break;
	case PKG_USERS:
-
		HASH_FREE(pkg->users, pkg_user, pkg_user_free);
+
		HASH_FREE(pkg->users, pkg_user_free);
		pkg->flags &= ~PKG_LOAD_USERS;
		break;
	case PKG_GROUPS:
-
		HASH_FREE(pkg->groups, pkg_group, pkg_group_free);
+
		HASH_FREE(pkg->groups, pkg_group_free);
		pkg->flags &= ~PKG_LOAD_GROUPS;
		break;
	case PKG_SHLIBS_REQUIRED:
-
		HASH_FREE(pkg->shlibs_required, pkg_shlib, pkg_shlib_free);
+
		HASH_FREE(pkg->shlibs_required, pkg_shlib_free);
		pkg->flags &= ~PKG_LOAD_SHLIBS_REQUIRED;
		break;
	case PKG_SHLIBS_PROVIDED:
-
		HASH_FREE(pkg->shlibs_provided, pkg_shlib, pkg_shlib_free);
+
		HASH_FREE(pkg->shlibs_provided, pkg_shlib_free);
		pkg->flags &= ~PKG_LOAD_SHLIBS_PROVIDED;
		break;
	case PKG_ANNOTATIONS:
@@ -1182,11 +1182,11 @@ pkg_list_free(struct pkg *pkg, pkg_list list) {
		pkg->flags &= ~PKG_LOAD_ANNOTATIONS;
		break;
	case PKG_CONFLICTS:
-
		HASH_FREE(pkg->conflicts, pkg_conflict, pkg_conflict_free);
+
		HASH_FREE(pkg->conflicts, pkg_conflict_free);
		pkg->flags &= ~PKG_LOAD_CONFLICTS;
		break;
	case PKG_PROVIDES:
-
		HASH_FREE(pkg->provides, pkg_provide, pkg_provide_free);
+
		HASH_FREE(pkg->provides, pkg_provide_free);
		pkg->flags &= ~PKG_LOAD_PROVIDES;
		break;
	}
modified libpkg/pkg.h.in
@@ -1065,7 +1065,7 @@ struct pkgdb_it * pkgdb_query_which(struct pkgdb *db, const char *path, bool glo
struct pkgdb_it * pkgdb_query_shlib_required(struct pkgdb *db, const char *shlib);
struct pkgdb_it * pkgdb_query_shlib_provided(struct pkgdb *db, const char *shlib);

-
struct pkgdb_it * pkgdb_query_provide(struct pkgdb *db, 
+
struct pkgdb_it * pkgdb_rquery_provide(struct pkgdb *db, 
    const char *provide, const char *repo);

/**
modified libpkg/pkg_config.c
@@ -925,7 +925,7 @@ pkg_shutdown(void)
	}

	ucl_object_unref(config);
-
	HASH_FREE(repos, pkg_repo, pkg_repo_free);
+
	HASH_FREE(repos, pkg_repo_free);

	parsed = false;

modified libpkg/pkg_conflicts.c
@@ -134,10 +134,10 @@ pkg_conflicts_request_resolve(struct pkg_jobs *j)
			pkg_conflicts_request_add_chain(&chain, req);

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

@@ -145,7 +145,7 @@ pkg_conflicts_request_resolve(struct pkg_jobs *j)
}

void
-
pkg_conflicts_register(struct pkg *p1, struct pkg *p2)
+
pkg_conflicts_register(struct pkg *p1, struct pkg *p2, enum pkg_conflict_type type)
{
	struct pkg_conflict *c1, *c2, *test;
	const char *o1, *o2;
@@ -156,6 +156,7 @@ pkg_conflicts_register(struct pkg *p1, struct pkg *p2)
	pkg_conflict_new(&c1);
	pkg_conflict_new(&c2);
	if (c1 != NULL && c2 != NULL) {
+
		c1->type = c2->type = type;
		HASH_FIND_STR(p1->conflicts, o2, test);
		if (test == NULL) {
			sbuf_set(&c1->origin, o2);
modified libpkg/pkg_jobs.c
@@ -46,7 +46,7 @@
#include "private/pkgdb.h"

static int find_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m,
-
		bool root, bool recursive);
+
		bool root, bool recursive, bool add_request);
static struct pkg *get_local_pkg(struct pkg_jobs *j, const char *origin, unsigned flag);
static struct pkg *get_remote_pkg(struct pkg_jobs *j, const char *origin, unsigned flag);
static int pkg_jobs_fetch(struct pkg_jobs *j);
@@ -103,6 +103,16 @@ 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);
+
	}
+
}
+

void
pkg_jobs_free(struct pkg_jobs *j)
{
@@ -127,9 +137,10 @@ pkg_jobs_free(struct pkg_jobs *j)
			free(cur);
		}
	}
-
	HASH_FREE(j->seen, pkg_job_seen, free);
-
	HASH_FREE(j->patterns, job_pattern, pkg_jobs_pattern_free);
-
	LL_FREE(j->jobs, pkg_solved, free);
+
	HASH_FREE(j->seen, free);
+
	HASH_FREE(j->patterns, pkg_jobs_pattern_free);
+
	HASH_FREE(j->provides, pkg_jobs_provide_free);
+
	LL_FREE(j->jobs, free);

	free(j);
}
@@ -240,7 +251,6 @@ pkg_jobs_add_req(struct pkg_jobs *j, const char *origin, struct pkg_job_universe
		bool add)
{
	struct pkg_job_request *req, *test, **head;
-
	bool replace = false;

	if (add)
		head = &j->request_add;
@@ -263,7 +273,7 @@ pkg_jobs_add_req(struct pkg_jobs *j, const char *origin, struct pkg_job_universe
}

enum pkg_priority_update_type {
-
	PKG_PRIORITY_UPDATE_REQUEST,
+
	PKG_PRIORITY_UPDATE_REQUEST = 0,
	PKG_PRIORITY_UPDATE_UNIVERSE,
	PKG_PRIORITY_UPDATE_CONFLICT,
	PKG_PRIORITY_UPDATE_DELETE
@@ -287,18 +297,24 @@ pkg_jobs_update_universe_priority(struct pkg_jobs *j,
	LL_FOREACH(item, it) {

		pkg_get(it->pkg, PKG_ORIGIN, &origin, PKG_DIGEST, &digest);
-
		if (it->pkg->type != PKG_INSTALLED && type == PKG_PRIORITY_UPDATE_CONFLICT) {
+
		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", origin, 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",
-
				is_local, origin, digest, it->priority, priority);
+
		pkg_debug(2, "universe: update %s priority of %s(%s): %d -> %d, reason: %d",
+
				is_local, origin, digest, it->priority, priority, type);
		it->priority = priority;

		if (type == PKG_PRIORITY_UPDATE_DELETE) {
@@ -364,7 +380,7 @@ pkg_jobs_update_universe_priority(struct pkg_jobs *j,
static void
pkg_jobs_update_conflict_priority(struct pkg_jobs *j, struct pkg_solved *req)
{
-
	struct pkg_conflict *c, *ctmp;
+
	struct pkg_conflict *c = NULL;
	struct pkg *lp = req->items[1]->pkg;
	struct pkg_job_universe_item *found, *cur, *rit = NULL;

@@ -393,26 +409,30 @@ pkg_jobs_update_conflict_priority(struct pkg_jobs *j, struct pkg_solved *req)
	}
}

-
static void
+
static int
pkg_jobs_set_request_priority(struct pkg_jobs *j, struct pkg_solved *req)
{
	struct pkg_solved *treq;
	const char *origin;

-
	if (req->type == PKG_SOLVED_UPGRADE && req->items[1]->pkg->conflicts != NULL) {
+
	if (req->type == PKG_SOLVED_UPGRADE
+
			&& req->items[1]->pkg->conflicts != NULL) {
		/*
		 * We have an upgrade request that has some conflicting packages, therefore
		 * update priorities of local packages and try to update priorities of remote ones
		 */
-
		pkg_jobs_update_conflict_priority(j, req);
-
		if (req->items[1]->priority > req->items[0]->priority) {
+
		if (req->items[0]->priority == 0)
+
			pkg_jobs_update_conflict_priority(j, req);
+

+
		if (req->items[1]->priority > req->items[0]->priority &&
+
				!req->already_deleted) {
			/*
			 * Split conflicting upgrade request into delete -> upgrade request
			 */
			treq = calloc(1, sizeof(struct pkg_solved));
			if (treq == NULL) {
				pkg_emit_errno("calloc", "pkg_solved");
-
				return;
+
				return (EPKG_FATAL);
			}

			treq->type = PKG_SOLVED_UPGRADE_REMOVE;
@@ -421,6 +441,7 @@ pkg_jobs_set_request_priority(struct pkg_jobs *j, struct pkg_solved *req)
			req->already_deleted = true;
			pkg_get(treq->items[0]->pkg, PKG_ORIGIN, &origin);
			pkg_debug(2, "split upgrade request for %s", origin);
+
			return (EPKG_CONFLICT);
		}
	}
	else if (req->type == PKG_SOLVED_DELETE) {
@@ -433,11 +454,21 @@ pkg_jobs_set_request_priority(struct pkg_jobs *j, struct pkg_solved *req)
			pkg_jobs_update_universe_priority(j, req->items[0], 0,
					PKG_PRIORITY_UPDATE_REQUEST);
	}
+

+
	return (EPKG_OK);
}

static int
pkg_jobs_sort_priority(struct pkg_solved *r1, struct pkg_solved *r2)
{
+
	if (r1->items[0]->priority == r2->items[0]->priority) {
+
		if (r1->type == PKG_SOLVED_DELETE && r2->type != PKG_SOLVED_DELETE)
+
			return (-1);
+
		else if (r2->type == PKG_SOLVED_DELETE && r1->type != PKG_SOLVED_DELETE)
+
			return (1);
+

+
		return (0);
+
	}
	return (r2->items[0]->priority - r1->items[0]->priority);
}

@@ -446,10 +477,15 @@ pkg_jobs_set_priorities(struct pkg_jobs *j)
{
	struct pkg_solved *req;

+
iter_again:
	LL_FOREACH(j->jobs, req) {
-
		if (req->items[0]->priority == 0) {
-
			pkg_jobs_set_request_priority(j, req);
-
		}
+
		req->items[0]->priority = 0;
+
		if (req->items[1] != NULL)
+
			req->items[1]->priority = 0;
+
	}
+
	LL_FOREACH(j->jobs, req) {
+
		if (pkg_jobs_set_request_priority(j, req) == EPKG_CONFLICT)
+
			goto iter_again;
	}

	DL_SORT(j->jobs, pkg_jobs_sort_priority);
@@ -491,7 +527,6 @@ pkg_jobs_handle_pkg_universe(struct pkg_jobs *j, struct pkg *pkg,
{
	struct pkg_job_universe_item *item, *cur, *tmp = NULL;
	const char *origin, *digest, *version, *name;
-
	int rc;
	struct pkg_job_seen *seen;

	pkg_get(pkg, PKG_ORIGIN, &origin, PKG_DIGEST, &digest,
@@ -552,9 +587,16 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
	struct pkg_dep *d = NULL;
	struct pkg_conflict *c = NULL;
	struct pkg *npkg, *rpkg;
-
	int ret;
	struct pkg_job_universe_item *unit;
-
	const char *origin;
+
	struct pkg_shlib *shlib = NULL;
+
	struct pkgdb_it *it;
+
	struct pkg_job_provide *pr, *prhead;
+
	struct pkg_job_seen *seen;
+
	int ret;
+
	const char *origin, *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 */
@@ -566,27 +608,6 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
			return (EPKG_OK);
		else if (ret != EPKG_OK)
			return (EPKG_FATAL);
-

-
		if (pkg->type == PKG_INSTALLED &&
-
				(j->type == PKG_JOBS_UPGRADE ||
-
						j->type == PKG_JOBS_INSTALL)) {
-
			/* Check if remote has newer version */
-
			pkg_get(pkg, PKG_ORIGIN, &origin);
-
			rpkg = get_remote_pkg(j, origin, 0);
-
			if (rpkg != NULL) {
-
				if (!pkg_need_upgrade(rpkg, pkg, false)) {
-
					pkg_free(rpkg);
-
					rpkg = NULL;
-
				}
-
				else {
-
					if (pkg_jobs_add_universe(j, rpkg, recursive, false, NULL)
-
							!= EPKG_OK)
-
						return (EPKG_FATAL);
-
				}
-
			}
-

-
			rpkg = NULL;
-
		}
	}

	/* Go through all depends */
@@ -683,6 +704,63 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
			return (EPKG_FATAL);
	}

+
	/* For remote packages we should also handle shlib deps */
+
	if (pkg->type != PKG_INSTALLED) {
+
		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_find_shlib_provide(j->db, pkg_shlib_name(shlib), j->reponame);
+
			if (it != NULL) {
+
				npkg = NULL;
+
				prhead = NULL;
+
				while (pkgdb_it_next(it, &npkg, flags) == EPKG_OK) {
+
					/* Skip seen packages */
+
					pkg_get(npkg, PKG_DIGEST, &digest);
+
					HASH_FIND_STR(j->seen, digest, seen);
+
					if (seen == NULL) {
+
						pkg_jobs_add_universe(j, npkg, recursive, false,
+
								&unit);
+

+
						/* Reset package to avoid freeing */
+
						npkg = 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_ORIGIN, &origin);
+
					pkg_debug(1, "cannot find packages that provide %s required for %s",
+
							pkg_shlib_name(shlib), origin);
+
					/*
+
					 * XXX: this is not normal but it is very common for the existing
+
					 * repos, hence we just ignore this stale dependency
+
					 */
+
				}
+
			}
+
		}
+
	}
+

	return (EPKG_OK);
}

@@ -758,7 +836,7 @@ new_pkg_version(struct pkg_jobs *j)
	pkg_jobs_add_universe(j, p, true, false, NULL);

	/* Use maximum priority for pkg */
-
	if (find_remote_pkg(j, origin, MATCH_EXACT, true, true) == EPKG_OK) {
+
	if (find_remote_pkg(j, origin, MATCH_EXACT, false, true, true) == EPKG_OK) {
		ret = true;
		goto end;
	}
@@ -771,11 +849,14 @@ 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 root, bool force, bool recursive,
+
		struct pkg_job_universe_item **unit,
+
		bool add_request)
{
	struct pkg *p1;
	struct pkg_job_universe_item *jit;
	struct pkg_job_seen *seen;
+
	struct pkg_job_request *jreq;
	int rc = EPKG_FATAL;
	const char *origin, *digest;

@@ -793,6 +874,10 @@ pkg_jobs_process_remote_pkg(struct pkg_jobs *j, struct pkg *p,
		/* We have already added exactly the same package to the universe */
		pkg_debug(3, "already seen package %s-%s in the universe, do not add it again",
				origin, digest);
+
		/* However, we may want to add it to the job request */
+
		HASH_FIND_STR(j->request_add, origin, jreq);
+
		if (jreq == NULL)
+
			pkg_jobs_add_req(j, origin, seen->un, true);
		return (EPKG_OK);
	}
	HASH_FIND_STR(j->universe, origin, jit);
@@ -834,7 +919,8 @@ pkg_jobs_process_remote_pkg(struct pkg_jobs *j, struct pkg *p,
	p->direct = root;
	/* Add a package to request chain and populate universe */
	rc = pkg_jobs_add_universe(j, p, recursive, false, &jit);
-
	pkg_jobs_add_req(j, origin, jit, true);
+
	if (add_request)
+
		pkg_jobs_add_req(j, origin, jit, true);

	if (unit != NULL)
		*unit = jit;
@@ -844,14 +930,15 @@ pkg_jobs_process_remote_pkg(struct pkg_jobs *j, struct pkg *p,

static int
find_remote_pkg(struct pkg_jobs *j, const char *pattern,
-
		match_t m, bool root, bool recursive)
+
		match_t m, bool root, bool recursive, bool add_request)
{
	struct pkg *p = NULL;
	struct pkgdb_it *it;
	bool force = false;
	int rc = EPKG_FATAL;
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
-
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
+
			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;
@@ -867,7 +954,8 @@ find_remote_pkg(struct pkg_jobs *j, const char *pattern,
		return (rc);

	while (pkgdb_it_next(it, &p, flags) == EPKG_OK) {
-
		rc = pkg_jobs_process_remote_pkg(j, p, root, force, recursive, NULL);
+
		rc = pkg_jobs_process_remote_pkg(j, p, root, force, recursive,
+
				NULL, add_request);
		p = NULL;
	}

@@ -886,7 +974,7 @@ pkg_jobs_find_remote_pattern(struct pkg_jobs *j, struct job_pattern *jp,
	struct pkg_job_universe_item *unit;

	if (!jp->is_file) {
-
		rc = find_remote_pkg(j, jp->pattern, jp->match, true, true);
+
		rc = find_remote_pkg(j, jp->pattern, jp->match, true, true, true);
	}
	else {
		pkg_manifest_keys_new(&keys);
@@ -895,7 +983,7 @@ pkg_jobs_find_remote_pattern(struct pkg_jobs *j, struct job_pattern *jp,
		else {
			pkg->type = PKG_FILE;
			rc = pkg_jobs_process_remote_pkg(j, pkg, true,
-
					j->flags & PKG_FLAG_FORCE, false, &unit);
+
					j->flags & PKG_FLAG_FORCE, false, &unit, true);
			unit->jp = jp;
			*got_local = true;
		}
@@ -936,8 +1024,8 @@ get_remote_pkg(struct pkg_jobs *j, const char *origin, unsigned flag)

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

	if ((it = pkgdb_rquery(j->db, origin, MATCH_EXACT, j->reponame)) == NULL)
@@ -1148,10 +1236,11 @@ pkg_conflicts_add_missing(struct pkg_jobs *j, const char *origin)
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)
+
		struct pkg_job_universe_item *u2, bool local_only,
+
		enum pkg_conflict_type type)
{

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

static void
@@ -1195,15 +1284,22 @@ pkg_conflicts_add_from_pkgdb_local(const char *o1, const char *o2, void *ud)
	 */
	LL_FOREACH(u1, cur1) {
		LL_FOREACH(u2, cur2) {
-
			if ((cur1->pkg->type == PKG_INSTALLED && cur2->pkg->type != PKG_INSTALLED) ||
-
				(cur2->pkg->type == PKG_INSTALLED && cur1->pkg->type != PKG_INSTALLED)) {
+
			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_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 ++;
+
			}
		}
	}
}
@@ -1244,7 +1340,7 @@ pkg_conflicts_add_from_pkgdb_remote(const char *o1, const char *o2, void *ud)
					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_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);
@@ -1271,6 +1367,62 @@ pkg_conflicts_integrity_check(struct pkg_jobs *j)
	return (pkgdb_integrity_check(j->db, pkg_conflicts_add_from_pkgdb_local, j));
}

+
static struct pkg_job_request *
+
pkg_jobs_find_deinstall_request(struct pkg_job_universe_item *item, struct pkg_jobs *j)
+
{
+
	struct pkg_job_request *found;
+
	struct pkg_job_universe_item *dep_item;
+
	struct pkg_dep *d = NULL;
+
	const char *origin;
+
	struct pkg *pkg = item->pkg;
+

+
	pkg_get(pkg, PKG_ORIGIN, &origin);
+

+
	HASH_FIND_STR(j->request_delete, origin, found);
+
	if (found == NULL) {
+
		while (pkg_deps(pkg, &d) == EPKG_OK) {
+
			HASH_FIND_STR(j->universe, pkg_dep_get(d, PKG_DEP_ORIGIN), dep_item);
+
			if (dep_item) {
+
				found = pkg_jobs_find_deinstall_request(dep_item, j);
+
				if (found)
+
					return (found);
+
			}
+
		}
+
	}
+
	else {
+
		return (found);
+
	}
+

+
	return (NULL);
+
}
+

+
static void
+
pkg_jobs_set_deinstall_reasons(struct pkg_jobs *j)
+
{
+
	struct sbuf *reason = sbuf_new_auto();
+
	struct pkg_solved *sit;
+
	struct pkg_job_request *jreq;
+
	struct pkg *req_pkg, *pkg;
+
	const char *name, *version;
+

+
	LL_FOREACH(j->jobs, sit) {
+
		jreq = pkg_jobs_find_deinstall_request(sit->items[0], j);
+
		if (jreq->item != sit->items[0]) {
+
			req_pkg = jreq->item->pkg;
+
			pkg = sit->items[0]->pkg;
+
			/* Set the reason */
+
			pkg_get(req_pkg, PKG_NAME, &name, PKG_VERSION, &version);
+
			sbuf_printf(reason, "depends on %s-%s", name, version);
+
			sbuf_finish(reason);
+

+
			pkg_set(pkg, PKG_REASON, sbuf_data(reason));
+
			sbuf_clear(reason);
+
		}
+
	}
+

+
	sbuf_delete(reason);
+
}
+

static int
jobs_solve_deinstall(struct pkg_jobs *j)
{
@@ -1307,7 +1459,7 @@ jobs_solve_deinstall(struct pkg_jobs *j)
		pkgdb_it_free(it);
	}

-
	j->solved = true;
+
	j->solved = 1;

	return( EPKG_OK);
}
@@ -1365,6 +1517,7 @@ jobs_solve_upgrade(struct pkg_jobs *j)
	struct pkg *pkg = NULL;
	struct pkgdb_it *it;
	char *origin;
+
	bool automatic;
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;

@@ -1381,9 +1534,9 @@ jobs_solve_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_get(pkg, PKG_ORIGIN, &origin);
+
			pkg_get(pkg, PKG_ORIGIN, &origin, PKG_AUTOMATIC, &automatic);
			/* Do not test we ignore what doesn't exists remotely */
-
			find_remote_pkg(j, origin, MATCH_EXACT, false, true);
+
			find_remote_pkg(j, origin, MATCH_EXACT, false, true, !automatic);
			pkg = NULL;
		}
		pkgdb_it_free(it);
@@ -1415,13 +1568,8 @@ static int
jobs_solve_install(struct pkg_jobs *j)
{
	struct job_pattern *jp, *jtmp;
-
	struct pkg *pkg;
-
	struct pkgdb_it *it;
	struct pkg_job_request *req, *rtmp;
-
	const char *origin;
	bool got_local;
-
	int flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
-
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;

	if ((j->flags & PKG_FLAG_PKG_VERSION_TEST) == PKG_FLAG_PKG_VERSION_TEST) {
		if (new_pkg_version(j)) {
@@ -1490,7 +1638,7 @@ jobs_solve_fetch(struct pkg_jobs *j)
				pkg_get(pkg, PKG_ORIGIN, &origin);
				/* Do not test we ignore what doesn't exists remotely */
				find_remote_pkg(j, origin, MATCH_EXACT, false,
-
						j->flags & PKG_FLAG_RECURSIVE);
+
						j->flags & PKG_FLAG_RECURSIVE, true);
			}
			pkg = NULL;
		}
@@ -1499,8 +1647,9 @@ jobs_solve_fetch(struct pkg_jobs *j)
		HASH_ITER(hh, j->patterns, jp, jtmp) {
			/* TODO: use repository priority here */
			if (find_remote_pkg(j, jp->pattern, jp->match, true,
-
					j->flags & PKG_FLAG_RECURSIVE) == EPKG_FATAL)
-
				pkg_emit_error("No packages matching '%s' has been found in the repositories", jp->pattern);
+
					j->flags & PKG_FLAG_RECURSIVE, true) == EPKG_FATAL)
+
				pkg_emit_error("No packages matching '%s' has been found in the "
+
						"repositories", jp->pattern);
		}
	}

@@ -1590,6 +1739,9 @@ pkg_jobs_solve(struct pkg_jobs *j)
		}
	}

+
	if (j->type == PKG_JOBS_DEINSTALL && j->solved)
+
		pkg_jobs_set_deinstall_reasons(j);
+

	return (ret);
}

@@ -1628,7 +1780,6 @@ pkg_jobs_handle_install(struct pkg_solved *ps, struct pkg_jobs *j, bool handle_r
	bool automatic = false;
	int flags = 0;
	int retcode = EPKG_FATAL;
-
	struct job_pattern *jp;

	old = ps->items[1] ? ps->items[1]->pkg : NULL;
	new = ps->items[0]->pkg;
@@ -1829,7 +1980,7 @@ pkg_jobs_apply(struct pkg_jobs *j)
					rc = pkg_jobs_check_conflicts(j);
					if (rc == EPKG_CONFLICT) {
						/* Cleanup results */
-
						LL_FREE(j->jobs, pkg_solved, free);
+
						LL_FREE(j->jobs, free);
						j->jobs = NULL;
						j->count = 0;
						has_conflicts = true;
modified libpkg/pkg_manifest.c
@@ -164,7 +164,7 @@ pkg_manifest_keys_new(struct pkg_manifest_key **key)

static void
pmk_free(struct pkg_manifest_key *key) {
-
	HASH_FREE(key->parser, dataparser, free);
+
	HASH_FREE(key->parser, free);

	free(key);
}
@@ -175,7 +175,7 @@ pkg_manifest_keys_free(struct pkg_manifest_key *key)
	if (key == NULL)
		return;

-
	HASH_FREE(key, pkg_manifest_key, pmk_free);
+
	HASH_FREE(key, pmk_free);
}

static int
@@ -845,7 +845,6 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
	struct pkg_user		*user     = NULL;
	struct pkg_group	*group    = NULL;
	struct pkg_shlib	*shlib    = NULL;
-
	struct pkg_note		*note     = NULL;
	struct pkg_conflict	*conflict = NULL;
	struct pkg_provide	*provide  = NULL;
	struct sbuf		*tmpsbuf  = NULL;
modified libpkg/pkg_ports.c
@@ -642,7 +642,7 @@ populate_keywords(struct plist *p)
static void
keyword_free(struct keyword *k)
{
-
	LL_FREE(k->actions, action, free);
+
	LL_FREE(k->actions, free);

	free(k);
}
@@ -1016,9 +1016,9 @@ ports_parse_plist(struct pkg *pkg, const char *plist, const char *stage)
	flush_script_buffer(pplist.post_upgrade_buf, pkg,
	    PKG_SCRIPT_POST_UPGRADE);

-
	HASH_FREE(pplist.hardlinks, hardlinks, free);
+
	HASH_FREE(pplist.hardlinks, free);

-
	HASH_FREE(pplist.keywords, keyword, keyword_free);
+
	HASH_FREE(pplist.keywords, keyword_free);

	if (pplist.pkgdep != NULL)
		free(pplist.pkgdep);
modified libpkg/pkg_solve.c
@@ -35,6 +35,7 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+
#include <math.h>

#include "pkg.h"
#include "private/event.h"
@@ -46,7 +47,7 @@ struct pkg_solve_item;
struct pkg_solve_variable {
	struct pkg_job_universe_item *unit;
	bool to_install;
-
	bool guess;
+
	int guess;
	int priority;
	const char *digest;
	const char *origin;
@@ -90,27 +91,6 @@ struct pkg_solve_problem {
	((item)->var->to_install ^ (item)->inverse)

/**
-
 * Check whether SAT rule is TRUE
-
 * @param rules list of rules
-
 * @return true or false
-
 */
-
static bool
-
pkg_solve_check_rules(struct pkg_solve_problem *problem)
-
{
-
	struct pkg_solve_variable *var, *tvar;
-

-
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
-
		if (!var->resolved) {
-
			pkg_debug(2, "solver: var %s-%s is not still resolved",
-
					var->origin, var->digest);
-
			return false;
-
		}
-
	}
-

-
	return (true);
-
}
-

-
/**
 * Updates rules related to a single variable
 * @param var
 */
@@ -124,6 +104,27 @@ pkg_solve_update_var_resolved (struct pkg_solve_variable *var)
	}
}

+
static void
+
pkg_debug_print_rule (struct pkg_solve_item *rule)
+
{
+
	struct pkg_solve_item *it;
+
	struct sbuf *sb;
+

+
	sb = sbuf_new_auto();
+

+
	sbuf_printf(sb, "%s", "rule: (");
+

+
	LL_FOREACH(rule, it) {
+
		sbuf_printf(sb, "%s%s%s%s", it->inverse ? "!" : "",
+
				it->var->origin,
+
				(it->var->unit->pkg->type == PKG_INSTALLED) ? "(l)" : "(r)",
+
				it->next ? " | " : ")");
+
	}
+
	sbuf_finish(sb);
+
	pkg_debug(2, "%s", sbuf_data(sb));
+
	sbuf_delete(sb);
+
}
+

/**
 * Propagate all units, must be called recursively
 * @param rules
@@ -165,6 +166,7 @@ check_again:
						}
						sbuf_finish(err_msg);
						pkg_emit_error("%splease resolve it manually", sbuf_data(err_msg));
+
						pkg_debug_print_rule(unresolved);
						sbuf_delete(err_msg);
						return (false);
					}
@@ -192,6 +194,7 @@ check_again:
										it->var->origin, it->var->digest,
										it->var->priority,
										it->var->to_install ? "install" : "delete");
+
								pkg_debug_print_rule(unresolved);
								break;
							}
						}
@@ -252,31 +255,38 @@ pkg_solve_propagate_pure(struct pkg_solve_problem *problem)
 * @return true if guess is accepted
 */
bool
-
pkg_solve_test_guess(struct pkg_solve_problem *problem)
+
pkg_solve_test_guess(struct pkg_solve_problem *problem, struct pkg_solve_variable *var)
{
	bool test = false;
-
	struct pkg_solve_variable *var, *tvar;
	struct _pkg_solve_var_rule *rul;
	struct pkg_solve_item *it, *cur;

-
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
-
		LL_FOREACH(var->rules, rul) {
-
			it = rul->rule;
-
			if (it->nitems != it->nresolved) {
-
				/* Check guess */
-
				test = false;
-
				LL_FOREACH(it, cur) {
-
					if (cur->var->resolved)
-
						test |= cur->var->to_install ^ cur->inverse;
-
					else
-
						test |= cur->var->guess ^ cur->inverse;
+
	LL_FOREACH(var->rules, rul) {
+
		it = rul->rule;
+
		if (it->nitems != it->nresolved) {
+
			/* Check guess */
+
			test = false;
+
			LL_FOREACH(it, cur) {
+
				if (cur->var->resolved)
+
					test |= cur->var->to_install ^ cur->inverse;
+
				else if (cur->var->guess != -1)
+
					test |= cur->var->guess ^ cur->inverse;
+
				else {
+
					/* Free variables are assumed as true */
+
					test = true;
+
					break;
				}
-
				if (!test)
-
					return (false);
+
			}
+
			if (!test) {
+
				pkg_debug(2, "solver: guess test failed at variable %s, trying to %d",
+
						var->origin, var->guess);
+
				pkg_debug_print_rule(it);
+
				return (false);
			}
		}
	}

+

	return (true);
}

@@ -290,9 +300,17 @@ pkg_solve_test_guess(struct pkg_solve_problem *problem)
bool
pkg_solve_sat_problem(struct pkg_solve_problem *problem)
{
-
	int propagated, iters = 0;
-
	bool guessed = false;
+
	int propagated;
	struct pkg_solve_variable *var, *tvar;
+
	int64_t unresolved = 0, iters = 0;
+
	bool rc;
+

+
	struct _solver_tree_elt {
+
		struct pkg_solve_variable *var;
+
		int guess;
+
		struct _solver_tree_elt *prev, *next;
+
	} *solver_tree = NULL, *elt;
+


	/* Obvious case */
	if (problem->rules_count == 0)
@@ -306,28 +324,67 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
	}

	/* Set initial guess */
+
	elt = solver_tree;
+

+
	/* DPLL Algorithm */
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
		if (!var->resolved) {
-
			/* Guess true for installed packages and false otherwise */
-
			var->guess = (var->unit->pkg->type == PKG_INSTALLED) ? true : false;
-
		}
-
	}

-
	while (!guessed) {
-
		HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
-
			if (pkg_solve_test_guess(problem)) {
-
				guessed = true;
-
				break;
+
			if (elt == NULL) {
+
				/* Add new element to the backtracking queue */
+
				elt = malloc (sizeof (*elt));
+
				if (elt == NULL) {
+
					pkg_emit_errno("malloc", "_solver_tree_elt");
+
					LL_FREE(solver_tree, free);
+
					return (false);
+
				}
+
				elt->var = var;
+
				elt->guess = -1;
+
				DL_APPEND(solver_tree, elt);
			}
-
			else {
-
				var->guess = !var->guess;
-
				if (pkg_solve_test_guess(problem)) {
-
					guessed = true;
-
					break;
+

+
			if (elt->guess == -1)
+
				/* Guess true for installed packages and false otherwise */
+
				var->guess = (var->unit->pkg->type == PKG_INSTALLED) ? true : false;
+
			else
+
				/* For analyzed variables we can only inverse previous guess */
+
				var->guess = !elt->guess;
+

+
			unresolved ++;
+
			if (!pkg_solve_test_guess(problem, var)) {
+
				if (elt->guess == -1) {
+
					/* This is free variable, so we can assign true or false to it */
+
					var->guess = !var->guess;
+
					rc = pkg_solve_test_guess(problem, var);
+
				}
+
				else {
+
					rc = false;
+
				}
+
				if (!rc) {
+
					/* Need to backtrack */
+
					iters ++;
+
					if (elt == NULL || elt->prev->next == NULL) {
+
						/* Cannot backtrack, UNSAT */
+
						pkg_debug(1, "problem is UNSAT problem after %d guesses", iters);
+
						LL_FREE(solver_tree, free);
+
						return (false);
+
					}
+
					/* Set the current variable as free variable */
+
					elt->guess = -1;
+
					var->guess = -1;
+
					/* Go to the previous level */
+
					elt = elt->prev;
+
					tvar = var;
+
					var = elt->var;
+
					continue;
				}
			}
+

+
			/* Assign the current guess */
+
			elt->guess = var->guess;
+
			/* Move to the next elt */
+
			elt = elt->next;
		}
-
		iters ++;
	}

	pkg_debug(1, "solved SAT problem in %d guesses", iters);
@@ -339,6 +396,8 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
		}
	}

+
	LL_FREE(solver_tree, free);
+

	return (true);
}

@@ -396,6 +455,7 @@ pkg_solve_variable_new(struct pkg_job_universe_item *item)
	pkg_get(item->pkg, PKG_ORIGIN, &origin, PKG_DIGEST, &digest);
	/* XXX: Is it safe to save a ptr here ? */
	result->digest = digest;
+
	result->guess = -1;
	result->origin = origin;
	result->prev = result;

@@ -514,14 +574,16 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
		struct pkg_solve_variable *pvar, bool conflicting)
{
	struct pkg_dep *dep, *dtmp;
-
	struct pkg_conflict *conflict, *ctmp, *cfound;
+
	struct pkg_conflict *conflict, *ctmp;
	struct pkg *pkg;
	struct pkg_solve_rule *rule;
	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;

-
	const char *origin;
+
	const char *origin, *digest;

	/* Go through all deps in all variables*/
	LL_FOREACH(pvar, cur_var) {
@@ -582,11 +644,25 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
			pkg_get(pkg, PKG_ORIGIN, &origin);
			/* Add conflict rule from each of the alternative */
			LL_FOREACH(var, tvar) {
-
				HASH_FIND_STR(tvar->unit->pkg->conflicts, origin, cfound);
-
				if (cfound == NULL) {
-
					/* Skip non-mutual conflicts */
-
					continue;
+
				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;
+

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

				/* Conflict rule: (!A | !Bx) */
				rule = pkg_solve_rule_new();
				if (rule == NULL)
@@ -615,6 +691,72 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
			}
		}

+
		/* Check required 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) {
+
						/* For each provide */
+
						pkg_get(pr->un->pkg, PKG_DIGEST, &digest);
+
						HASH_FIND(hd, problem->variables_by_digest, digest,
+
								strlen(digest), var);
+
						if (var == NULL) {
+
							continue;
+
						}
+
						/* XXX: select all its versions? */
+

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

+
						it->inverse = false;
+
						RULE_ITEM_PREPEND(rule, it);
+
						cnt ++;
+
					}
+

+
					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 (conflicting) {
			/*
			 * If this var chain contains mutually conflicting vars
@@ -841,7 +983,7 @@ pkg_solve_dimacs_export(struct pkg_solve_problem *problem, FILE *f)
		fprintf(f, "0\n");
	}

-
	HASH_FREE(ordered_variables, pkg_solve_ordered_variable, free);
+
	HASH_FREE(ordered_variables, free);

	return (EPKG_OK);
}
@@ -850,7 +992,7 @@ static void
pkg_solve_insert_res_job (struct pkg_solve_variable *var,
		struct pkg_solve_problem *problem, struct pkg_jobs *j)
{
-
	struct pkg_solved *res, *dres;
+
	struct pkg_solved *res;
	struct pkg_solve_variable *cur_var, *del_var = NULL, *add_var = NULL;
	int seen_add = 0, seen_del = 0;

@@ -884,7 +1026,8 @@ pkg_solve_insert_res_job (struct pkg_solve_variable *var,
		}
		else if (seen_del == 0 && seen_add != 0) {
			res->items[0] = add_var->unit;
-
			res->type = PKG_SOLVED_INSTALL;
+
			res->type = (j->type == PKG_JOBS_FETCH) ?
+
					PKG_SOLVED_FETCH : PKG_SOLVED_INSTALL;
			DL_APPEND(j->jobs, res);
			pkg_debug(3, "pkg_solve: schedule installation of %s %s",
					add_var->origin, add_var->digest);
@@ -999,7 +1142,7 @@ pkg_solve_parse_sat_output(FILE *f, struct pkg_solve_problem *problem, struct pk
		ret = EPKG_FATAL;
	}

-
	HASH_FREE(ordered_variables, pkg_solve_ordered_variable, free);
+
	HASH_FREE(ordered_variables, free);
	if (line != NULL)
		free(line);
	return (ret);
modified libpkg/pkgdb.c
@@ -81,7 +81,6 @@ static void pkgdb_regex_delete(void *);
static int pkgdb_upgrade(struct pkgdb *);
static void populate_pkg(sqlite3_stmt *stmt, struct pkg *pkg);
static void pkgdb_detach_remotes(sqlite3 *);
-
static bool is_attached(sqlite3 *, const char *);
static int sqlcmd_init(sqlite3 *db, __unused const char **err,
    __unused const void *noused);
static int prstmt_initialize(struct pkgdb *db);
@@ -202,30 +201,6 @@ load_tag_val(sqlite3 *db, struct pkg *pkg, const char *sql, unsigned flags,
	return (EPKG_OK);
}

-
static const char *
-
pkgdb_get_reponame(struct pkgdb *db, const char *repo)
-
{
-
	const char	*reponame = NULL;
-
	struct pkg_repo	*r;
-

-
	assert(db->type == PKGDB_REMOTE);
-

-
	if (repo != NULL) {
-
		if ((r = pkg_repo_find_ident(repo)) == NULL) {
-
			pkg_emit_error("repository '%s' does not exist", repo);
-
			return (NULL);
-
		}
-
		reponame = pkg_repo_name(r);
-

-
		if (!is_attached(db->sqlite, reponame)) {
-
			pkg_emit_error("repository '%s' does not exist", repo);
-
			return (NULL);
-
		}
-
	}
-

-
	return (reponame);
-
}
-

static int
compare_column_func(const void *pkey, const void *pcolumn)
{
@@ -816,7 +791,7 @@ pkgdb_open_multirepos(const char *dbdir, struct pkgdb *db)
			continue;

		/* is it already attached? */
-
		if (is_attached(db->sqlite, pkg_repo_name(r))) {
+
		if (pkgdb_is_attached(db->sqlite, pkg_repo_name(r))) {
			pkg_emit_error("repository '%s' is already "
			    "listed, ignoring", pkg_repo_ident(r));
			continue;
@@ -1470,7 +1445,7 @@ pkgdb_case_sensitive(void)
	return (_case_sensitive_flag);
}

-
static const char *
+
const char *
pkgdb_get_pattern_query(const char *pattern, match_t match)
{
	char		*checkorigin = NULL;
@@ -3317,8 +3292,8 @@ sql_exec(sqlite3 *s, const char *sql, ...)
	return (ret);
}

-
static bool
-
is_attached(sqlite3 *s, const char *name)
+
bool
+
pkgdb_is_attached(sqlite3 *s, const char *name)
{
	sqlite3_stmt	*stmt;
	const char	*dbname;
@@ -3345,8 +3320,8 @@ is_attached(sqlite3 *s, const char *name)
	return (false);
}

-
static int
-
sql_on_all_attached_db(sqlite3 *s, struct sbuf *sql, const char *multireposql,
+
int
+
pkgdb_sql_all_attached(sqlite3 *s, struct sbuf *sql, const char *multireposql,
    const char *compound)
{
	sqlite3_stmt	*stmt;
@@ -3523,128 +3498,6 @@ pkgdb_compact(struct pkgdb *db)
	return (sql_exec(db->sqlite, "VACUUM;"));
}

-
struct pkgdb_it *
-
pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match,
-
    const char *repo)
-
{
-
	sqlite3_stmt	*stmt = NULL;
-
	struct sbuf	*sql = NULL;
-
	const char	*reponame = NULL;
-
	const char	*comp = NULL;
-
	int		 ret;
-
	char		 basesql[BUFSIZ] = ""
-
		"SELECT id, origin, name, version, comment, "
-
		"prefix, desc, arch, maintainer, www, "
-
		"licenselogic, flatsize, pkgsize, "
-
		"cksum, manifestdigest, path AS repopath, '%1$s' AS dbname "
-
		"FROM '%1$s'.packages p";
-

-
	assert(db != NULL);
-
	assert(match == MATCH_ALL || (pattern != NULL && pattern[0] != '\0'));
-

-
	/*
-
	 * If we have no remote repos loaded, we just return nothing instead of failing
-
	 * an assert deep inside pkgdb_get_reponame
-
	 */
-
	if (db->type != PKGDB_REMOTE)
-
		return (NULL);
-

-
	reponame = pkgdb_get_reponame(db, repo);
-

-
	sql = sbuf_new_auto();
-
	comp = pkgdb_get_pattern_query(pattern, match);
-
	if (comp && comp[0])
-
		strlcat(basesql, comp, sizeof(basesql));
-

-
	/*
-
	 * Working on multiple remote repositories
-
	 */
-
	if (reponame == NULL) {
-
		/* duplicate the query via UNION for all the attached
-
		 * databases */
-

-
		ret = sql_on_all_attached_db(db->sqlite, sql,
-
		    basesql, " UNION ALL ");
-
		if (ret != EPKG_OK) {
-
			sbuf_delete(sql);
-
			return (NULL);
-
		}
-
	} else
-
		sbuf_printf(sql, basesql, reponame, reponame);
-

-
	sbuf_cat(sql, " ORDER BY name;");
-
	sbuf_finish(sql);
-

-
	pkg_debug(4, "Pkgdb: running '%s'", sbuf_get(sql));
-
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), sbuf_size(sql), &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite);
-
		sbuf_delete(sql);
-
		return (NULL);
-
	}
-

-
	sbuf_delete(sql);
-

-
	if (match != MATCH_ALL && match != MATCH_CONDITION)
-
		sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
-

-
	return (pkgdb_it_new(db, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE));
-
}
-

-
struct pkgdb_it *
-
pkgdb_query_provide(struct pkgdb *db, const char *provide, const char *repo)
-
{
-
	sqlite3_stmt	*stmt;
-
	struct sbuf	*sql = NULL;
-
	const char	*reponame = NULL;
-
	int		 ret;
-
	const char	 basesql[] = ""
-
			"SELECT p.id, p.origin, p.name, p.version, p.comment, p.desc, "
-
			"p.message, p.arch, p.maintainer, p.www, "
-
			"p.flatsize "
-
			"FROM '%1$s'.packages AS p, '%1$s'.pkg_provides AS pp, "
-
			"'%1$s'.provides AS pr "
-
			"WHERE p.id = pp.package_id "
-
			"AND pp.provide_id = pr.id "
-
			"AND pr.name = ?1;";
-

-
	assert(db != NULL);
-
	reponame = pkgdb_get_reponame(db, repo);
-

-
	sql = sbuf_new_auto();
-
	/*
-
	 * Working on multiple remote repositories
-
	 */
-
	if (reponame == NULL) {
-
		/* duplicate the query via UNION for all the attached
-
		 * databases */
-

-
		ret = sql_on_all_attached_db(db->sqlite, sql,
-
				basesql, " UNION ALL ");
-
		if (ret != EPKG_OK) {
-
			sbuf_delete(sql);
-
			return (NULL);
-
		}
-
	} else
-
		sbuf_printf(sql, basesql, reponame);
-

-
	sbuf_finish(sql);
-

-
	pkg_debug(4, "Pkgdb: running '%s'", sbuf_get(sql));
-
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite);
-
		sbuf_delete(sql);
-
		return (NULL);
-
	}
-

-
	sbuf_delete(sql);
-

-
	sqlite3_bind_text(stmt, 1, provide, -1, SQLITE_TRANSIENT);
-

-
	return (pkgdb_it_new(db, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE));
-
}
-

static int
pkgdb_search_build_search_query(struct sbuf *sql, match_t match,
    pkgdb_field field, pkgdb_field sort)
@@ -3752,7 +3605,7 @@ pkgdb_search(struct pkgdb *db, const char *pattern, match_t match,
			return (NULL);
		}
		/* test on all the attached databases */
-
		if (sql_on_all_attached_db(db->sqlite, sql,
+
		if (pkgdb_sql_all_attached(db->sqlite, sql,
		    multireposql, " UNION ALL ") != EPKG_OK) {
			sbuf_delete(sql);
			return (NULL);
@@ -3973,7 +3826,7 @@ pkgdb_integrity_check(struct pkgdb *db, conflict_func_cb cb, void *cbdata)
	sbuf_delete(conflictmsg);
	sbuf_delete(origin);

-
	sql_exec(db->sqlite, "DROP TABLE IF EXISTS integritycheck");
+
	assert (sql_exec(db->sqlite, "DROP TABLE IF EXISTS integritycheck") == EPKG_OK);

	return (retcode);
}
@@ -4474,7 +4327,7 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)
		sbuf_printf(sql, "(");

		/* execute on all databases */
-
		sql_on_all_attached_db(db->sqlite, sql,
+
		pkgdb_sql_all_attached(db->sqlite, sql,
		    "SELECT origin AS c FROM '%1$s'.packages", " UNION ");

		/* close parentheses for the compound statement */
@@ -4487,7 +4340,7 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)
		sbuf_printf(sql, "(");

		/* execute on all databases */
-
		sql_on_all_attached_db(db->sqlite, sql,
+
		pkgdb_sql_all_attached(db->sqlite, sql,
		    "SELECT origin AS c FROM '%1$s'.packages", " UNION ALL ");

		/* close parentheses for the compound statement */
@@ -4500,7 +4353,7 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)
		sbuf_printf(sql, "(");

		/* execute on all databases */
-
		sql_on_all_attached_db(db->sqlite, sql,
+
		pkgdb_sql_all_attached(db->sqlite, sql,
		    "SELECT flatsize AS s FROM '%1$s'.packages", " UNION ALL ");

		/* close parentheses for the compound statement */
@@ -4513,7 +4366,7 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)
		sbuf_printf(sql, "(");

		/* execute on all databases */
-
		sql_on_all_attached_db(db->sqlite, sql,
+
		pkgdb_sql_all_attached(db->sqlite, sql,
		    "SELECT '%1$s' AS c", " UNION ALL ");

		/* close parentheses for the compound statement */
modified libpkg/pkgdb_repo.c
@@ -1001,3 +1001,256 @@ pkgdb_repo_register_conflicts(const char *origin, char **conflicts,

	return (EPKG_OK);
}
+

+
const char *
+
pkgdb_get_reponame(struct pkgdb *db, const char *repo)
+
{
+
	const char	*reponame = NULL;
+
	struct pkg_repo	*r;
+

+
	assert(db->type == PKGDB_REMOTE);
+

+
	if (repo != NULL) {
+
		if ((r = pkg_repo_find_ident(repo)) == NULL) {
+
			pkg_emit_error("repository '%s' does not exist", repo);
+
			return (NULL);
+
		}
+
		reponame = pkg_repo_name(r);
+

+
		if (!pkgdb_is_attached(db->sqlite, reponame)) {
+
			pkg_emit_error("repository '%s' does not exist", repo);
+
			return (NULL);
+
		}
+
	}
+

+
	return (reponame);
+
}
+

+
struct pkgdb_it *
+
pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match,
+
    const char *repo)
+
{
+
	sqlite3_stmt	*stmt = NULL;
+
	struct sbuf	*sql = NULL;
+
	const char	*reponame = NULL;
+
	const char	*comp = NULL;
+
	int		 ret;
+
	char		 basesql[BUFSIZ] = ""
+
		"SELECT id, origin, name, version, comment, "
+
		"prefix, desc, arch, maintainer, www, "
+
		"licenselogic, flatsize, pkgsize, "
+
		"cksum, manifestdigest, path AS repopath, '%1$s' AS dbname "
+
		"FROM '%1$s'.packages p";
+

+
	assert(db != NULL);
+
	assert(match == MATCH_ALL || (pattern != NULL && pattern[0] != '\0'));
+

+
	/*
+
	 * If we have no remote repos loaded, we just return nothing instead of failing
+
	 * an assert deep inside pkgdb_get_reponame
+
	 */
+
	if (db->type != PKGDB_REMOTE)
+
		return (NULL);
+

+
	reponame = pkgdb_get_reponame(db, repo);
+

+
	sql = sbuf_new_auto();
+
	comp = pkgdb_get_pattern_query(pattern, match);
+
	if (comp && comp[0])
+
		strlcat(basesql, comp, sizeof(basesql));
+

+
	/*
+
	 * Working on multiple remote repositories
+
	 */
+
	if (reponame == NULL) {
+
		/* duplicate the query via UNION for all the attached
+
		 * databases */
+

+
		ret = pkgdb_sql_all_attached(db->sqlite, sql,
+
		    basesql, " UNION ALL ");
+
		if (ret != EPKG_OK) {
+
			sbuf_delete(sql);
+
			return (NULL);
+
		}
+
	} else
+
		sbuf_printf(sql, basesql, reponame, reponame);
+

+
	sbuf_cat(sql, " ORDER BY name;");
+
	sbuf_finish(sql);
+

+
	pkg_debug(4, "Pkgdb: running '%s'", sbuf_get(sql));
+
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), sbuf_size(sql), &stmt, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		sbuf_delete(sql);
+
		return (NULL);
+
	}
+

+
	sbuf_delete(sql);
+

+
	if (match != MATCH_ALL && match != MATCH_CONDITION)
+
		sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
+

+
	return (pkgdb_it_new(db, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE));
+
}
+

+
struct pkgdb_it *
+
pkgdb_rquery_provide(struct pkgdb *db, const char *provide, const char *repo)
+
{
+
	sqlite3_stmt	*stmt;
+
	struct sbuf	*sql = NULL;
+
	const char	*reponame = NULL;
+
	int		 ret;
+
	const char	 basesql[] = ""
+
			"SELECT p.id, p.origin, p.name, p.version, p.comment, "
+
			"p.prefix, p.desc, p.arch, p.maintainer, p.www, "
+
			"p.licenselogic, p.flatsize, p.pkgsize, "
+
			"p.cksum, p.manifestdigest, p.path AS repopath, '%1$s' AS dbname "
+
			"FROM '%1$s'.packages AS p, '%1$s'.pkg_provides AS pp, "
+
			"'%1$s'.provides AS pr "
+
			"WHERE p.id = pp.package_id "
+
			"AND pp.provide_id = pr.id "
+
			"AND pr.name = ?1;";
+

+
	assert(db != NULL);
+
	reponame = pkgdb_get_reponame(db, repo);
+

+
	sql = sbuf_new_auto();
+
	/*
+
	 * Working on multiple remote repositories
+
	 */
+
	if (reponame == NULL) {
+
		/* duplicate the query via UNION for all the attached
+
		 * databases */
+

+
		ret = pkgdb_sql_all_attached(db->sqlite, sql,
+
				basesql, " UNION ALL ");
+
		if (ret != EPKG_OK) {
+
			sbuf_delete(sql);
+
			return (NULL);
+
		}
+
	} else
+
		sbuf_printf(sql, basesql, reponame);
+

+
	sbuf_finish(sql);
+

+
	pkg_debug(4, "Pkgdb: running '%s'", sbuf_get(sql));
+
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		sbuf_delete(sql);
+
		return (NULL);
+
	}
+

+
	sbuf_delete(sql);
+

+
	sqlite3_bind_text(stmt, 1, provide, -1, SQLITE_TRANSIENT);
+

+
	return (pkgdb_it_new(db, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE));
+
}
+

+
struct pkgdb_it *
+
pkgdb_find_shlib_provide(struct pkgdb *db, const char *require, const char *repo)
+
{
+
	sqlite3_stmt	*stmt;
+
	struct sbuf	*sql = NULL;
+
	const char	*reponame = NULL;
+
	int		 ret;
+
	const char	 basesql[] = ""
+
			"SELECT p.id, p.origin, p.name, p.version, p.comment, "
+
			"p.prefix, p.desc, p.arch, p.maintainer, p.www, "
+
			"p.licenselogic, p.flatsize, p.pkgsize, "
+
			"p.cksum, p.manifestdigest, p.path AS repopath, '%1$s' AS dbname "
+
			"FROM '%1$s'.packages AS p INNER JOIN '%1$s'.pkg_shlibs_provided AS ps ON "
+
			"p.id = ps.package_id "
+
			"WHERE ps.shlib_id = (SELECT id FROM '%1$s'.shlibs WHERE name=?1);";
+

+
	assert(db != NULL);
+
	reponame = pkgdb_get_reponame(db, repo);
+

+
	sql = sbuf_new_auto();
+
	/*
+
	 * Working on multiple remote repositories
+
	 */
+
	if (reponame == NULL) {
+
		/* duplicate the query via UNION for all the attached
+
		 * databases */
+

+
		ret = pkgdb_sql_all_attached(db->sqlite, sql,
+
				basesql, " UNION ALL ");
+
		if (ret != EPKG_OK) {
+
			sbuf_delete(sql);
+
			return (NULL);
+
		}
+
	} else
+
		sbuf_printf(sql, basesql, reponame);
+

+
	sbuf_finish(sql);
+

+
	pkg_debug(4, "Pkgdb: running '%s'", sbuf_get(sql));
+
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		sbuf_delete(sql);
+
		return (NULL);
+
	}
+

+
	sbuf_delete(sql);
+

+
	sqlite3_bind_text(stmt, 1, require, -1, SQLITE_TRANSIENT);
+

+
	return (pkgdb_it_new(db, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE));
+
}
+

+
struct pkgdb_it *
+
pkgdb_find_shlib_require(struct pkgdb *db, const char *provide, const char *repo)
+
{
+
	sqlite3_stmt	*stmt;
+
	struct sbuf	*sql = NULL;
+
	const char	*reponame = NULL;
+
	int		 ret;
+
	const char	 basesql[] = ""
+
			"SELECT p.id, p.origin, p.name, p.version, p.comment, "
+
			"p.prefix, p.desc, p.arch, p.maintainer, p.www, "
+
			"p.licenselogic, p.flatsize, p.pkgsize, "
+
			"p.cksum, p.manifestdigest, p.path AS repopath, '%1$s' AS dbname "
+
			"FROM '%1$s'.packages AS p INNER JOIN '%1$s'.pkg_shlibs_required AS ps ON "
+
			"p.id = ps.package_id "
+
			"WHERE ps.shlib_id = (SELECT id FROM '%1$s'.shlibs WHERE name=?1);";
+

+
	assert(db != NULL);
+
	reponame = pkgdb_get_reponame(db, repo);
+

+
	sql = sbuf_new_auto();
+
	/*
+
	 * Working on multiple remote repositories
+
	 */
+
	if (reponame == NULL) {
+
		/* duplicate the query via UNION for all the attached
+
		 * databases */
+

+
		ret = pkgdb_sql_all_attached(db->sqlite, sql,
+
				basesql, " UNION ALL ");
+
		if (ret != EPKG_OK) {
+
			sbuf_delete(sql);
+
			return (NULL);
+
		}
+
	} else
+
		sbuf_printf(sql, basesql, reponame);
+

+
	sbuf_finish(sql);
+

+
	pkg_debug(4, "Pkgdb: running '%s'", sbuf_get(sql));
+
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		sbuf_delete(sql);
+
		return (NULL);
+
	}
+

+
	sbuf_delete(sql);
+

+
	sqlite3_bind_text(stmt, 1, provide, -1, SQLITE_TRANSIENT);
+

+
	return (pkgdb_it_new(db, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE));
+
}
modified libpkg/plugins.c
@@ -78,7 +78,7 @@ pkg_plugin_hook_free(struct pkg_plugin *p)
{
	assert(p != NULL);

-
	HASH_FREE(p->hooks, plugin_hook, free);
+
	HASH_FREE(p->hooks, free);

	return (EPKG_OK);
}
@@ -98,7 +98,7 @@ plug_free(struct pkg_plugin *p)
static int
pkg_plugin_free(void)
{
-
	LL_FREE(plugins, pkg_plugin, plug_free);
+
	LL_FREE(plugins, plug_free);

	return (EPKG_OK);
}
modified libpkg/private/pkg.h
@@ -62,8 +62,8 @@
		ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | \
		ARCHIVE_EXTRACT_FFLAGS|ARCHIVE_EXTRACT_XATTR)

-
#define HASH_FREE(data, type, free_func) do {      \
-
	struct type *hf1, *hf2;                    \
+
#define HASH_FREE(data, free_func) do {      \
+
	__typeof(data) hf1, hf2;                    \
	HASH_ITER(hh, data, hf1, hf2) {            \
		HASH_DEL(data, hf1);               \
		free_func(hf1);                    \
@@ -71,8 +71,8 @@
	data = NULL;                               \
} while (0)

-
#define LL_FREE(head, type, free_func) do {   \
-
	struct type *l1, *l2;                 \
+
#define LL_FREE(head, free_func) do {   \
+
	__typeof(head) l1, l2;                 \
	LL_FOREACH_SAFE(head, l1, l2) {       \
		LL_DELETE(head, l1);          \
		free_func(l1);                \
@@ -148,8 +148,16 @@ struct pkg_dep {
	UT_hash_handle	 hh;
};

+
enum pkg_conflict_type {
+
	PKG_CONFLICT_ALL = 0,
+
	PKG_CONFLICT_REMOTE_LOCAL,
+
	PKG_CONFLICT_REMOTE_REMOTE,
+
	PKG_CONFLICT_LOCAL_LOCAL
+
};
+

struct pkg_conflict {
	struct sbuf		*origin;
+
	enum pkg_conflict_type type;
	UT_hash_handle	hh;
};

@@ -225,6 +233,13 @@ struct pkg_job_seen {
	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_jobs {
	struct pkg_job_universe_item *universe;
	struct pkg_job_request	*request_add;
@@ -232,6 +247,7 @@ struct pkg_jobs {
	struct pkg_solved *jobs;
	struct pkg_job_seen *seen;
	struct pkgdb	*db;
+
	struct pkg_job_provide *provides;
	pkg_jobs_t	 type;
	pkg_flags	 flags;
	int		 solved;
@@ -402,7 +418,8 @@ int pkgdb_is_dir_used(struct pkgdb *db, const char *dir, int64_t *res);
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);
+
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,
modified libpkg/private/pkgdb.h
@@ -145,4 +145,59 @@ struct pkgdb_it *pkgdb_repo_origins(sqlite3 *sqlite);
int pkgdb_repo_register_conflicts(const char *origin, char **conflicts,
		int conflicts_num, sqlite3 *sqlite);

+
/**
+
 * Execute SQL statement on all attached databases
+
 * @param s
+
 * @param sql
+
 * @param multireposql
+
 * @param compound
+
 * @return
+
 */
+
int
+
pkgdb_sql_all_attached(sqlite3 *s, struct sbuf *sql, const char *multireposql,
+
    const char *compound);
+

+
/**
+
 * Get repository name
+
 * @param db
+
 * @param repo
+
 * @return
+
 */
+
const char *pkgdb_get_reponame(struct pkgdb *db, const char *repo);
+

+
/**
+
 * Get query for the specified match type
+
 * @param pattern
+
 * @param match
+
 * @return
+
 */
+
const char * pkgdb_get_pattern_query(const char *pattern, match_t match);
+

+
/**
+
 * Returns whether the specified database is attached
+
 * @param s
+
 * @param name
+
 * @return
+
 */
+
bool pkgdb_is_attached(sqlite3 *s, const char *name);
+

+
/**
+
 * Find provides for a specified require in repos
+
 * @param db
+
 * @param provide
+
 * @param repo
+
 * @return
+
 */
+
struct pkgdb_it *pkgdb_find_shlib_require(struct pkgdb *db,
+
		const char *provide, const char *repo);
+
/**
+
 * Find requires for a specified provide in repos
+
 * @param db
+
 * @param require
+
 * @param repo
+
 * @return
+
 */
+
struct pkgdb_it *pkgdb_find_shlib_provide(struct pkgdb *db,
+
		const char *require, const char *repo);
+

#endif
modified libpkg/update.c
@@ -617,7 +617,7 @@ pkg_parse_conflicts_file(FILE *f, sqlite3 *sqlite)
static int
pkg_update_incremental(const char *name, struct pkg_repo *repo, time_t *mtime)
{
-
	FILE *fmanifest = NULL, *fdigests = NULL, *fconflicts = NULL;
+
	FILE *fmanifest = NULL, *fdigests = NULL /*, *fconflicts = NULL*/;
	sqlite3 *sqlite = NULL;
	struct pkg *pkg = NULL;
	int rc = EPKG_FATAL;
modified m4/.gitignore
@@ -2,3 +2,4 @@
*
# Except this file
!.gitignore
+
!ax_*.m4
added m4/ax_append_flag.m4
@@ -0,0 +1,70 @@
+
# ===========================================================================
+
#      http://www.gnu.org/software/autoconf-archive/ax_append_flag.html
+
# ===========================================================================
+
#
+
# SYNOPSIS
+
#
+
#   AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
+
#
+
# DESCRIPTION
+
#
+
#   FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
+
#   added in between.
+
#
+
#   If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
+
#   CFLAGS) is used.  FLAGS-VARIABLE is not changed if it already contains
+
#   FLAG.  If FLAGS-VARIABLE is unset in the shell, it is set to exactly
+
#   FLAG.
+
#
+
#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
+
#
+
# LICENSE
+
#
+
#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+
#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+
#
+
#   This program is free software: you can redistribute it and/or modify it
+
#   under the terms of the GNU General Public License as published by the
+
#   Free Software Foundation, either version 3 of the License, or (at your
+
#   option) any later version.
+
#
+
#   This program is distributed in the hope that it will be useful, but
+
#   WITHOUT ANY WARRANTY; without even the implied warranty of
+
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+
#   Public License for more details.
+
#
+
#   You should have received a copy of the GNU General Public License along
+
#   with this program. If not, see <http://www.gnu.org/licenses/>.
+
#
+
#   As a special exception, the respective Autoconf Macro's copyright owner
+
#   gives unlimited permission to copy, distribute and modify the configure
+
#   scripts that are the output of Autoconf when processing the Macro. You
+
#   need not follow the terms of the GNU General Public License when using
+
#   or distributing such scripts, even though portions of the text of the
+
#   Macro appear in them. The GNU General Public License (GPL) does govern
+
#   all other use of the material that constitutes the Autoconf Macro.
+
#
+
#   This special exception to the GPL applies to versions of the Autoconf
+
#   Macro released by the Autoconf Archive. When you make and distribute a
+
#   modified version of the Autoconf Macro, you may extend this special
+
#   exception to the GPL to apply to your modified version as well.
+

+
#serial 2
+

+
AC_DEFUN([AX_APPEND_FLAG],
+
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
+
AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])dnl
+
AS_VAR_SET_IF(FLAGS,
+
  [case " AS_VAR_GET(FLAGS) " in
+
    *" $1 "*)
+
      AC_RUN_LOG([: FLAGS already contains $1])
+
      ;;
+
    *)
+
      AC_RUN_LOG([: FLAGS="$FLAGS $1"])
+
      AS_VAR_SET(FLAGS, ["AS_VAR_GET(FLAGS) $1"])
+
      ;;
+
   esac],
+
  [AS_VAR_SET(FLAGS,["$1"])])
+
AS_VAR_POPDEF([FLAGS])dnl
+
])dnl AX_APPEND_FLAG
+

added m4/ax_cflags_warn_all.m4
@@ -0,0 +1,123 @@
+
# ===========================================================================
+
#    http://www.gnu.org/software/autoconf-archive/ax_cflags_warn_all.html
+
# ===========================================================================
+
#
+
# SYNOPSIS
+
#
+
#   AX_CFLAGS_WARN_ALL   [(shellvar [,default, [A/NA]])]
+
#   AX_CXXFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])]
+
#   AX_FCFLAGS_WARN_ALL  [(shellvar [,default, [A/NA]])]
+
#
+
# DESCRIPTION
+
#
+
#   Try to find a compiler option that enables most reasonable warnings.
+
#
+
#   For the GNU compiler it will be -Wall (and -ansi -pedantic) The result
+
#   is added to the shellvar being CFLAGS, CXXFLAGS, or FCFLAGS by default.
+
#
+
#   Currently this macro knows about the GCC, Solaris, Digital Unix, AIX,
+
#   HP-UX, IRIX, NEC SX-5 (Super-UX 10), Cray J90 (Unicos 10.0.0.8), and
+
#   Intel compilers.  For a given compiler, the Fortran flags are much more
+
#   experimental than their C equivalents.
+
#
+
#    - $1 shell-variable-to-add-to : CFLAGS, CXXFLAGS, or FCFLAGS
+
#    - $2 add-value-if-not-found : nothing
+
#    - $3 action-if-found : add value to shellvariable
+
#    - $4 action-if-not-found : nothing
+
#
+
#   NOTE: These macros depend on AX_APPEND_FLAG.
+
#
+
# LICENSE
+
#
+
#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+
#   Copyright (c) 2010 Rhys Ulerich <rhys.ulerich@gmail.com>
+
#
+
#   This program is free software; you can redistribute it and/or modify it
+
#   under the terms of the GNU General Public License as published by the
+
#   Free Software Foundation; either version 3 of the License, or (at your
+
#   option) any later version.
+
#
+
#   This program is distributed in the hope that it will be useful, but
+
#   WITHOUT ANY WARRANTY; without even the implied warranty of
+
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+
#   Public License for more details.
+
#
+
#   You should have received a copy of the GNU General Public License along
+
#   with this program. If not, see <http://www.gnu.org/licenses/>.
+
#
+
#   As a special exception, the respective Autoconf Macro's copyright owner
+
#   gives unlimited permission to copy, distribute and modify the configure
+
#   scripts that are the output of Autoconf when processing the Macro. You
+
#   need not follow the terms of the GNU General Public License when using
+
#   or distributing such scripts, even though portions of the text of the
+
#   Macro appear in them. The GNU General Public License (GPL) does govern
+
#   all other use of the material that constitutes the Autoconf Macro.
+
#
+
#   This special exception to the GPL applies to versions of the Autoconf
+
#   Macro released by the Autoconf Archive. When you make and distribute a
+
#   modified version of the Autoconf Macro, you may extend this special
+
#   exception to the GPL to apply to your modified version as well.
+

+
#serial 15
+

+
AC_DEFUN([AX_FLAGS_WARN_ALL],[dnl
+
AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl
+
AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl
+
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings],
+
VAR,[VAR="no, unknown"
+
ac_save_[]FLAGS="$[]FLAGS"
+
for ac_arg dnl
+
in "-warn all  % -warn all"   dnl Intel
+
   "-pedantic  % -Wall"       dnl GCC
+
   "-xstrconst % -v"          dnl Solaris C
+
   "-std1      % -verbose -w0 -warnprotos" dnl Digital Unix
+
   "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX
+
   "-ansi -ansiE % -fullwarn" dnl IRIX
+
   "+ESlit     % +w1"         dnl HP-UX C
+
   "-Xc        % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10)
+
   "-h conform % -h msglevel 2" dnl Cray C (Unicos)
+
   #
+
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+
   AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+
                     [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+
done
+
FLAGS="$ac_save_[]FLAGS"
+
])
+
AS_VAR_POPDEF([FLAGS])dnl
+
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+
case ".$VAR" in
+
     .ok|.ok,*) m4_ifvaln($3,$3) ;;
+
   .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;;
+
   *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;;
+
esac
+
AS_VAR_POPDEF([VAR])dnl
+
])dnl AX_FLAGS_WARN_ALL
+
dnl  implementation tactics:
+
dnl   the for-argument contains a list of options. The first part of
+
dnl   these does only exist to detect the compiler - usually it is
+
dnl   a global option to enable -ansi or -extrawarnings. All other
+
dnl   compilers will fail about it. That was needed since a lot of
+
dnl   compilers will give false positives for some option-syntax
+
dnl   like -Woption or -Xoption as they think of it is a pass-through
+
dnl   to later compile stages or something. The "%" is used as a
+
dnl   delimiter. A non-option comment can be given after "%%" marks
+
dnl   which will be shown but not added to the respective C/CXXFLAGS.
+

+
AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl
+
AC_LANG_PUSH([C])
+
AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
+
AC_LANG_POP([C])
+
])
+

+
AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl
+
AC_LANG_PUSH([C++])
+
AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
+
AC_LANG_POP([C++])
+
])
+

+
AC_DEFUN([AX_FCFLAGS_WARN_ALL],[dnl
+
AC_LANG_PUSH([Fortran])
+
AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
+
AC_LANG_POP([Fortran])
+
])
+

added m4/ax_require_defined.m4
@@ -0,0 +1,37 @@
+
# ===========================================================================
+
#    http://www.gnu.org/software/autoconf-archive/ax_require_defined.html
+
# ===========================================================================
+
#
+
# SYNOPSIS
+
#
+
#   AX_REQUIRE_DEFINED(MACRO)
+
#
+
# DESCRIPTION
+
#
+
#   AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
+
#   been defined and thus are available for use.  This avoids random issues
+
#   where a macro isn't expanded.  Instead the configure script emits a
+
#   non-fatal:
+
#
+
#     ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
+
#
+
#   It's like AC_REQUIRE except it doesn't expand the required macro.
+
#
+
#   Here's an example:
+
#
+
#     AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
+
#
+
# LICENSE
+
#
+
#   Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
+
#
+
#   Copying and distribution of this file, with or without modification, are
+
#   permitted in any medium without royalty provided the copyright notice
+
#   and this notice are preserved. This file is offered as-is, without any
+
#   warranty.
+

+
#serial 1
+

+
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
+
  m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
+
])dnl AX_REQUIRE_DEFINED
modified src/delete.c
@@ -53,6 +53,7 @@ exec_delete(int argc, char **argv)
	match_t match = MATCH_EXACT;
	int ch;
	bool force = false;
+
	bool recursive_flag = false;
	bool yes;
	bool dry_run = false;
	int retcode = EX_SOFTWARE;
@@ -88,7 +89,7 @@ exec_delete(int argc, char **argv)
			quiet = true;
			break;
		case 'R':
-
			f |= PKG_FLAG_RECURSIVE;
+
			recursive_flag = true;
			break;
		case 'x':
			match = MATCH_REGEX;
@@ -142,6 +143,15 @@ exec_delete(int argc, char **argv)
		return (EX_IOERR);
	}

+
	/*
+
	 * By default delete packages recursively.
+
	 * If force mode is enabled then we try to remove packages non-recursively.
+
	 * However, if -f and -R flags are both enabled then we return to
+
	 * recursive deletion.
+
	 */
+
	if (!force || recursive_flag)
+
		f |= PKG_FLAG_RECURSIVE;
+

	pkg_jobs_set_flags(jobs, f);

	if (match == MATCH_EXACT) {
modified src/fetch.c
@@ -73,7 +73,7 @@ exec_fetch(int argc, char **argv)
			match = MATCH_ALL;
			break;
		case 'd':
-
			f |= PKG_FLAG_WITH_DEPS;
+
			f |= PKG_FLAG_WITH_DEPS | PKG_FLAG_RECURSIVE;
			break;
		case 'g':
			match = MATCH_GLOB;
modified src/utils.c
@@ -629,7 +629,10 @@ print_jobs_summary_pkg(struct pkg *new_pkg, struct pkg *old_pkg,
	case PKG_SOLVED_DELETE:
		*oldsize += flatsize;

-
		pkg_printf("\tRemoving %n-%v\n", new_pkg, new_pkg);
+
		pkg_printf("\tRemoving %n-%v", new_pkg, new_pkg);
+
		if (why != NULL)
+
			printf(" (%s)", why);
+
		printf("\n");
		break;
	case PKG_SOLVED_UPGRADE_REMOVE:
		pkg_printf("\tRemoving old version of %n-%v\n", new_pkg, new_pkg);
modified src/which.c
@@ -27,6 +27,7 @@
 */

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

#include <stdio.h>
#include <pkg.h>
@@ -40,10 +41,13 @@
void
usage_which(void)
{
-
	fprintf(stderr, "Usage: pkg which [-qgo] <file>\n\n");
+
	fprintf(stderr, "Usage: pkg which [-qgop] <file>\n\n");
	fprintf(stderr, "For more information see 'pkg help which'.\n");
}

+
static bool is_there(char *);
+
int get_match(char **, char *, char *);
+

int
exec_which(int argc, char **argv)
{
@@ -51,12 +55,16 @@ exec_which(int argc, char **argv)
	struct pkgdb_it *it = NULL;
	struct pkg *pkg = NULL;
	char pathabs[MAXPATHLEN];
+
	char *p, *path, *match;
	int ret = EPKG_OK, retcode = EX_SOFTWARE;
	int ch;
+
	int res, pathlen;
	bool orig = false;
	bool glob = false;
+
	bool search = false;
+
	bool search_s = false;

-
	while ((ch = getopt(argc, argv, "qgo")) != -1) {
+
	while ((ch = getopt(argc, argv, "qgop")) != -1) {
		switch (ch) {
		case 'q':
			quiet = true;
@@ -67,6 +75,9 @@ exec_which(int argc, char **argv)
		case 'o':
			orig = true;
			break;
+
		case 'p':
+
			search_s = true;
+
			break;
		default:
			usage_which();
			return (EX_USAGE);
@@ -76,18 +87,11 @@ exec_which(int argc, char **argv)
	argc -= optind;
	argv += optind;

-
	if (argc != 1) {
+
	if (argc < 1) {
		usage_which();
		return (EX_USAGE);
	}

-
	if (!glob)
-
		absolutepath(argv[0], pathabs, sizeof(pathabs));
-
	else {
-
		if (strlcpy(pathabs, argv[0], sizeof(pathabs)) >= sizeof(pathabs))
-
			return (EX_USAGE);
-
	}
-

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
		return (EX_IOERR);
	}
@@ -98,32 +102,123 @@ exec_which(int argc, char **argv)
		return (EX_TEMPFAIL);
	}

-
	if ((it = pkgdb_query_which(db, pathabs, glob)) == NULL) {
-
		retcode = EX_IOERR;
-
		goto cleanup;
+
	if (search_s) {
+
		if ((path = getenv("PATH")) == NULL) {
+
			printf("$PATH is not set, falling back to non-search behaviour\n");
+
			search_s = false;
+
		} else {
+
			pathlen = strlen(path) + 1;
+
		}
	}

-
	while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
-
		retcode = EX_OK;
-
		if (quiet && orig)
-
			pkg_printf("%o\n", pkg);
-
		else if (quiet && !orig)
-
			pkg_printf("%n-%v\n", pkg, pkg);
-
		else if (!quiet && orig)
-
			pkg_printf("%S was installed by package %o\n", pathabs, pkg);
-
		else if (!quiet && !orig)
-
			pkg_printf("%S was installed by package %n-%v\n", pathabs, pkg, pkg);
-
	}
+
	while (argc >= 1) {
+
		retcode = EX_SOFTWARE;
+
		if (search_s) {
+
			if ((argv[0][0] == '.') || (argv[0][0] == '/')) {
+
				search = false;
+
			} else {
+
				search = true;
+

+
				if (strlen(argv[0]) >= FILENAME_MAX) {
+
					retcode = EX_USAGE;
+
					goto cleanup;
+
				}
+

+
				p = malloc(pathlen);
+
				if (p == NULL) {
+
					retcode = EX_OSERR;
+
					goto cleanup;
+
				}
+
				strlcpy(p, path, pathlen);
+

+
				match = NULL;
+
				res = get_match(&match, p, argv[0]);
+
				free(p);
+

+
				if (res == (EX_USAGE)) {
+
					printf("%s was not found in PATH, falling back to non-search behaviour\n", argv[0]);
+
					search = false;
+
				} else if (res == (EX_OSERR)) {
+
					retcode = EX_OSERR;
+
					goto cleanup;
+
				} else {
+
					absolutepath(match, pathabs, sizeof(pathabs));
+
					free(match);
+
				}
+
			}
+
		}
+

+
		if (!glob && !search)
+
			absolutepath(argv[0], pathabs, sizeof(pathabs));
+
		else if (!search) {
+
			if (strlcpy(pathabs, argv[0], sizeof(pathabs)) >= sizeof(pathabs))
+
				retcode = EX_USAGE;
+
				goto cleanup;
+
		}
+

+

+
		if ((it = pkgdb_query_which(db, pathabs, glob)) == NULL) {
+
			retcode = EX_IOERR;
+
			goto cleanup;
+
		}
+

+
		pkg = NULL;
+
		while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
+
			retcode = EX_OK;
+
			if (quiet && orig)
+
				pkg_printf("%o\n", pkg);
+
			else if (quiet && !orig)
+
				pkg_printf("%n-%v\n", pkg, pkg);
+
			else if (!quiet && orig)
+
				pkg_printf("%S was installed by package %o\n", pathabs, pkg);
+
			else if (!quiet && !orig)
+
				pkg_printf("%S was installed by package %n-%v\n", pathabs, pkg, pkg);
+
		}

-
	if (retcode != EX_OK && !quiet)
-
		printf("%s was not found in the database\n", pathabs);
+
		if (retcode != EX_OK && !quiet)
+
			printf("%s was not found in the database\n", pathabs);

-
	pkg_free(pkg);
-
	pkgdb_it_free(it);
+
		pkg_free(pkg);
+
		pkgdb_it_free(it);

-
cleanup:
-
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
-
	pkgdb_close(db);
+
		argc--;
+
		argv++;
+

+
	}
+

+
	cleanup:
+
		pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
+
		pkgdb_close(db);

	return (retcode);
}
+

+

+
static bool
+
is_there(char *candidate)
+
{
+
	return (access(candidate, F_OK) == 0);
+
}
+

+
int
+
get_match(char **pathabs, char *path, char *filename)
+
{
+
	char candidate[PATH_MAX];
+
	const char *d;
+
	int len;
+

+
	while ((d = strsep(&path, ":")) != NULL) {
+
		if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
+
		    filename) >= (int)sizeof(candidate))
+
			continue;
+
		if (is_there(candidate)) {
+
			len = strlen(candidate) + 1;
+
			*pathabs = malloc(len);
+
			if (*pathabs == NULL)
+
				return (EX_OSERR);
+
			strlcpy(*pathabs, candidate, len);
+
			return (EX_OK);
+
		}
+
	}
+
	return (EX_USAGE);
+
}