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 669b695aae46f2955feaed2aa233a2d3cf6c0b0c
parent 0186eb380ed68d6c88839bb321b65813606f9876
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)