Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'master' into indexfile
Matthew Seaman committed 12 years ago
commit 669b695aae46f2955feaed2aa233a2d3cf6c0b0c
parent 0186eb3
36 files changed +658 -494
modified configure.ac
@@ -2,7 +2,7 @@ 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.a5])
+
m4_define([pkg_version], [maj_ver.med_ver.min_ver.a7])

AC_INIT([pkg],[pkg_version],[https://github.com/freebsd/pkg],[pkg])
AC_CONFIG_SRCDIR([configure.ac])
modified docs/pkg-repo.8
@@ -174,7 +174,7 @@ set to
.Dv PUBKEY
and its path set via
.Sy PUBKEY
-
setting in the repository configuration file :
+
setting in the repository configuration file:
.Pp
.Dl pkg repo /usr/ports/packages repo.key
.Pp
modified docs/pkg_printf.3
@@ -485,7 +485,7 @@ Default row format:
.It Cm %Bn
Required shared library name [string]
.Vt struct pkg_shlib *
-
.It Cm %C
+
.It Cm \&%C
Categories [array]
.Vt struct pkg *
.Pp
modified libpkg/pkg.c
@@ -364,14 +364,6 @@ pkg_set_from_file(struct pkg *pkg, pkg_attr attr, const char *path, bool trimcr)
}

int
-
pkg_licenses(const struct pkg *pkg, struct pkg_license **l)
-
{
-
	assert(pkg != NULL);
-

-
	HASH_NEXT(pkg->licenses, (*l));
-
}
-

-
int
pkg_users(const struct pkg *pkg, struct pkg_user **u)
{
	assert(pkg != NULL);
@@ -425,14 +417,6 @@ pkg_files(const struct pkg *pkg, struct pkg_file **f)
}

int
-
pkg_categories(const struct pkg *pkg, struct pkg_category **c)
-
{
-
	assert(pkg != NULL);
-

-
	HASH_NEXT(pkg->categories, (*c));
-
}
-

-
int
pkg_dirs(const struct pkg *pkg, struct pkg_dir **d)
{
	assert(pkg != NULL);
@@ -483,30 +467,34 @@ pkg_provides(const struct pkg *pkg, struct pkg_provide **c)
int
pkg_addlicense(struct pkg *pkg, const char *name)
{
-
	struct pkg_license *l = NULL;
+
	pkg_object *o = NULL;
+
	pkg_iter iter = NULL;

	assert(pkg != NULL);
	assert(name != NULL && name[0] != '\0');
	const char *pkgname;

-
	if (pkg->licenselogic == LICENSE_SINGLE && HASH_COUNT(pkg->licenses) != 0) {
+
	if (pkg->licenselogic == LICENSE_SINGLE && UCL_COUNT(pkg->licenses) != 0) {
		pkg_get(pkg, PKG_NAME, &pkgname);
		pkg_emit_error("%s have a single license which is already set",
		    pkgname);
		return (EPKG_FATAL);
	}

-
	HASH_FIND_STR(pkg->licenses, name, l);
-
	if (l != NULL) {
-
		pkg_emit_error("duplicate license listing: %s, ignoring", name);
-
		return (EPKG_OK);
+
	while ((o = pkg_object_iterate(pkg->licenses, &iter))) {
+
		if (strcmp(pkg_object_string(o), name) == 0) {
+
			if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
				pkg_emit_error("duplicate license listing: %s, fatal (developer mode)", name);
+
				return (EPKG_FATAL);
+
			} else {
+
				pkg_emit_error("duplicate license listing: %s, ignoring", name);
+
				return (EPKG_OK);
+
			}
+
		}
	}

-
	pkg_license_new(&l);
-

-
	strlcpy(l->name, name, sizeof(l->name));
-

-
	HASH_ADD_STR(pkg->licenses, name, l);
+
	o = ucl_object_fromstring_common(name, strlen(name), 0);
+
	pkg->licenses = ucl_array_append(pkg->licenses, o);

	return (EPKG_OK);
}
@@ -521,8 +509,13 @@ pkg_adduid(struct pkg *pkg, const char *name, const char *uidstr)

	HASH_FIND_STR(pkg->users, name, u);
	if (u != NULL) {
-
		pkg_emit_error("duplicate user listing: %s, ignoring", name);
-
		return (EPKG_OK);
+
		if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
			pkg_emit_error("duplicate user listing: %s, fatal (developer mode)", name);
+
			return (EPKG_FATAL);
+
		} else {
+
			pkg_emit_error("duplicate user listing: %s, ignoring", name);
+
			return (EPKG_OK);
+
		}
	}

	pkg_user_new(&u);
@@ -555,8 +548,13 @@ pkg_addgid(struct pkg *pkg, const char *name, const char *gidstr)

	HASH_FIND_STR(pkg->groups, name, g);
	if (g != NULL) {
-
		pkg_emit_error("duplicate group listing: %s, ignoring", name);
-
		return (EPKG_OK);
+
		if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
			pkg_emit_error("duplicate group listing: %s, fatal (developer mode)", name);
+
			return (EPKG_FATAL);
+
		} else {
+
			pkg_emit_error("duplicate group listing: %s, ignoring", name);
+
			return (EPKG_OK);
+
		}
	}

	pkg_group_new(&g);
@@ -593,9 +591,15 @@ pkg_adddep(struct pkg *pkg, const char *name, const char *origin, const char *ve
	HASH_FIND_STR(pkg->deps, origin, d);
	if (d != NULL) {
		pkg_get(pkg, PKG_NAME, &n1, PKG_VERSION, &v1);
-
		pkg_emit_error("%s-%s: duplicate dependency listing: %s-%s, ignoring",
-
		    n1, v1, name, version);
-
		return (EPKG_OK);
+
		if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
			pkg_emit_error("%s-%s: duplicate dependency listing: %s-%s, fatal (developer mode)",
+
			    n1, v1, name, version);
+
			return (EPKG_FATAL);
+
		} else {
+
			pkg_emit_error("%s-%s: duplicate dependency listing: %s-%s, ignoring",
+
			    n1, v1, name, version);
+
			return (EPKG_OK);
+
		}
	}

	pkg_dep_new(&d);
@@ -654,8 +658,13 @@ pkg_addfile_attr(struct pkg *pkg, const char *path, const char *sha256, const ch
	if (check_duplicates) {
		HASH_FIND_STR(pkg->files, path, f);
		if (f != NULL) {
-
			pkg_emit_error("duplicate file listing: %s, ignoring", pkg_file_path(f));
-
			return (EPKG_OK);
+
			if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
				pkg_emit_error("duplicate file listing: %s, fatal (developer mode)", pkg_file_path(f));
+
				return (EPKG_FATAL);
+
			} else {
+
				pkg_emit_error("duplicate file listing: %s, ignoring", pkg_file_path(f));
+
				return (EPKG_OK);
+
			}
		}
	}

@@ -682,23 +691,26 @@ pkg_addfile_attr(struct pkg *pkg, const char *path, const char *sha256, const ch
int
pkg_addcategory(struct pkg *pkg, const char *name)
{
-
	struct pkg_category *c = NULL;
+
	pkg_object *o = NULL;
+
	pkg_iter iter = NULL;

	assert(pkg != NULL);
	assert(name != NULL && name[0] != '\0');

-
	HASH_FIND_STR(pkg->categories, name, c);
-
	if (c != NULL) {
-
		pkg_emit_error("duplicate category listing: %s, ignoring", name);
-
		return (EPKG_OK);
+
	while ((o = (pkg_object_iterate(pkg->categories, &iter)))) {
+
		if (strcmp(pkg_object_string(o), name) == 0) {
+
			if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
				pkg_emit_error("duplicate category listing: %s, fatal (developer mode)", name);
+
				return (EPKG_FATAL);
+
			} else {
+
				pkg_emit_error("duplicate category listing: %s, ignoring", name);
+
				return (EPKG_OK);
+
			}
+
		}
	}

-
	pkg_category_new(&c);
-

-
	sbuf_set(&c->name, name);
-

-
	HASH_ADD_KEYPTR(hh, pkg->categories, pkg_category_name(c),
-
	    strlen(pkg_category_name(c)), c);
+
	o = ucl_object_fromstring_common(name, strlen(name), 0);
+
	pkg->categories = ucl_array_append(pkg->categories, o);

	return (EPKG_OK);
}
@@ -721,8 +733,13 @@ pkg_adddir_attr(struct pkg *pkg, const char *path, const char *uname, const char
	if (check_duplicates) {
		HASH_FIND_STR(pkg->dirs, path, d);
		if (d != NULL) {
-
			pkg_emit_error("duplicate directory listing: %s, ignoring", path);
-
			return (EPKG_OK);
+
			if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
				pkg_emit_error("duplicate directory listing: %s, fatal (developer mode)", path);
+
				return (EPKG_FATAL);
+
			} else {
+
				pkg_emit_error("duplicate directory listing: %s, ignoring", path);
+
				return (EPKG_OK);
+
			}
		}
	}

@@ -856,8 +873,13 @@ pkg_addoption(struct pkg *pkg, const char *key, const char *value)
		pkg_option_new(&o);
		sbuf_set(&o->key, key);
	} else if ( o->value != NULL) {
-
		pkg_emit_error("duplicate options listing: %s, ignoring", key);
-
		return (EPKG_OK);
+
		if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
			pkg_emit_error("duplicate options listing: %s, fatal (developer mode)", key);
+
			return (EPKG_FATAL);
+
		} else {
+
			pkg_emit_error("duplicate options listing: %s, ignoring", key);
+
			return (EPKG_OK);
+
		}
	}

	sbuf_set(&o->value, value);
@@ -889,8 +911,13 @@ pkg_addoption_default(struct pkg *pkg, const char *key,
		pkg_option_new(&o);
		sbuf_set(&o->key, key);
	} else if ( o->default_value != NULL) {
-
		pkg_emit_error("duplicate default value for option: %s, ignoring", key);
-
		return (EPKG_OK);
+
		if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
			pkg_emit_error("duplicate default value for option: %s, fatal (developer mode)", key);
+
			return (EPKG_FATAL);
+
		} else {
+
			pkg_emit_error("duplicate default value for option: %s, ignoring", key);
+
			return (EPKG_OK);
+
		}
	}

	sbuf_set(&o->default_value, default_value);
