Radish alpha
H
HardenedBSD Package Manager
Radicle
Git (anonymous pull)
Log in to clone via SSH
Merge branch 'master' into indexfile
Matthew Seaman committed 12 years ago
commit edb8f16bf401316a1cb4c7803a5a27262ea8295f
parent 6b5b6492bc690b4ad2c462f022c91b5a8d5ba18c
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);
+
}