@@ -921,8 +948,13 @@ pkg_addoption_description(struct pkg *pkg, const char *key,
		pkg_option_new(&o);
		sbuf_set(&o->key, key);
	} else if ( o->description != NULL) {
-
		pkg_emit_error("duplicate description for option: %s, ignoring", key);
-
		return (EPKG_OK);
+
		if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
			pkg_emit_error("duplicate description for option: %s, fatal (developer mode)", key);
+
			return (EPKG_FATAL);
+
		} else {
+
			pkg_emit_error("duplicate description for option: %s, ignoring", key);
+
			return (EPKG_OK);
+
		}
	}

	sbuf_set(&o->description, description);
@@ -1043,9 +1075,15 @@ pkg_addannotation(struct pkg *pkg, const char *tag, const char *value)

	an = ucl_object_find_key(pkg->annotations, tag);
	if (an != NULL) {
-
		pkg_emit_error("duplicate annotation tag: %s value: %s,"
-
	           " ignoring", tag, value);
-
		return (EPKG_OK);
+
		if (pkg_object_bool(pkg_config_get("DEVELOPER_MODE"))) {
+
			pkg_emit_error("duplicate annotation tag: %s value: %s,"
+
			    " fatal (developer mode)", tag, value);
+
			return (EPKG_OK);
+
		} else {
+
			pkg_emit_error("duplicate annotation tag: %s value: %s,"
+
			    " ignoring", tag, value);
+
			return (EPKG_OK);
+
		}
	}
	an = ucl_object_fromstring_common(value, strlen(value), 0);
	pkg->annotations = ucl_object_insert_key(pkg->annotations,
@@ -1055,6 +1093,22 @@ pkg_addannotation(struct pkg *pkg, const char *tag, const char *value)
}

pkg_object *
+
pkg_licenses(const struct pkg *pkg)
+
{
+
	assert (pkg != NULL);
+

+
	return (pkg->licenses);
+
}
+

+
pkg_object *
+
pkg_categories(const struct pkg *pkg)
+
{
+
	assert (pkg != NULL);
+

+
	return (pkg->categories);
+
}
+

+
pkg_object *
pkg_annotations(const struct pkg *pkg)
{
	assert(pkg != NULL);
@@ -1099,11 +1153,11 @@ pkg_list_count(const struct pkg *pkg, pkg_list list)
	case PKG_RDEPS:
		return (HASH_COUNT(pkg->rdeps));
	case PKG_LICENSES:
-
		return (HASH_COUNT(pkg->licenses));
+
		return (UCL_COUNT(pkg->licenses));
	case PKG_OPTIONS:
		return (HASH_COUNT(pkg->options));
	case PKG_CATEGORIES:
-
		return (HASH_COUNT(pkg->categories));
+
		return (UCL_COUNT(pkg->categories));
	case PKG_FILES:
		return (HASH_COUNT(pkg->files));
	case PKG_DIRS:
@@ -1139,7 +1193,10 @@ pkg_list_free(struct pkg *pkg, pkg_list list) {
		pkg->flags &= ~PKG_LOAD_RDEPS;
		break;
	case PKG_LICENSES:
-
		HASH_FREE(pkg->licenses, pkg_license_free);
+
		if (pkg->licenses != NULL) {
+
			ucl_object_unref(pkg->licenses);
+
			pkg->licenses = NULL;
+
		}
		pkg->flags &= ~PKG_LOAD_LICENSES;
		break;
	case PKG_OPTIONS:
@@ -1147,7 +1204,10 @@ pkg_list_free(struct pkg *pkg, pkg_list list) {
		pkg->flags &= ~PKG_LOAD_OPTIONS;
		break;
	case PKG_CATEGORIES:
-
		HASH_FREE(pkg->categories, free);
+
		if (pkg->categories != NULL) {
+
			ucl_object_unref(pkg->categories);
+
			pkg->categories = NULL;
+
		}
		pkg->flags &= ~PKG_LOAD_CATEGORIES;
		break;
	case PKG_FILES:
modified libpkg/pkg.h.in
@@ -573,19 +573,8 @@ int pkg_files(const struct pkg *, struct pkg_file **file);
 */
int pkg_dirs(const struct pkg *pkg, struct pkg_dir **dir);

-
/**
-
 * Iterates over the categories of the package.
-
 * @param Must be set to NULL for the first call.
-
 * @return An error code
-
 */
-
int pkg_categories(const struct pkg *pkg, struct pkg_category **category);
-

-
/**
-
 * Iterates over the licenses of the package.
-
 * @param Must be set to NULL for the first call.
-
 * @return An error code.
-
 */
-
int pkg_licenses(const struct pkg *pkg, struct pkg_license **license);
+
pkg_object *pkg_categories(const struct pkg *pkg);
+
pkg_object *pkg_licenses(const struct pkg *pkg);

/**
 * Iterates over the users of the package.
@@ -1305,6 +1294,7 @@ const char *pkg_object_string(pkg_object *o);
void pkg_object_free(pkg_object *o);
const char *pkg_object_key(pkg_object *);
pkg_object *pkg_object_iterate(pkg_object *, pkg_iter *);
+
unsigned pkg_object_count(pkg_object *);
const char *pkg_object_dump(pkg_object *o);
const char *pkg_config_dump(void);

@@ -1358,6 +1348,8 @@ typedef enum {
	PKG_EVENT_NOTICE,
	PKG_EVENT_DEBUG,
	PKG_EVENT_INCREMENTAL_UPDATE,
+
	PKG_EVENT_QUERY_YESNO,
+
	PKG_EVENT_QUERY_SELECT,
	/* errors */
	PKG_EVENT_ERROR,
	PKG_EVENT_ERRNO,
@@ -1477,6 +1469,16 @@ struct pkg_event {
			int level;
			char *msg;
		} e_debug;
+
		struct {
+
			const char *msg;
+
			int deft;
+
		} e_query_yesno;
+
		struct {
+
			const char *msg;
+
			const char **items;
+
			int ncnt;
+
			int deft;
+
		} e_query_select;
	};
};

@@ -1652,4 +1654,22 @@ struct sbuf *pkg_sbuf_vprintf(struct sbuf * restrict sbuf,
bool pkg_has_message(struct pkg *p);
bool pkg_is_locked(const struct pkg * restrict p);

+

+
/**
+
 * Defines how many chars of checksum are there in a package's name
+
 * We define this number as sufficient for 24k packages.
+
 * To find out probability of collision it is possible to use the following
+
 * python function to calculate 'birthday paradox' probability:
+
 *  def bp(m, n):
+
 *      power = -(n * n) / (2. * m)
+
 *      return 1. - exp(power)
+
 * 
+
 * For our choice of 2^40 (or 10 hex characters) it is:
+
 *  >>> bp(float(2 ** 40), 24500.)
+
 *  0.00027292484660568217
+
 * 
+
 * And it is negligible probability
+
 */ 
+
#define PKG_FILE_CKSUM_CHARS 10
+

#endif
modified libpkg/pkg_add.c
@@ -239,7 +239,7 @@ pkg_add(struct pkgdb *db, const char *path, unsigned flags, struct pkg_manifest_
	 * somesuch, there's no valid directory to search.
	 */

-
	if (strncmp(path, "-", 2) != 0) {
+
	if (strncmp(path, "-", 2) != 0 && (flags & PKG_ADD_UPGRADE) == 0) {
		basedir = dirname(path);
		if ((ext = strrchr(path, '.')) == NULL) {
			pkg_emit_error("%s has no extension", path);
modified libpkg/pkg_attributes.c
@@ -215,66 +215,6 @@ pkg_dir_try(struct pkg_dir const * const d)
}

/*
-
 * Category
-
 */
-

-
int
-
pkg_category_new(struct pkg_category **c)
-
{
-
	if ((*c = calloc(1, sizeof(struct pkg_category))) == NULL)
-
		return (EPKG_FATAL);
-

-
	return (EPKG_OK);
-
}
-

-
const char *
-
pkg_category_name(struct pkg_category const * const c)
-
{
-
	assert(c != NULL);
-

-
	return (sbuf_get(c->name));
-
}
-

-
void
-
pkg_category_free(struct pkg_category *c)
-
{
-

-
	if (c == NULL)
-
		return;
-

-
	sbuf_free(c->name);
-
	free(c);
-
}
-

-
/*
-
 * License
-
 */
-
int
-
pkg_license_new(struct pkg_license **l)
-
{
-
	if ((*l = calloc(1, sizeof(struct pkg_license))) == NULL) {
-
		pkg_emit_errno("calloc", "pkg_license");
-
		return (EPKG_FATAL);
-
	}
-

-
	return (EPKG_OK);
-
}
-

-
void
-
pkg_license_free(struct pkg_license *l)
-
{
-
	free(l);
-
}
-

-
const char *
-
pkg_license_name(struct pkg_license const * const l)
-
{
-
	assert(l != NULL);
-

-
	return (l->name);
-
}
-

-
/*
 * User
 */

modified libpkg/pkg_event.c
@@ -55,6 +55,7 @@ sbuf_json_escape(struct sbuf *buf, const char *str)
static void
pipeevent(struct pkg_event *ev)
{
+
	int i;
	struct pkg_dep *dep = NULL;
	struct sbuf *msg, *buf;
	const char *message;
@@ -320,6 +321,32 @@ pipeevent(struct pkg_event *ev)
			ev->e_incremental_update.added,
			ev->e_incremental_update.processed);
		break;
+
	case PKG_EVENT_QUERY_YESNO:
+
		sbuf_printf(msg, "{\"type\": \"QUERY_YESNO\", "
+
		    "\"data\": {"
+
			"\"msg\": \"%s\","
+
			"\"default\": \"%d\""
+
			"}}", ev->e_query_yesno.msg,
+
			ev->e_query_yesno.deft);
+
		break;
+
	case PKG_EVENT_QUERY_SELECT:
+
		sbuf_printf(msg, "{\"type\": \"QUERY_SELECT\", "
+
		    "\"data\": {"
+
			"\"msg\": \"%s\","
+
			"\"ncnt\": \"%d\","
+
			"\"default\": \"%d\","
+
			"\"items\": ["
+
			, ev->e_query_select.msg,
+
			ev->e_query_select.ncnt,
+
			ev->e_query_select.deft);
+
		for (i = 0; i < ev->e_query_select.ncnt - 1; i++)
+
		{
+
			sbuf_printf(msg, "{ \"text\": \"%s\" },",
+
				ev->e_query_select.items[i]);
+
		}
+
		sbuf_printf(msg, "{ \"text\": \"%s\" } ] }}",
+
			ev->e_query_select.items[i]);
+
		break;
	default:
		break;
	}
@@ -336,13 +363,15 @@ pkg_event_register(pkg_event_cb cb, void *data)
	_data = data;
}

-
static void
+
static int
pkg_emit_event(struct pkg_event *ev)
{
+
	int ret = 0;
	pkg_plugins_hook_run(PKG_PLUGIN_HOOK_EVENT, ev, NULL);
	if (_cb != NULL)
-
		_cb(_data, ev);
+
		ret = _cb(_data, ev);
	pipeevent(ev);
+
	return (ret);
}

void
@@ -719,6 +748,36 @@ pkg_emit_incremental_update(int updated, int removed, int added, int processed)
	pkg_emit_event(&ev);
}

+
bool
+
pkg_emit_query_yesno(bool deft, const char *msg)
+
{
+
	struct pkg_event ev;
+
	int ret;
+

+
	ev.type = PKG_EVENT_QUERY_YESNO;
+
	ev.e_query_yesno.msg = msg;
+
	ev.e_query_yesno.deft = deft;
+

+
	ret = pkg_emit_event(&ev);
+
	return (ret ? true : false);
+
}
+

+
int
+
pkg_emit_query_select(const char *msg, const char **items, int ncnt, int deft)
+
{
+
	struct pkg_event ev;
+
	int ret;
+

+
	ev.type = PKG_EVENT_QUERY_SELECT;
+
	ev.e_query_select.msg = msg;
+
	ev.e_query_select.items = items;
+
	ev.e_query_select.ncnt = ncnt;
+
	ev.e_query_select.deft = deft;
+

+
	ret = pkg_emit_event(&ev);
+
	return ret;
+
}
+

void
pkg_debug(int level, const char *fmt, ...)
{
modified libpkg/pkg_jobs.c
@@ -593,6 +593,7 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
	struct pkg_job_provide *pr, *prhead;
	struct pkg_job_seen *seen;
	int ret;
+
	bool automatic = false;
	const char *origin, *digest;
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
@@ -625,6 +626,9 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
			 * We have a package installed, but its dependencies are not,
			 * try to search a remote dependency
			 */
+
			pkg_get(pkg, PKG_ORIGIN, &origin);
+
			pkg_debug(1, "dependency %s of local package %s is not installed",
+
					pkg_dep_get(d, PKG_DEP_ORIGIN), origin);
			npkg = get_remote_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), 0);
			if (npkg == NULL) {
				/* Cannot continue */
@@ -634,9 +638,12 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
				}
				return (EPKG_FATAL);
			}
+
			/* Set automatic if no local package is found */
+
			pkg_set(npkg, PKG_AUTOMATIC, (int64_t)true);
		}
-
		else if (j->type == PKG_JOBS_UPGRADE ||
-
				j->type == PKG_JOBS_INSTALL) {
+
		else if ((j->type == PKG_JOBS_UPGRADE ||
+
				j->type == PKG_JOBS_INSTALL) &&
+
				npkg->type == PKG_INSTALLED) {
			/* For upgrade jobs we need to ensure that we do not have a newer version */
			rpkg = get_remote_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), 0);
			if (rpkg != NULL) {
@@ -646,13 +653,16 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
				}
			}
		}
-
		pkg_set(npkg, PKG_AUTOMATIC, (int64_t)true);
+

		if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
			return (EPKG_FATAL);
		if (rpkg != NULL) {
+
			/* Save automatic flag */
+
			pkg_get(npkg, PKG_AUTOMATIC, &automatic);
+
			pkg_set(rpkg, PKG_AUTOMATIC, (int64_t)automatic);
+

			if (pkg_jobs_add_universe(j, rpkg, recursive, false, NULL) != EPKG_OK)
				return (EPKG_FATAL);
-
			pkg_set(rpkg, PKG_AUTOMATIC, (int64_t)true);
		}
	}

@@ -691,17 +701,34 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
		if (unit != NULL)
			continue;

-
		/* Check both local and remote conflicts */
-
		npkg = get_remote_pkg(j, pkg_conflict_origin(c), 0);
-
		if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
-
			return (EPKG_FATAL);
-
		npkg = get_local_pkg(j, pkg_conflict_origin(c), 0);
-
		if (npkg == NULL) {
-
			continue;
+
		/* Check local and remote conflicts */
+
		if (pkg->type == PKG_INSTALLED) {
+
			/* Installed packages can conflict with remote ones */
+
			npkg = get_remote_pkg(j, pkg_conflict_origin(c), 0);
+
			if (npkg == NULL)
+
				continue;
+

+
			if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
+
				return (EPKG_FATAL);
		}
+
		else {
+
			/* Remote packages can conflict with remote and local */
+
			npkg = get_local_pkg(j, pkg_conflict_origin(c), 0);
+
			if (npkg == NULL)
+
				continue;

-
		if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
-
			return (EPKG_FATAL);
+
			if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
+
				return (EPKG_FATAL);
+

+
			if (c->type != PKG_CONFLICT_REMOTE_LOCAL) {
+
				npkg = get_remote_pkg(j, pkg_conflict_origin(c), 0);
+
				if (npkg == NULL)
+
					continue;
+

+
				if (pkg_jobs_add_universe(j, npkg, recursive, false, NULL) != EPKG_OK)
+
					return (EPKG_FATAL);
+
			}
+
		}
	}

	/* For remote packages we should also handle shlib deps */
@@ -710,24 +737,52 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg,
			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;
+
				rpkg = 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;
+
				while (pkgdb_it_next(it, &rpkg, flags) == EPKG_OK) {
+
					pkg_get(rpkg, PKG_DIGEST, &digest, PKG_ORIGIN, &origin);
+
					/* Check for local packages */
+
					HASH_FIND_STR(j->universe, origin, unit);
+
					if (unit != NULL) {
+
						if (pkg_need_upgrade (rpkg, unit->pkg, false)) {
+
							/* Remote provide is newer, so we can add it */
+
							if (pkg_jobs_add_universe(j, rpkg, recursive, false,
+
																&unit) != EPKG_OK)
+
								return (EPKG_FATAL);
+
						}
					}
					else {
-
						unit = seen->un;
+
						/* Maybe local package has just been not added */
+
						npkg = get_local_pkg(j, origin, 0);
+
						if (npkg != NULL) {
+
							if (pkg_jobs_add_universe(j, npkg, recursive, false,
+
									&unit) != EPKG_OK)
+
								return (EPKG_FATAL);
+
							if (pkg_need_upgrade (rpkg, npkg, false)) {
+
								/* Remote provide is newer, so we can add it */
+
								if (pkg_jobs_add_universe(j, rpkg, recursive, false,
+
										&unit) != EPKG_OK)
+
									return (EPKG_FATAL);
+
							}
+
						}
+
					}
+
					/* Skip seen packages */
+
					if (unit == NULL) {
+
						HASH_FIND_STR(j->seen, digest, seen);
+
						if (seen == NULL) {
+
							pkg_jobs_add_universe(j, rpkg, recursive, false,
+
									&unit);
+

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

					pr = calloc (1, sizeof (*pr));
@@ -1043,7 +1098,7 @@ static bool
pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive)
{
	int ret, ret1, ret2;
-
	const char *lversion, *rversion;
+
	const char *lversion, *rversion, *larch, *rarch;
	struct pkg_option *lo = NULL, *ro = NULL;
	struct pkg_dep *ld = NULL, *rd = NULL;
	struct pkg_shlib *ls = NULL, *rs = NULL;
@@ -1054,8 +1109,8 @@ pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive)
	if (pkg_is_locked(lp))
		return (false);

-
	pkg_get(lp, PKG_VERSION, &lversion);
-
	pkg_get(rp, PKG_VERSION, &rversion);
+
	pkg_get(lp, PKG_VERSION, &lversion, PKG_ARCH, &larch);
+
	pkg_get(rp, PKG_VERSION, &rversion, PKG_ARCH, &rarch);

	ret = pkg_version_cmp(lversion, rversion);
	if (ret > 0)
@@ -1063,6 +1118,12 @@ pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive)
	else if (ret < 0)
		return (true);

+
	/* Compare archs */
+
	if (strcmp (larch, rarch) != 0) {
+
		pkg_set(rp, PKG_REASON, "ABI changed");
+
		return (true);
+
	}
+

	/* compare options */
	for (;;) {
		ret1 = pkg_options(rp, &ro);
@@ -1580,8 +1641,11 @@ jobs_solve_install(struct pkg_jobs *j)

	if (j->solved == 0) {
		HASH_ITER(hh, j->patterns, jp, jtmp) {
-
			if (pkg_jobs_find_remote_pattern(j, jp, &got_local) != EPKG_OK)
+
			if (pkg_jobs_find_remote_pattern(j, jp, &got_local) != EPKG_OK) {
+
				pkg_emit_error("No packages matching '%s' have been found in the "
+
						"repositories", jp->pattern);
				return (EPKG_FATAL);
+
			}
		}
		if (got_local) {
			/*
@@ -1648,7 +1712,7 @@ jobs_solve_fetch(struct pkg_jobs *j)
			/* TODO: use repository priority here */
			if (find_remote_pkg(j, jp->pattern, jp->match, true,
					j->flags & PKG_FLAG_RECURSIVE, true) == EPKG_FATAL)
-
				pkg_emit_error("No packages matching '%s' has been found in the "
+
				pkg_emit_error("No packages matching '%s' have been found in the "
						"repositories", jp->pattern);
		}
	}
@@ -1799,7 +1863,10 @@ pkg_jobs_handle_install(struct pkg_solved *ps, struct pkg_jobs *j, bool handle_r
		target = ps->items[0]->jp->path;
	}
	else {
-
		pkg_snprintf(path, sizeof(path), "%S/%R", cachedir, new);
+
		pkg_snprintf(path, sizeof(path), "%R", new);
+
		if (*path != '/')
+
			pkg_snprintf(path, sizeof(path), "%S/%n-%v-%z",
+
					cachedir, new, new, new);
		target = path;
	}

@@ -2031,8 +2098,9 @@ pkg_jobs_apply(struct pkg_jobs *j)
			if (p->type != PKG_REMOTE)												\
				continue;															\
			int64_t pkgsize;														\
-
			pkg_get(p, PKG_PKGSIZE, &pkgsize, PKG_REPOPATH, &repopath);				\
-
			snprintf(cachedpath, sizeof(cachedpath), "%s/%s", cachedir, repopath);	\
+
			pkg_get(p, PKG_PKGSIZE, &pkgsize);				\
+
			pkg_snprintf(cachedpath, sizeof(cachedpath), "%S/%n-%v-%z", \
+
				cachedir, p, p, p);													\
			if (stat(cachedpath, &st) == -1)										\
				dlsize += pkgsize;													\
			else																	\
@@ -2062,7 +2130,6 @@ pkg_jobs_fetch(struct pkg_jobs *j)
	struct stat st;
	int64_t dlsize = 0;
	const char *cachedir = NULL;
-
	const char *repopath = NULL;
	char cachedpath[MAXPATHLEN];
	
	cachedir = pkg_object_string(pkg_config_get("PKG_CACHEDIR"));
@@ -2128,10 +2195,10 @@ pkg_jobs_check_conflicts(struct pkg_jobs *j)
		else {
			p = ps->items[0]->pkg;
			if (p->type == PKG_REMOTE) {
-
				const char *pkgrepopath;
-
				pkg_get(p, PKG_REPOPATH, &pkgrepopath);
-
				snprintf(path, sizeof(path), "%s/%s", cachedir,
-
						pkgrepopath);
+
				pkg_snprintf(path, sizeof(path), "%R", p);
+
				if (*path != '/')
+
					pkg_snprintf(path, sizeof(path), "%S/%n-%v-%z",
+
							cachedir, p, p, p);
				if (pkg_open(&pkg, path, keys, 0) != EPKG_OK)
					return (EPKG_FATAL);
				p = pkg;
modified libpkg/pkg_manifest.c
@@ -840,8 +840,6 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
	struct pkg_option	*option   = NULL;
	struct pkg_file		*file     = NULL;
	struct pkg_dir		*dir      = NULL;
-
	struct pkg_category	*category = NULL;
-
	struct pkg_license	*license  = NULL;
	struct pkg_user		*user     = NULL;
	struct pkg_group	*group    = NULL;
	struct pkg_shlib	*shlib    = NULL;
@@ -891,10 +889,9 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
	}

	pkg_debug(4, "Emitting licenses");
-
	seq = NULL;
-
	while (pkg_licenses(pkg, &license) == EPKG_OK)
-
		seq = ucl_array_append(seq, ucl_object_fromstring(pkg_license_name(license)));
-
	obj = ucl_object_insert_key(top, seq, "licenses", 8, false);
+
	if (pkg->categories != NULL)
+
		obj = ucl_object_insert_key(top,
+
		    ucl_object_ref(pkg->categories), "licenses", 8, false);

	obj = ucl_object_insert_key(top, ucl_object_fromint(flatsize), "flatsize", 8, false);
	if (pkgsize > 0)
@@ -916,10 +913,9 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
	obj = ucl_object_insert_key(top, map, "deps", 4, false);

	pkg_debug(4, "Emitting categories");
-
	seq = NULL;
-
	while (pkg_categories(pkg, &category) == EPKG_OK)
-
		seq = ucl_array_append(seq, ucl_object_fromstring(pkg_category_name(category)));
-
	obj = ucl_object_insert_key(top, seq, "categories", 10, false);
+
	if (pkg->categories != NULL)
+
		obj = ucl_object_insert_key(top,
+
		    ucl_object_ref(pkg->categories), "categories", 10, false);

	pkg_debug(4, "Emitting users");
	seq = NULL;
@@ -973,7 +969,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)

	if (pkg->annotations != NULL)
		obj = ucl_object_insert_key(top,
-
		    ucl_object_ref(map), "annotations", 11, false);
+
		    ucl_object_ref(pkg->annotations), "annotations", 11, false);

	if ((flags & PKG_MANIFEST_EMIT_COMPACT) == 0) {
		if ((flags & PKG_MANIFEST_EMIT_NOFILES) == 0) {
modified libpkg/pkg_object.c
@@ -27,10 +27,14 @@
#include <assert.h>
#include <ucl.h>
#include "pkg.h"
+
#include "private/pkg.h"

const char *
pkg_object_dump(pkg_object *o)
{
+
	if (o == NULL)
+
		return ("");
+

	return (ucl_object_emit(o, UCL_EMIT_CONFIG));
}

@@ -43,6 +47,9 @@ pkg_object_free(pkg_object *o)
const char *
pkg_object_key(pkg_object *o)
{
+
	if (o == NULL)
+
		return (NULL);
+

	return (ucl_object_key(o));
}

@@ -58,6 +65,10 @@ pkg_object_iterate(pkg_object *o, pkg_iter *it)
pkg_object_t
pkg_object_type(pkg_object *o)
{
+

+
	if (o == NULL)
+
		return (PKG_NULL);
+

	switch (o->type) {
	case UCL_OBJECT:
		return (PKG_OBJECT);
@@ -88,6 +99,9 @@ pkg_object_string(pkg_object *o)
{
	const char *ret;

+
	if (o == NULL)
+
		return (NULL);
+

	ret = ucl_object_tostring_forced(o);

	if (ret && *ret == '\0')
@@ -103,3 +117,8 @@ pkg_object_int(pkg_object *o)
	return (ucl_object_toint(o));
}

+
unsigned
+
pkg_object_count(pkg_object *o)
+
{
+
	return (UCL_COUNT(o));
+
}
modified libpkg/pkg_ports.c
@@ -321,9 +321,14 @@ file(struct plist *p, char *line, struct file_attr *a)
		buf = NULL;
		regular = false;

-
		if (S_ISREG(st.st_mode))
+
		if (S_ISDIR(st.st_mode)) {
+
			pkg_emit_error("Plist error, directory listed as a file: %s", line);
+
			free_file_attr(a);
+
			return (EPKG_FATAL);
+
		} else if (S_ISREG(st.st_mode))
			regular = true;

+

		/* special case for hardlinks */
		if (st.st_nlink > 1)
			regular = is_hardlink(p->hardlinks, &st);
modified libpkg/pkg_printf.c
@@ -155,7 +155,8 @@
 *
 * x
 * y
-
 * z
+
 *
+
 * z  pkg          short checksum
 */

struct pkg_printf_fmt {
@@ -758,6 +759,15 @@ static const struct pkg_printf_fmt fmt[] = {
		PP_ALL,
		&format_checksum,
	},
+
	[PP_PKG_SHORT_CHECKSUM] =
+
	{
+
		'z',
+
		'\0',
+
		false,
+
		true,
+
		PP_ALL,
+
		&format_short_checksum,
+
	},
	[PP_PKG_VERSION] =
	{
		'v',
@@ -930,13 +940,14 @@ format_categories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
		return (list_count(sbuf, pkg_list_count(pkg, PKG_CATEGORIES),
				   p));
	else {
-
		struct pkg_category	*cat = NULL;
-
		int			 count;
+
		pkg_object	*cat;
+
		pkg_iter	 it = NULL;
+
		int		 count;

		set_list_defaults(p, "%Cn", ", ");

		count = 1;
-
		while (pkg_categories(pkg, &cat) == EPKG_OK) {
+
		while ((cat = pkg_object_iterate(pkg->categories, &it))) {
			if (count > 1)
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
					     cat, count, PP_C);
@@ -955,9 +966,9 @@ format_categories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
struct sbuf *
format_category_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_category	*cat = data;
+
	pkg_object	*o = (pkg_object *)data;

-
	return (string_val(sbuf, pkg_category_name(cat), p));
+
	return (string_val(sbuf, pkg_object_string(o), p));
}

/*
@@ -1239,16 +1250,17 @@ format_licenses(struct sbuf *sbuf, const void *data, struct percent_esc *p)
	const struct pkg	*pkg = data;

	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_LICENSES),
+
		return (list_count(sbuf, pkg_object_count(pkg_licenses(pkg)),
				   p));
	else {
-
		struct pkg_license	*lic = NULL;
+
		pkg_object	*lic;
+
		pkg_iter	 iter = NULL;
		int			 count;

		set_list_defaults(p, "%Ln", " %l ");

		count = 1;
-
		while (pkg_licenses(pkg, &lic) == EPKG_OK) {
+
		while ((lic = pkg_object_iterate(pkg->licenses, &iter))) {
			if (count > 1)
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
					     lic, count, PP_L);
@@ -1267,9 +1279,9 @@ format_licenses(struct sbuf *sbuf, const void *data, struct percent_esc *p)
struct sbuf *
format_license_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_license	*license = data;
+
	pkg_object *o = (pkg_object *) data;

-
	return (string_val(sbuf, pkg_license_name(license), p));
+
	return (string_val(sbuf, pkg_object_string(o), p));
}

/*
@@ -1817,6 +1829,26 @@ format_checksum(struct sbuf *sbuf, const void *data, struct percent_esc *p)
}

/*
+
 * %z -- Package short checksum. string. Accepts field width, left align
+
 */
+
struct sbuf *
+
format_short_checksum(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg	*pkg = data;
+
	const char		*checksum;
+
	char	 csum[PKG_FILE_CKSUM_CHARS + 1];
+
	int slen;
+

+
	pkg_get(pkg, PKG_CKSUM, &checksum);
+

+
	slen = MIN(PKG_FILE_CKSUM_CHARS, strlen(checksum));
+
	memcpy(csum, checksum, slen);
+
	csum[slen] = '\0';
+

+
	return (string_val(sbuf, csum, p));
+
}
+

+
/*
 * %w -- Home page URL.  string.  Accepts field width, left align
 */
struct sbuf *
modified libpkg/pkg_repo.c
@@ -73,7 +73,8 @@ pkg_repo_fetch(struct pkg *pkg)
	pkg_get(pkg, PKG_REPONAME, &reponame,
	    PKG_CKSUM, &sum, PKG_NAME, &name, PKG_VERSION, &version);

-
	pkg_snprintf(dest, sizeof(dest), "%S/%R", cachedir, pkg);
+
	pkg_snprintf(dest, sizeof(dest), "%S/%n-%v-%z",
+
			cachedir, pkg, pkg, pkg);

	/* If it is already in the local cachedir, dont bother to
	 * download it */
@@ -109,6 +110,11 @@ pkg_repo_fetch(struct pkg *pkg)
	else
		pkg_snprintf(url, sizeof(url), "%S/%R", packagesite, pkg);

+
	if (strncasecmp(packagesite, "file://", 7) == 0) {
+
		pkg_set(pkg, PKG_REPOPATH, url + 7);
+
		return (EPKG_OK);
+
	}
+

	retcode = pkg_fetch_file(repo, url, dest, 0);
	fetched = 1;

modified libpkg/pkg_solve.c
@@ -115,10 +115,19 @@ pkg_debug_print_rule (struct pkg_solve_item *rule)
	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 ? " | " : ")");
+
		if (it->var->resolved) {
+
			sbuf_printf(sb, "%s%s%s(%c)%s", it->inverse ? "!" : "",
+
					it->var->origin,
+
					(it->var->unit->pkg->type == PKG_INSTALLED) ? "(l)" : "(r)",
+
					(it->var->to_install) ? '+' : '-',
+
					it->next ? " | " : ")");
+
		}
+
		else {
+
			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));
@@ -151,8 +160,10 @@ check_again:
					ret = false;
					LL_FOREACH(unresolved, it) {
						if (it->var->resolved) {
-
							if (PKG_SOLVE_CHECK_ITEM(it))
+
							if (PKG_SOLVE_CHECK_ITEM(it)) {
								ret = true;
+
								break;
+
							}
						}
					}
					if (!ret) {
@@ -179,12 +190,15 @@ check_again:
					ret = false;
					LL_FOREACH(unresolved, it) {
						if (it->var->resolved) {
-
							if (PKG_SOLVE_CHECK_ITEM(it))
+
							if (PKG_SOLVE_CHECK_ITEM(it)) {
								ret = true;
+
								break;
+
							}
						}
					}
					if (!ret) {
						/* This is a unit */
+
						int resolved = 0;
						LL_FOREACH(unresolved, it) {
							if (!it->var->resolved) {
								it->var->to_install = (!it->inverse);
@@ -195,9 +209,14 @@ check_again:
										it->var->priority,
										it->var->to_install ? "install" : "delete");
								pkg_debug_print_rule(unresolved);
+
								resolved ++;
								break;
							}
						}
+
						if (resolved == 0) {
+
							pkg_debug_print_rule(unresolved);
+
							assert (resolved > 0);
+
						}
						solved_vars ++;
						(*propagated) ++;
						/* We want to try propagating all clauses for a single variable */
@@ -225,6 +244,7 @@ pkg_solve_propagate_pure(struct pkg_solve_problem *problem)
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
		if (var->nrules == 0) {
			/* This variable is independent and should not change its state */
+
			assert (var->rules == NULL);
			var->to_install = (var->unit->pkg->type == PKG_INSTALLED);
			var->resolved = true;
			pkg_debug(2, "leave %s-%s(%d) to %s",
@@ -290,6 +310,27 @@ pkg_solve_test_guess(struct pkg_solve_problem *problem, struct pkg_solve_variabl
	return (true);
}

+
/*
+
 * Set initial guess based on a variable passed
+
 */
+
static bool
+
pkg_solve_initial_guess(struct pkg_solve_variable *var)
+
{
+
	if (var->unit->pkg->type == PKG_INSTALLED) {
+
		/* For local packages assume true if we have no upgrade */
+
		if (var->unit->next == NULL && var->unit->prev == var->unit)
+
			return (true);
+
	}
+
	else {
+
		/* For remote packages we return true if they are upgrades for local ones */
+
		if (var->unit->next != NULL || var->unit->prev != var->unit)
+
			return (true);
+
	}
+

+
	/* Otherwise set initial guess to false */
+
	return (false);
+
}
+

/**
 * Try to solve sat problem
 * @param rules incoming rules to solve
@@ -303,7 +344,7 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
	int propagated;
	struct pkg_solve_variable *var, *tvar;
	int64_t unresolved = 0, iters = 0;
-
	bool rc;
+
	bool rc, backtrack;

	struct _solver_tree_elt {
		struct pkg_solve_variable *var;
@@ -327,9 +368,15 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
	elt = solver_tree;

	/* DPLL Algorithm */
-
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
+
	DL_FOREACH2(problem->variables_by_digest, var, hd.next) {
		if (!var->resolved) {

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

			if (elt == NULL) {
				/* Add new element to the backtracking queue */
				elt = malloc (sizeof (*elt));
@@ -342,15 +389,17 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
				elt->guess = -1;
				DL_APPEND(solver_tree, elt);
			}
+
			assert (var == elt->var);

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

			unresolved ++;
+
			iters ++;
			if (!pkg_solve_test_guess(problem, var)) {
				if (elt->guess == -1) {
					/* This is free variable, so we can assign true or false to it */
@@ -374,8 +423,8 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
					var->guess = -1;
					/* Go to the previous level */
					elt = elt->prev;
-
					tvar = var;
-
					var = elt->var;
+
					tvar = elt->var;
+
					backtrack = true;
					continue;
				}
			}
@@ -715,11 +764,12 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
					cnt = 1;
					LL_FOREACH(prhead, pr) {
						/* For each provide */
-
						pkg_get(pr->un->pkg, PKG_DIGEST, &digest);
+
						pkg_get(pr->un->pkg, PKG_DIGEST, &digest, PKG_ORIGIN, &origin);
						HASH_FIND(hd, problem->variables_by_digest, digest,
								strlen(digest), var);
						if (var == NULL) {
-
							continue;
+
							if (pkg_solve_add_universe_variable(j, problem, origin, &var) != EPKG_OK)
+
								continue;
						}
						/* XXX: select all its versions? */

modified libpkg/pkgdb.c
@@ -2569,12 +2569,12 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
	struct pkg_file		*file = NULL;
	struct pkg_dir		*dir = NULL;
	struct pkg_option	*option = NULL;
-
	struct pkg_category	*category = NULL;
-
	struct pkg_license	*license = NULL;
	struct pkg_user		*user = NULL;
	struct pkg_group	*group = NULL;
	struct pkg_conflict	*conflict = NULL;
	struct pkgdb_it		*it = NULL;
+
	pkg_object		*obj;
+
	pkg_iter		 iter;

	sqlite3			*s;

@@ -2760,11 +2760,11 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
	 * Insert categories
	 */

-
	while (pkg_categories(pkg, &category) == EPKG_OK) {
-
		const char	*pkg_cat = pkg_category_name(category);
-
		ret = run_prstmt(CATEGORY1, pkg_cat);
+
	iter = NULL;
+
	while ((obj = pkg_object_iterate(pkg->categories, &iter))) {
+
		ret = run_prstmt(CATEGORY1, pkg_object_string(obj));
		if (ret == SQLITE_DONE)
-
			ret = run_prstmt(CATEGORY2, package_id, pkg_cat);
+
			ret = run_prstmt(CATEGORY2, package_id, pkg_object_string(obj));
		if (ret != SQLITE_DONE) {
			ERROR_SQLITE(s);
			goto cleanup;
@@ -2775,11 +2775,12 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
	 * Insert licenses
	 */

-
	while (pkg_licenses(pkg, &license) == EPKG_OK) {
-
		if (run_prstmt(LICENSES1, pkg_license_name(license))
+
	iter = NULL;
+
	while ((obj = pkg_object_iterate(pkg->licenses, &iter))) {
+
		if (run_prstmt(LICENSES1, pkg_object_string(obj))
		    != SQLITE_DONE
		    ||
-
		    run_prstmt(LICENSES2, package_id, pkg_license_name(license))
+
		    run_prstmt(LICENSES2, package_id, pkg_object_string(obj))
		    != SQLITE_DONE) {
			ERROR_SQLITE(s);
			goto cleanup;
modified libpkg/pkgdb_repo.c
@@ -472,8 +472,6 @@ pkgdb_repo_add_package(struct pkg *pkg, const char *pkg_path,
	lic_t			 licenselogic;
	int			 ret;
	struct pkg_dep		*dep      = NULL;
-
	struct pkg_category	*category = NULL;
-
	struct pkg_license	*license  = NULL;
	struct pkg_option	*option   = NULL;
	struct pkg_shlib	*shlib    = NULL;
	pkg_object		*obj;
@@ -526,14 +524,12 @@ try_again:
		}
	}

-
	category = NULL;
-
	while (pkg_categories(pkg, &category) == EPKG_OK) {
-
		const char *cat_name = pkg_category_name(category);
-

-
		ret = run_prepared_statement(CAT1, cat_name);
+
	it = NULL;
+
	while ((obj = pkg_object_iterate(pkg->categories, &it))) {
+
		ret = run_prepared_statement(CAT1, pkg_object_string(obj));
		if (ret == SQLITE_DONE)
			ret = run_prepared_statement(CAT2, package_id,
-
					cat_name);
+
			    pkg_object_string(obj));
		if (ret != SQLITE_DONE)
		{
			ERROR_SQLITE(sqlite);
@@ -541,14 +537,12 @@ try_again:
		}
	}

-
	license = NULL;
-
	while (pkg_licenses(pkg, &license) == EPKG_OK) {
-
		const char *lic_name = pkg_license_name(license);
-

-
		ret = run_prepared_statement(LIC1, lic_name);
+
	it = NULL;
+
	while ((obj = pkg_object_iterate(pkg->licenses, &it))) {
+
		ret = run_prepared_statement(LIC1, pkg_object_string(obj));
		if (ret == SQLITE_DONE)
			ret = run_prepared_statement(LIC2, package_id,
-
					lic_name);
+
			    pkg_object_string(obj));
		if (ret != SQLITE_DONE) {
			ERROR_SQLITE(sqlite);
			return (EPKG_FATAL);
modified libpkg/private/pkg.h
@@ -117,8 +117,8 @@ struct pkg {
	int64_t		 old_flatsize;
	int64_t		 pkgsize;
	struct sbuf	*scripts[PKG_NUM_SCRIPTS];
-
	struct pkg_license	*licenses;
-
	struct pkg_category	*categories;
+
	ucl_object_t		*licenses;
+
	ucl_object_t		*categories;
	struct pkg_dep		*deps;
	struct pkg_dep		*rdeps;
	struct pkg_file		*files;
@@ -166,17 +166,6 @@ struct pkg_provide {
	UT_hash_handle	hh;
};

-
struct pkg_license {
-
	/* should be enough to match a license name */
-
	char name[64];
-
	UT_hash_handle	hh;
-
};
-

-
struct pkg_category {
-
	struct sbuf	*name;
-
	UT_hash_handle	hh;
-
};
-

struct pkg_file {
	char		 path[MAXPATHLEN];
	int64_t		 size;
@@ -371,12 +360,6 @@ void pkg_file_free(struct pkg_file *);
int pkg_dir_new(struct pkg_dir **);
void pkg_dir_free(struct pkg_dir *);

-
int pkg_category_new(struct pkg_category **);
-
void pkg_category_free(struct pkg_category *);
-

-
int pkg_license_new(struct pkg_license **);
-
void pkg_license_free(struct pkg_license *);
-

int pkg_option_new(struct pkg_option **);
void pkg_option_free(struct pkg_option *);

modified libpkg/private/pkg_printf.h
@@ -141,10 +141,11 @@ typedef enum _fmt_code_t {
	PP_PKG_CHECKSUM,
	PP_PKG_VERSION,
	PP_PKG_HOME_PAGE,
-
	PP_LAST_FORMAT = PP_PKG_HOME_PAGE,
+
	PP_PKG_SHORT_CHECKSUM,
+
	PP_LAST_FORMAT = PP_PKG_SHORT_CHECKSUM,
	PP_LITERAL_PERCENT,
	PP_UNKNOWN,
-
	PP_END_MARKER,
+
	PP_END_MARKER
} fmt_code_t;

#define	ITEM_FMT_SET	(0x1U << 0)
@@ -221,6 +222,7 @@ _static struct sbuf *format_requirements(struct sbuf *, const void *, struct per
_static struct sbuf *format_flatsize(struct sbuf *, const void *, struct percent_esc *);
_static struct sbuf *format_install_tstamp(struct sbuf *, const void *, struct percent_esc *);
_static struct sbuf *format_checksum(struct sbuf *, const void *, struct percent_esc *);
+
_static struct sbuf *format_short_checksum(struct sbuf *, const void *, struct percent_esc *);
_static struct sbuf *format_version(struct sbuf *, const void *, struct percent_esc *);
_static struct sbuf *format_home_url(struct sbuf *, const void *, struct percent_esc *);
_static struct sbuf *format_literal_percent(struct sbuf *, __unused const void *, __unused struct percent_esc *);
modified libpkg/rcscripts.c
@@ -150,7 +150,7 @@ rc_start(const char *rc_file)
	if ((error = posix_spawn(&pid, "/usr/sbin/service", NULL, NULL,
	    __DECONST(char **, argv), environ)) != 0) {
		errno = error;
-
		pkg_emit_errno("Cannot stop service", rc_file);
+
		pkg_emit_errno("Cannot start service", rc_file);
		return (-1);
	}

modified src/annotate.c
@@ -68,7 +68,7 @@ do_add(struct pkgdb *db, struct pkg *pkg, const char *tag, const char *value)
	int		 ret = EPKG_OK;


-
	if (yes || query_tty_yesno("%n-%v: Add annotation tagged: %S with "
+
	if (yes || query_tty_yesno(false, "%n-%v: Add annotation tagged: %S with "
	               "value: %S? [y/N]: ", pkg, pkg, tag, value)) {

		ret = pkgdb_add_annotation(db, pkg, tag, value);
@@ -103,7 +103,7 @@ do_modify(struct pkgdb *db, struct pkg *pkg, const char *tag, const char *value)
	int		 ret = EPKG_OK;


-
	if (yes || query_tty_yesno("%n-%v: Change annotation tagged: %S to "
+
	if (yes || query_tty_yesno(false, "%n-%v: Change annotation tagged: %S to "
		         "new value: %S? [y/N]: ", pkg, pkg, tag, value)) {
		ret = pkgdb_modify_annotation(db, pkg, tag, value);
		if (ret == EPKG_OK || ret == EPKG_WARN) {
@@ -127,7 +127,7 @@ do_delete(struct pkgdb *db, struct pkg *pkg, const char *tag)
	const char	*pkgname, *pkgversion;
	int		 ret = EPKG_OK;

-
	if (yes || query_tty_yesno("%n-%v: Delete annotation tagged: %S "
+
	if (yes || query_tty_yesno(false, "%n-%v: Delete annotation tagged: %S "
			 "[y/N]: ", pkg, pkg, tag)) {
		ret = pkgdb_delete_annotation(db, pkg, tag);
		if (ret == EPKG_OK) {
modified src/autoremove.c
@@ -128,7 +128,7 @@ exec_autoremove(__unused int argc, __unused char **argv)
		print_jobs_summary(jobs,
		    "Deinstallation has been requested for the following %d packages:\n\n", nbactions);
		if (!yes && !dry_run)
-
			yes = query_yesno(
+
			yes = query_yesno(false,
		            "\nProceed with deinstalling packages [y/N]: ");
		if (dry_run)
			yes = false;
modified src/check.c
@@ -177,7 +177,7 @@ fix_deps(struct pkgdb *db, struct deps_head *dh, int nbpkgs, bool yes)
	print_jobs_summary(jobs, "The following packages will be installed:\n\n");
	
	if (!yes)
-
		yes = query_yesno("\n>>> Try to fix the missing dependencies [y/N]: ");
+
		yes = query_yesno(false, "\n>>> Try to fix the missing dependencies [y/N]: ");

	if (yes) {
		if (pkgdb_access(PKGDB_MODE_WRITE, PKGDB_DB_LOCAL) ==
modified src/clean.c
@@ -1,6 +1,7 @@
/*-
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
+
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
@@ -27,27 +28,32 @@

#include <sys/stat.h>
#include <sys/queue.h>
+
/* For MIN */
+
#include <sys/param.h>

#include <assert.h>
#include <err.h>
#include <fts.h>
+
#include <libutil.h>
#include <pkg.h>
#include <stdbool.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
+
#include <uthash.h>

#include "pkgcli.h"

struct deletion_list {
	STAILQ_ENTRY(deletion_list) next;
-
	unsigned	 reason;
-
	const char	*path;
-
	const char	*origin;
-
	const char	*newname;
-
	const char	*newversion;
-
	char		 data[0];
+
	char	*path;
};
+

+
struct sumlist {
+
	char sum[PKG_FILE_CKSUM_CHARS + 1];
+
	UT_hash_handle hh;
+
};
+

#define OUT_OF_DATE	(1U<<0)
#define REMOVED		(1U<<1)
#define CKSUM_MISMATCH	(1U<<2)
@@ -57,58 +63,19 @@ struct deletion_list {
STAILQ_HEAD(dl_head, deletion_list);

static int
-
add_to_dellist(struct dl_head *dl,  unsigned reason, const char *path,
-
	       const char *origin, const char *newname, const char *newversion)
+
add_to_dellist(struct dl_head *dl,  const char *path)
{
	struct deletion_list	*dl_entry;
-
	size_t			 alloc_len;
-
	size_t			 offset;

	assert(path != NULL);
-
	assert(origin != NULL);
-

-
	alloc_len = sizeof(struct deletion_list) + strlen(path) +
-
		strlen(origin) + 2;
-
	if (newname != NULL)
-
		alloc_len += strlen(newname) + 1;
-
	if (newversion != NULL)
-
		alloc_len += strlen(newversion) + 1;

-
	dl_entry = calloc(1, alloc_len);
+
	dl_entry = calloc(1, sizeof(struct deletion_list));
	if (dl_entry == NULL) {
		warn("adding deletion list entry");
		return (EPKG_FATAL);
	}

-
	dl_entry->reason = reason;
-

-
	offset = 0;
-

-
	alloc_len = strlen(path) + 1;
-
	strlcpy(&(dl_entry->data[offset]), path, alloc_len);
-
	dl_entry->path = &(dl_entry->data[offset]);
-
	offset = alloc_len;
-

-
	alloc_len = strlen(origin) + 1;
-
	strlcpy(&(dl_entry->data[offset]), origin, alloc_len);
-
	dl_entry->origin = &(dl_entry->data[offset]);
-
	offset += alloc_len;
-

-
	if (newname != NULL) {
-
		alloc_len = strlen(newname) + 1;
-
		strlcpy(&(dl_entry->data[offset]), newname, alloc_len);
-
		dl_entry->newname = &(dl_entry->data[offset]);
-
		offset += alloc_len;
-
	} else
-
		dl_entry->newname = NULL;
-

-
	if (newversion != NULL) {
-
		alloc_len = strlen(newversion) + 1;
-
		strlcpy(&(dl_entry->data[offset]), newversion, alloc_len);
-
		dl_entry->newversion = &(dl_entry->data[offset]);
-
		offset += alloc_len;
-
	} else
-
		dl_entry->newversion = NULL;
+
	dl_entry->path = strdup(path);

	STAILQ_INSERT_TAIL(dl, dl_entry, next);

@@ -123,55 +90,7 @@ free_dellist(struct dl_head *dl)
	while (!STAILQ_EMPTY(dl)) {
		dl_entry = STAILQ_FIRST(dl);
		STAILQ_REMOVE_HEAD(dl, next);
-
		free(dl_entry);
-
	}
-
}
-

-
static void
-
display_dellist(struct dl_head *dl, const char *cachedir)
-
{
-
	struct deletion_list	*dl_entry;
-
	const char		*relpath;
-

-
	printf("The following package files will be deleted "
-
	       "from the cache directory\n%s:\n\n", cachedir);
-

-
	printf("%-30s %-20s %s\n", "Package:", "Origin:", "Reason:");
-
	STAILQ_FOREACH(dl_entry, dl, next) {
-
		if (strlen(cachedir) + 1 < strlen(dl_entry->path)) {
-
			relpath = dl_entry->path + strlen(cachedir);
-
			if (relpath[0] == '/')
-
				relpath++;
-
		} else
-
			relpath = dl_entry->path;
-

-
		printf("%-30s %-20s ", relpath, dl_entry->origin);
-

-
		switch (dl_entry->reason) {
-
		case OUT_OF_DATE:
-
			printf("Superseded by %s-%s\n",
-
			    dl_entry->newname != NULL ?
-
			       dl_entry->newname :
-
			       "(unknown)",
-
			    dl_entry->newversion != NULL ?
-
			       dl_entry->newversion :
-
			       "(unknown)");
-
			break;
-
		case REMOVED:
-
			printf("Removed from the repository\n");
-
			break;
-
		case CKSUM_MISMATCH:
-
			printf("Checksum mismatch\n");
-
			break;
-
		case ALL:
-
			printf("Removing all\n");
-
			break;
-
		case SIZE_MISMATCH:
-
			printf("Size mismatch\n");
-
			break;
-
		default:	/* not reached */
-
			break;
-
		}
+
		free(dl_entry->path);
	}
}

@@ -182,9 +101,6 @@ delete_dellist(struct dl_head *dl)
	int			retcode = EX_OK;
	int			count = 0;

-
	if (!quiet)
-
		printf("Deleting:\n");
-

	STAILQ_FOREACH(dl_entry, dl, next) {
		if (!quiet)
			printf("\t%s\n", dl_entry->path);
@@ -205,6 +121,31 @@ delete_dellist(struct dl_head *dl)
	return (retcode);
}

+
/*
+
 * Extract hash from filename in format <name>-<version>-<hash>.txz
+
 */
+
static bool
+
extract_filename_sum(const char *fname, char sum[])
+
{
+
	const char *dash_pos, *dot_pos;
+

+
	dot_pos = strrchr(fname, '.');
+
	if (dot_pos == NULL)
+
		dot_pos = fname + strlen(fname);
+

+
	dash_pos = strrchr(fname, '-');
+
	if (dash_pos == NULL)
+
		return (false);
+
	else if (dot_pos < dash_pos)
+
		dot_pos = fname + strlen(fname);
+

+
	if (dot_pos - dash_pos != PKG_FILE_CKSUM_CHARS + 1)
+
		return (false);
+

+
	strlcpy(sum, dash_pos + 1, PKG_FILE_CKSUM_CHARS + 1);
+
	return (true);
+
}
+

void
usage_clean(void)
{
@@ -217,20 +158,21 @@ exec_clean(int argc, char **argv)
{
	struct pkgdb	*db = NULL;
	struct pkgdb_it	*it = NULL;
-
	struct pkg	*pkg = NULL;
	struct pkg	*p = NULL;
+
	struct sumlist	*sumlist = NULL, *s, *t;
	FTS		*fts = NULL;
	FTSENT		*ent = NULL;
	struct dl_head	dl = STAILQ_HEAD_INITIALIZER(dl);
-
	const char	*cachedir;
-
	char		*paths[2];
-
	char		*repopath;
+
	const char	*cachedir, *sum;
+
	char		*paths[2], csum[PKG_FILE_CKSUM_CHARS + 1];
	bool		 all = false;
	bool		 dry_run = false;
-
	bool		 yes;
+
	bool		 yes, sumloaded = false;
	int		 retcode;
	int		 ret;
	int		 ch;
+
	size_t		 total = 0, slen;
+
	char		 size[7];
	struct pkg_manifest_key *keys = NULL;

	yes = pkg_object_bool(pkg_config_get("ASSUME_ALWAYS_YES"));
@@ -295,100 +237,42 @@ exec_clean(int argc, char **argv)

	pkg_manifest_keys_new(&keys);
	while ((ent = fts_read(fts)) != NULL) {
-
		const char *origin, *pkgrepopath;
-

		if (ent->fts_info != FTS_F)
			continue;

-
		repopath = ent->fts_path + strlen(cachedir);
-
		if (repopath[0] == '/')
-
			repopath++;
-

-
		if (pkg_open(&pkg, ent->fts_path, keys, PKG_OPEN_MANIFEST_ONLY) != EPKG_OK) {
-
			if (!quiet)
-
				warnx("skipping %s", ent->fts_path);
-
			continue;
-
		}
-

-
		pkg_get(pkg, PKG_ORIGIN, &origin);
-
		it = pkgdb_search(db, origin, MATCH_EXACT, FIELD_ORIGIN,
-
		    FIELD_NONE, NULL);
-

-
		if (it == NULL) {
-
			if (!quiet)
-
				warnx("skipping %s", ent->fts_path);
-
			continue;
-
		}
-

		if (all) {
-
			ret = add_to_dellist(&dl, ALL, ent->fts_path,
-
					     origin, NULL, NULL);
-
			/*
-
			 * FIXME: `ret' is not checked, what should be done if
-
			 * add_to_dellist() fails ?
-
			 */
-
			pkgdb_it_free(it);
+
			ret = add_to_dellist(&dl, ent->fts_path);
+
			total += ent->fts_statp->st_size;
			continue;
		}

-
		ret = pkgdb_it_next(it, &p, PKG_LOAD_BASIC);
-
		if (ret == EPKG_FATAL) {
-
			if (!quiet)
-
				warnx("skipping %s", ent->fts_path);
+
		if (sumlist == NULL && !sumloaded) {
+
			it = pkgdb_search(db, "*", MATCH_GLOB, FIELD_NAME, FIELD_NONE, NULL);
+
			while (pkgdb_it_next(it, &p, PKG_LOAD_BASIC) == EPKG_OK) {
+
				pkg_get(p, PKG_CKSUM, &sum);
+
				slen = MIN(strlen(sum), PKG_FILE_CKSUM_CHARS);
+
				s = calloc(1, sizeof(struct sumlist));
+
				memcpy(s->sum, sum, slen);
+
				s->sum[slen] = '\0';
+
				HASH_ADD_STR(sumlist, sum, s);
+
			}
			pkgdb_it_free(it);
-
			continue;
+
			pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
+
			pkgdb_close(db);
		}

-
		if (ret == EPKG_END) {
-
			/* No matching package found in repo */
-
			ret = add_to_dellist(&dl, REMOVED, ent->fts_path,
-
					     origin, NULL, NULL);
-
			/*
-
			 * FIXME: `ret' is not checked, what should be done if
-
			 * add_to_dellist() fails ?
-
			 */
-
			pkgdb_it_free(it);
+
		s = NULL;
+
		if (extract_filename_sum(ent->fts_name, csum))
+
			HASH_FIND_STR(sumlist, csum, s);
+
		if (s == NULL) {
+
			ret = add_to_dellist(&dl, ent->fts_path);
+
			total += ent->fts_statp->st_size;
			continue;
		}
-

-
		pkg_get(p, PKG_REPOPATH, &pkgrepopath);
-

-
		if (strcmp(repopath, pkgrepopath)) {
-
			const char	*newname;
-
			const char	*newversion;
-

-
			pkg_get(p,
-
				PKG_NAME,    &newname,
-
				PKG_VERSION, &newversion);
-

-
			ret = add_to_dellist(&dl, OUT_OF_DATE, ent->fts_path,
-
					     origin, newname, newversion);
-
		} else {
-
			char local_cksum[SHA256_DIGEST_LENGTH * 2 + 1];
-
			const char *cksum;
-
			int64_t size;
-

-
			pkg_get(p, PKG_CKSUM, &cksum, PKG_PKGSIZE, &size);
-

-
			if (ent->fts_statp->st_size != size) {
-
				ret = add_to_dellist(&dl, SIZE_MISMATCH,
-
				                  ent->fts_path, origin, NULL, NULL);
-
			} else if (hash_file(ent->fts_path, local_cksum) == EPKG_OK) {
-

-
				if (strcmp(cksum, local_cksum) != 0) {
-
					ret = add_to_dellist(&dl, CKSUM_MISMATCH, ent->fts_path,
-
							     origin, NULL, NULL);
-
				}
-
			}
-
		}
-

-
		if (ret != EPKG_OK && ret != EPKG_END) {
-
			retcode = EX_OSERR; /* out of memory */
-
			pkgdb_it_free(it);
-
			goto cleanup;
-
		}
-

-
		pkgdb_it_free(it);
+
	}
+
	HASH_ITER(hh, sumlist, s, t) {
+
		HASH_DEL(sumlist, s);
+
		free(s);
	}

	if (STAILQ_EMPTY(&dl)) {
@@ -398,12 +282,12 @@ exec_clean(int argc, char **argv)
		goto cleanup;
	}

-
	if (dry_run || !yes || !quiet)
-
		display_dellist(&dl, cachedir);
+
	humanize_number(size, sizeof(size), total, "B", HN_AUTOSCALE, 0);

+
	printf("The cleanup will free %s\n", size);
	if (!dry_run) {
		if (!yes)
-
			yes = query_yesno(
+
			yes = query_yesno(false,
				"\nProceed with cleaning the cache [y/N]: ");
		if (yes)
			retcode = delete_dellist(&dl);
@@ -414,13 +298,8 @@ cleanup:
	pkg_manifest_keys_free(keys);
	free_dellist(&dl);

-
	pkg_free(pkg);
-
	pkg_free(p);
	if (fts != NULL)
		fts_close(fts);

-
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
-
	pkgdb_close(db);
-

	return (retcode);
}
modified src/create.c
@@ -87,9 +87,9 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
		return (EX_IOERR);
	}
	/* XXX: get rid of hardcoded timeouts */
-
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0.5, 20) != EPKG_OK) {
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
		pkgdb_close(db);
-
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
		return (EX_TEMPFAIL);
	}

@@ -127,9 +127,11 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
			STAILQ_INSERT_TAIL(&head, e, next);
			foundone = true;
		}
-
		if (!foundone)
+
		if (!foundone) {
			warnx("No installed package matching \"%s\" found\n",
			    argv[i]);
+
			retcode++;
+
		}

		pkgdb_it_free(it);
		if (ret != EPKG_END)
@@ -160,7 +162,7 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
	}

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

	return (retcode);
modified src/delete.c
@@ -192,7 +192,7 @@ exec_delete(int argc, char **argv)
			goto cleanup;
		}
		if (!yes && !dry_run)
-
			yes = query_yesno(
+
			yes = query_yesno(false,
		            "\nProceed with deinstalling packages [y/N]: ");
	}
	if (!yes || (retcode = pkg_jobs_apply(jobs)) != EPKG_OK)
modified src/event.c
@@ -331,6 +331,15 @@ event_callback(void *data, struct pkg_event *ev)
	case PKG_EVENT_DEBUG:
		fprintf(stderr, "DBG(%d)> %s\n", ev->e_debug.level, ev->e_debug.msg);
		break;
+
	case PKG_EVENT_QUERY_YESNO:
+
		return ( ev->e_query_yesno.deft ?
+
			query_yesno(true, ev->e_query_yesno.msg, "[Y/n]") :
+
			query_yesno(false, ev->e_query_yesno.msg, "[y/N]") );
+
		break;
+
	case PKG_EVENT_QUERY_SELECT:
+
		return query_select(ev->e_query_select.msg, ev->e_query_select.items,
+
			ev->e_query_select.ncnt, ev->e_query_select.deft);
+
		break;
	default:
		break;
	}
modified src/fetch.c
@@ -180,7 +180,7 @@ exec_fetch(int argc, char **argv)
		print_jobs_summary(jobs, "The following packages will be fetched:\n\n");

		if (!yes)
-
			yes = query_yesno("\nProceed with fetching packages [y/N]: ");
+
			yes = query_yesno(false, "\nProceed with fetching packages [y/N]: ");
	}
	
	if (!yes || pkg_jobs_apply(jobs) != EPKG_OK)
modified src/install.c
@@ -209,7 +209,7 @@ exec_install(int argc, char **argv)
			    nbactions, pkg_jobs_total(jobs));

			if (!yes && !dry_run)
-
				yes = query_yesno(
+
				yes = query_yesno(false, 
				    "\nProceed with this action [y/N]: ");
			if (dry_run)
				yes = false;
modified src/lock.c
@@ -64,7 +64,7 @@ do_lock(struct pkgdb *db, struct pkg *pkg)
		return (EPKG_OK);
	}

-
	if (!yes && !query_yesno("%n-%v: lock this package? [y/N]: ",
+
	if (!yes && !query_yesno(false, "%n-%v: lock this package? [y/N]: ",
				 pkg, pkg))
		return (EPKG_OK);

@@ -84,7 +84,7 @@ do_unlock(struct pkgdb *db, struct pkg *pkg)
		return (EPKG_OK);
	}

-
	if (!yes && !query_yesno("%n-%v: unlock this package? [y/N]: ",
+
	if (!yes && !query_yesno(false, "%n-%v: unlock this package? [y/N]: ",
				 pkg, pkg))
		return (EPKG_OK);

modified src/pkgcli.h
@@ -245,7 +245,8 @@ void usage_config(void);
			 INFO_DESCR|INFO_MESSAGE|INFO_DEPS|INFO_RDEPS| \
			 INFO_FILES|INFO_DIRS)

-
bool query_yesno(const char *msg, ...);
+
bool query_yesno(bool deft, const char *msg, ...);
+
int query_select(const char *msg, const char **opts, int ncnt, int deft);
bool query_tty_yesno(const char *msg, ...);
int info_flags(uint64_t opt, bool remote);
void print_info(struct pkg * const pkg, uint64_t opt);
modified src/query.c
@@ -329,15 +329,13 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
{
	struct sbuf		*output = sbuf_new_auto();
	struct pkg_dep		*dep    = NULL;
-
	struct pkg_category	*cat    = NULL;
	struct pkg_option	*option = NULL;
	struct pkg_file		*file   = NULL;
	struct pkg_dir		*dir    = NULL;
-
	struct pkg_license	*lic    = NULL;
	struct pkg_user		*user   = NULL;
	struct pkg_group	*group  = NULL;
	struct pkg_shlib	*shlib  = NULL;
-
	pkg_object		*obj, *o;
+
	pkg_object		*o;
	pkg_iter		 it;

	switch (multiline) {
@@ -354,8 +352,9 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
		}
		break;
	case 'C':
-
		while (pkg_categories(pkg, &cat) == EPKG_OK) {
-
			format_str(pkg, output, qstr, cat);
+
		it = NULL;
+
		while ((o = pkg_object_iterate(pkg_categories(pkg), &it))) {
+
			format_str(pkg, output, qstr, o);
			printf("%s\n", sbuf_data(output));
		}
		break;
@@ -378,8 +377,9 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
		}
		break;
	case 'L':
-
		while (pkg_licenses(pkg, &lic) == EPKG_OK) {
-
			format_str(pkg, output, qstr, lic);
+
		it = NULL;
+
		while ((o = pkg_object_iterate(pkg_licenses(pkg), &it))) {
+
			format_str(pkg, output, qstr, o);
			printf("%s\n", sbuf_data(output));
		}
		break;
@@ -408,9 +408,8 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
		}
		break;
	case 'A':
-
		obj = pkg_annotations(pkg);
		it = NULL;
-
		while ((o = pkg_object_iterate(obj, &it))) {
+
		while ((o = pkg_object_iterate(pkg_annotations(pkg), &it))) {
			format_str(pkg, output, qstr, o);
			printf("%s\n", sbuf_data(output));
		}
modified src/rquery.c
@@ -84,12 +84,13 @@ print_index(struct pkg *pkg)
#ifndef PORTSDIR
#define PORTSDIR "/usr/ports"
#endif
-
	struct pkg_category *cat = NULL;
+
	pkg_object *obj = NULL;
+
	pkg_iter iter = NULL;

	pkg_printf("%n-%v|" PORTSDIR "/%o|%p|%c|" PORTSDIR "/%o/pkg-descr|%m|",
	    pkg, pkg, pkg, pkg, pkg, pkg, pkg);
-
	while (pkg_categories(pkg, &cat) == EPKG_OK)
-
		pkg_printf("%Cn ", cat);
+
	while ((obj = pkg_object_iterate(pkg_categories(pkg), &iter)))
+
		pkg_printf("%Cn ", obj);
	printf("\n");
}

modified src/set.c
@@ -173,10 +173,10 @@ exec_set(int argc, char **argv)

		if (!yes) {
			if (pkg != NULL)
-
				yes = query_yesno("Change origin from %S to %S for %n-%v? [y/N]: ",
+
				yes = query_yesno(false, "Change origin from %S to %S for %n-%v? [y/N]: ",
				    oldorigin, neworigin, pkg, pkg);
			else
-
				yes = query_yesno("Change origin from %S to %S for all dependencies? "
+
				yes = query_yesno(false, "Change origin from %S to %S for all dependencies? "
				    "[y/N]: ", oldorigin, neworigin);
		}
		if (pkg != NULL && yes) {
@@ -203,9 +203,9 @@ exec_set(int argc, char **argv)
					continue;
				if (!yes) {
					if (newautomatic)
-
						yes = query_yesno("Mark %n-%v as automatically installed? [y/N]: ", pkg, pkg);
+
						yes = query_yesno(false, "Mark %n-%v as automatically installed? [y/N]: ", pkg, pkg);
					else
-
						yes = query_yesno("Mark %n-%v as not automatically installed? [y/N]: ", pkg, pkg);
+
						yes = query_yesno(false, "Mark %n-%v as not automatically installed? [y/N]: ", pkg, pkg);
				}
				if (yes)
					pkgdb_set(db, pkg, PKG_SET_AUTOMATIC, newautomatic);
modified src/upgrade.c
@@ -161,7 +161,7 @@ exec_upgrade(int argc, char **argv)
				nbactions, pkg_jobs_total(jobs));

			if (!yes && !dry_run)
-
				yes = query_yesno("\nProceed with this action [y/N]: ");
+
				yes = query_yesno(false, "\nProceed with this action [y/N]: ");
			if (dry_run)
				yes = false;
		}
modified src/utils.c
@@ -39,6 +39,8 @@
#include <unistd.h>
#include <stdarg.h>
#include <paths.h>
+
#define _WITH_GETLINE
+
#include <stdio.h>
#include <pkg.h>

#include "pkgcli.h"
@@ -85,10 +87,10 @@ cleanup:
}

bool
-
query_yesno(const char *msg, ...)
+
query_yesno(bool deft, const char *msg, ...)
{
	int	 c;
-
	bool	 r = false;
+
	bool	 r = deft;
	va_list	 ap;

	va_start(ap, msg);
@@ -98,8 +100,10 @@ query_yesno(const char *msg, ...)
	c = getchar();
	if (c == 'y' || c == 'Y')
		r = true;
+
	else if (c == 'n' || c == 'N')
+
		r = false;
	else if (c == '\n' || c == EOF)
-
		return (false);
+
		return r;

	while ((c = getchar()) != '\n' && c != EOF)
		continue;
@@ -107,6 +111,41 @@ query_yesno(const char *msg, ...)
	return (r);
}

+
int
+
query_select(const char *msg, const char **opts, int ncnt, int deft)
+
{
+
	int i;
+
	char *str = NULL;
+
	char *endpntr = NULL;
+
	size_t n = 0;
+

+
	printf("%s\n", msg);
+
	for (i = 0; i < ncnt; i++) {
+
		if (i + 1 == deft)
+
		{
+
			printf("*[%d] %s\n",
+
				i + 1, opts[i]);
+
		} else {
+
			printf(" [%d] %s\n",
+
				i + 1, opts[i]);
+
		}
+
	}
+

+
	getline(&str, &n, stdin);
+
	i = (int) strtoul(str, &endpntr, 10);
+

+
	if (endpntr == NULL || *endpntr == '\0') {
+
		i = deft;
+
	} else if (*endpntr == '\n' || *endpntr == '\r') {
+
		if (i > ncnt || i < 1)
+
			i = deft;
+
	} else
+
		i = -1;
+

+
	free(str);
+
	return i;
+
}
+

/* unlike realpath(3), this routine does not expand symbolic links */
char *
absolutepath(const char *src, char *dest, size_t dest_size) {
@@ -332,7 +371,7 @@ print_info(struct pkg * const pkg, uint64_t options)
				printf("\n");
			break;
		case INFO_CATEGORIES:
-
			if (pkg_list_count(pkg, PKG_CATEGORIES) > 0) {
+
			if (pkg_object_count(pkg_categories(pkg)) > 0) {
				if (print_tag)
					printf("%-15s: ", "Categories");
				pkg_printf("%C%{%Cn%| %}\n", pkg);
@@ -340,7 +379,7 @@ print_info(struct pkg * const pkg, uint64_t options)
				printf("\n");
			break;
		case INFO_LICENSES:
-
			if (pkg_list_count(pkg, PKG_LICENSES) > 0) {
+
			if (pkg_object_count(pkg_licenses(pkg)) > 0) {
				if (print_tag)
					printf("%-15s: ", "Licenses");
				pkg_printf("%L%{%Ln%| %l %}\n", pkg);
@@ -393,7 +432,7 @@ print_info(struct pkg * const pkg, uint64_t options)
			}
			break;
		case INFO_ANNOTATIONS:
-
			if (pkg_list_count(pkg, PKG_ANNOTATIONS) > 0) {
+
			if (pkg_object_count(pkg_annotations(pkg)) > 0) {
				if (print_tag)
					printf("%-15s:\n", "Annotations");
				if (quiet)