Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'master' into fix-autoremove-completion
Brad Davis committed 12 years ago
commit 5b00ee1fdd1042469fb0778f6d4b7ba71ca58f8a
parent 90886b8
57 files changed +3465 -1137
modified Makefile
@@ -1,6 +1,5 @@

-
SUBDIR=	external \
-
	libpkg \
+
SUBDIR=	libpkg \
	pkg \
	scripts

modified README.md
@@ -196,7 +196,7 @@ repositories, updating remote package repositories and installing from them, etc

The first thing to start with is to get pkgng installed on your machine.

-
You can grap a development snapshot of pkgng from the [pkgng Github repository][1]
+
You can grab a development snapshot of pkgng from the [pkgng Github repository][1]

To get the latest version of pkgng from the Git repo, just clone it:

modified external/libucl/include/ucl.h
@@ -774,6 +774,16 @@ unsigned char *ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type);
 */
bool ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len);

+
/**
+
 * Set FILENAME and CURDIR variables in parser
+
 * @param parser parser object
+
 * @param filename filename to set or NULL to set FILENAME to "undef" and CURDIR to getcwd()
+
 * @param need_expand perform realpath() if this variable is true and filename is not NULL
+
 * @return true if variables has been set
+
 */
+
bool ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename,
+
		bool need_expand);
+

typedef void* ucl_object_iter_t;

/**
modified external/libucl/src/ucl_parser.c
@@ -86,8 +86,14 @@ ucl_chunk_restore_state (struct ucl_chunk *chunk, struct ucl_parser_saved_state
static inline void
ucl_set_err (struct ucl_chunk *chunk, int code, const char *str, UT_string **err)
{
-
	ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'",
-
			chunk->line, chunk->column, str, *chunk->pos);
+
	if (isgraph (*chunk->pos)) {
+
		ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'",
+
				chunk->line, chunk->column, str, *chunk->pos);
+
	}
+
	else {
+
		ucl_create_err (err, "error on line %d at column %d: '%s', character: '0x%02x'",
+
				chunk->line, chunk->column, str, (int)*chunk->pos);
+
	}
}

/**
@@ -899,7 +905,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
		 * A key must start with alpha, number, '/' or '_' and end with space character
		 */
		if (c == NULL) {
-
			if (ucl_lex_is_comment (p[0], p[1])) {
+
			if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
				if (!ucl_skip_comments (parser)) {
					return false;
				}
@@ -926,6 +932,12 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
				*end_of_object = true;
				return true;
			}
+
			else if (*p == '.') {
+
				ucl_chunk_skipc (chunk, p);
+
				parser->prev_state = parser->state;
+
				parser->state = UCL_STATE_MACRO_NAME;
+
				return true;
+
			}
			else {
				/* Invalid identifier */
				ucl_set_err (chunk, UCL_ESYNTAX, "key must begin with a letter", &parser->err);
@@ -994,7 +1006,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
				return false;
			}
		}
-
		else if (ucl_lex_is_comment (p[0], p[1])) {
+
		else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
			/* Check for comment */
			if (!ucl_skip_comments (parser)) {
				return false;
@@ -1127,7 +1139,7 @@ ucl_parse_string_value (struct ucl_parser *parser,
			*var_expand = true;
		}

-
		if (ucl_lex_is_atom_end (*p) || ucl_lex_is_comment (p[0], p[1])) {
+
		if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
			break;
		}
		ucl_chunk_skipc (chunk, p);
@@ -1292,7 +1304,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
		default:
			/* Skip any spaces and comments */
			if (ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) ||
-
					ucl_lex_is_comment (p[0], p[1])) {
+
					(chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
				while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
					ucl_chunk_skipc (chunk, p);
				}
@@ -1374,7 +1386,7 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
			/* Skip whitespaces */
			ucl_chunk_skipc (chunk, p);
		}
-
		else if (ucl_lex_is_comment (p[0], p[1])) {
+
		else if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
			/* Skip comment */
			if (!ucl_skip_comments (parser)) {
				return false;
@@ -1534,8 +1546,14 @@ ucl_state_machine (struct ucl_parser *parser)
	bool next_key = false, end_of_object = false;

	if (parser->top_obj == NULL) {
-
		obj = ucl_add_parser_stack (NULL, parser, false, 0);
+
		if (*chunk->pos == '[') {
+
			obj = ucl_add_parser_stack (NULL, parser, true, 0);
+
		}
+
		else {
+
			obj = ucl_add_parser_stack (NULL, parser, false, 0);
+
		}
		parser->top_obj = obj;
+
		parser->cur_obj = obj;
		parser->state = UCL_STATE_INIT;
	}

@@ -1548,7 +1566,7 @@ ucl_state_machine (struct ucl_parser *parser)
			 * if we got [ or { correspondingly or can just treat new data as
			 * a key of newly created object
			 */
-
			obj = parser->top_obj;
+
			obj = parser->cur_obj;
			if (!ucl_skip_comments (parser)) {
				parser->prev_state = parser->state;
				parser->state = UCL_STATE_ERROR;
@@ -1558,14 +1576,10 @@ ucl_state_machine (struct ucl_parser *parser)
				p = chunk->pos;
				if (*p == '[') {
					parser->state = UCL_STATE_VALUE;
-
					obj->type = UCL_ARRAY;
-
					ucl_hash_destroy (obj->value.ov, NULL);
-
					obj->value.av = NULL;
					ucl_chunk_skipc (chunk, p);
				}
				else {
					parser->state = UCL_STATE_KEY;
-
					obj->type = UCL_OBJECT;
					if (*p == '{') {
						ucl_chunk_skipc (chunk, p);
					}
@@ -1653,7 +1667,7 @@ ucl_state_machine (struct ucl_parser *parser)
				/* Now we need to skip all spaces */
				while (p < chunk->end) {
					if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
-
						if (ucl_lex_is_comment (p[0], p[1])) {
+
						if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
							/* Skip comment */
							if (!ucl_skip_comments (parser)) {
								return false;
@@ -1675,8 +1689,8 @@ ucl_state_machine (struct ucl_parser *parser)
				return false;
			}
			macro_len = ucl_expand_variable (parser, &macro_escaped, macro_start, macro_len);
-
			parser->state = UCL_STATE_AFTER_VALUE;
-
			if (macro_escaped == macro_start) {
+
			parser->state = parser->prev_state;
+
			if (macro_escaped == NULL) {
				if (!macro->handler (macro_start, macro_len, macro->ud)) {
					return false;
				}
@@ -1714,6 +1728,9 @@ ucl_parser_new (int flags)

	new->flags = flags;

+
	/* Initial assumption about filevars */
+
	ucl_parser_set_filevars (new, NULL, false);
+

	return new;
}

@@ -1736,16 +1753,51 @@ void
ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
		const char *value)
{
-
	struct ucl_variable *new;
+
	struct ucl_variable *new = NULL, *cur;

-
	new = UCL_ALLOC (sizeof (struct ucl_variable));
-
	memset (new, 0, sizeof (struct ucl_variable));
-
	new->var = strdup (var);
-
	new->var_len = strlen (var);
-
	new->value = strdup (value);
-
	new->value_len = strlen (value);
+
	if (var == NULL) {
+
		return;
+
	}

-
	LL_PREPEND (parser->variables, new);
+
	/* Find whether a variable already exists */
+
	LL_FOREACH (parser->variables, cur) {
+
		if (strcmp (cur->var, var) == 0) {
+
			new = cur;
+
			break;
+
		}
+
	}
+

+
	if (value == NULL) {
+

+
		if (new != NULL) {
+
			/* Remove variable */
+
			LL_DELETE (parser->variables, new);
+
			free (new->var);
+
			free (new->value);
+
			UCL_FREE (sizeof (struct ucl_variable), new);
+
		}
+
		else {
+
			/* Do nothing */
+
			return;
+
		}
+
	}
+
	else {
+
		if (new == NULL) {
+
			new = UCL_ALLOC (sizeof (struct ucl_variable));
+
			memset (new, 0, sizeof (struct ucl_variable));
+
			new->var = strdup (var);
+
			new->var_len = strlen (var);
+
			new->value = strdup (value);
+
			new->value_len = strlen (value);
+

+
			LL_PREPEND (parser->variables, new);
+
		}
+
		else {
+
			free (new->value);
+
			new->value = strdup (value);
+
			new->value_len = strlen (value);
+
		}
+
	}
}

bool
modified external/libucl/src/ucl_util.c
@@ -25,6 +25,8 @@
#include "ucl_internal.h"
#include "ucl_chartable.h"

+
#include <libgen.h> /* For dirname */
+

#ifdef HAVE_OPENSSL
#include <openssl/err.h>
#include <openssl/sha.h>
@@ -439,7 +441,7 @@ ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *bufl
	int fd;
	struct stat st;

-
	if (stat (filename, &st) == -1) {
+
	if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) {
		ucl_create_err (err, "cannot stat file %s: %s",
				filename, strerror (errno));
		return false;
@@ -535,6 +537,7 @@ ucl_include_url (const unsigned char *data, size_t len,
	size_t buflen = 0;
	struct ucl_chunk *chunk;
	char urlbuf[PATH_MAX];
+
	int prev_state;

	snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);

@@ -566,6 +569,9 @@ ucl_include_url (const unsigned char *data, size_t len,
#endif
	}

+
	prev_state = parser->state;
+
	parser->state = UCL_STATE_INIT;
+

	res = ucl_parser_add_chunk (parser, buf, buflen);
	if (res == true) {
		/* Remove chunk from the stack */
@@ -575,6 +581,8 @@ ucl_include_url (const unsigned char *data, size_t len,
			UCL_FREE (sizeof (struct ucl_chunk), chunk);
		}
	}
+

+
	parser->state = prev_state;
	free (buf);

	return res;
@@ -597,6 +605,7 @@ ucl_include_file (const unsigned char *data, size_t len,
	unsigned char *buf = NULL;
	size_t buflen;
	char filebuf[PATH_MAX], realbuf[PATH_MAX];
+
	int prev_state;

	snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
	if (realpath (filebuf, realbuf) == NULL) {
@@ -634,6 +643,11 @@ ucl_include_file (const unsigned char *data, size_t len,
#endif
	}

+
	ucl_parser_set_filevars (parser, realbuf, false);
+

+
	prev_state = parser->state;
+
	parser->state = UCL_STATE_INIT;
+

	res = ucl_parser_add_chunk (parser, buf, buflen);
	if (res == true) {
		/* Remove chunk from the stack */
@@ -643,6 +657,9 @@ ucl_include_file (const unsigned char *data, size_t len,
			UCL_FREE (sizeof (struct ucl_chunk), chunk);
		}
	}
+

+
	parser->state = prev_state;
+

	if (buflen > 0) {
		munmap (buf, buflen);
	}
@@ -693,16 +710,55 @@ ucl_includes_handler (const unsigned char *data, size_t len, void* ud)
}

bool
+
ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand)
+
{
+
	char realbuf[PATH_MAX], *curdir;
+

+
	if (filename != NULL) {
+
		if (need_expand) {
+
			if (realpath (filename, realbuf) == NULL) {
+
				return false;
+
			}
+
		}
+
		else {
+
			ucl_strlcpy (realbuf, filename, sizeof (realbuf));
+
		}
+

+
		/* Define variables */
+
		ucl_parser_register_variable (parser, "FILENAME", realbuf);
+
		curdir = dirname (realbuf);
+
		ucl_parser_register_variable (parser, "CURDIR", curdir);
+
	}
+
	else {
+
		/* Set everything from the current dir */
+
		curdir = getcwd (realbuf, sizeof (realbuf));
+
		ucl_parser_register_variable (parser, "FILENAME", "undef");
+
		ucl_parser_register_variable (parser, "CURDIR", curdir);
+
	}
+

+
	return true;
+
}
+

+
bool
ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
{
	unsigned char *buf;
	size_t len;
	bool ret;
+
	char realbuf[PATH_MAX];
+

+
	if (realpath (filename, realbuf) == NULL) {
+
		ucl_create_err (&parser->err, "cannot open file %s: %s",
+
				filename,
+
				strerror (errno));
+
		return false;
+
	}

-
	if (!ucl_fetch_file (filename, &buf, &len, &parser->err)) {
+
	if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err)) {
		return false;
	}

+
	ucl_parser_set_filevars (parser, realbuf, false);
	ret = ucl_parser_add_chunk (parser, buf, len);

	if (len > 0) {
modified libpkg/Makefile
@@ -2,7 +2,8 @@

LIB=		pkg
INCS=		pkg.h
-
WARNS=		6
+
# That's cheating real warnings are set later
+
WARNS=		0
PREFIX?=	/usr/local
LIBDIR=		${PREFIX}/lib
INCLUDEDIR=	${PREFIX}/include
@@ -10,9 +11,87 @@ SHLIB_MAJOR= 1

MANDIR=		${PREFIX}/man/man

+
EXPATSRCS=	${.CURDIR}/../external/expat/lib/xmlparse.c \
+
		${.CURDIR}/../external/expat/lib/xmlrole.c \
+
		${.CURDIR}/../external/expat/lib/xmltok.c
+
UCLSRCS=	${.CURDIR}/../external/libucl/src/ucl_emitter.c \
+
		${.CURDIR}/../external/libucl/src/ucl_hash.c \
+
		${.CURDIR}/../external/libucl/src/ucl_parser.c \
+
		${.CURDIR}/../external/libucl/src/ucl_util.c \
+
		${.CURDIR}/../external/libucl/src/xxhash.c
+
SQLITESRCS=	${.CURDIR}/../external/sqlite/sqlite3.c \
+
		${.CURDIR}/../external/sqlite/shell.c
+
YAMLSRCS=	${.CURDIR}/../external/libyaml/src/api.c \
+
		${.CURDIR}/../external/libyaml/src/loader.c \
+
		${.CURDIR}/../external/libyaml/src/parser.c \
+
		${.CURDIR}/../external/libyaml/src/reader.c \
+
		${.CURDIR}/../external/libyaml/src/scanner.c
+
ELFSRCS+=	${.CURDIR}/../external/libelf/elf.c \
+
		${.CURDIR}/../external/libelf/elf_begin.c \
+
		${.CURDIR}/../external/libelf/elf_cntl.c \
+
		${.CURDIR}/../external/libelf/elf_end.c \
+
		${.CURDIR}/../external/libelf/elf_errmsg.c \
+
		${.CURDIR}/../external/libelf/elf_errno.c \
+
		${.CURDIR}/../external/libelf/elf_data.c \
+
		${.CURDIR}/../external/libelf/elf_fill.c \
+
		${.CURDIR}/../external/libelf/elf_flag.c \
+
		${.CURDIR}/../external/libelf/elf_getarhdr.c \
+
		${.CURDIR}/../external/libelf/elf_getarsym.c \
+
		${.CURDIR}/../external/libelf/elf_getbase.c \
+
		${.CURDIR}/../external/libelf/elf_getident.c \
+
		${.CURDIR}/../external/libelf/elf_hash.c \
+
		${.CURDIR}/../external/libelf/elf_kind.c \
+
		${.CURDIR}/../external/libelf/elf_memory.c \
+
		${.CURDIR}/../external/libelf/elf_next.c \
+
		${.CURDIR}/../external/libelf/elf_open.c \
+
		${.CURDIR}/../external/libelf/elf_rand.c \
+
		${.CURDIR}/../external/libelf/elf_rawfile.c \
+
		${.CURDIR}/../external/libelf/elf_phnum.c \
+
		${.CURDIR}/../external/libelf/elf_shnum.c \
+
		${.CURDIR}/../external/libelf/elf_shstrndx.c \
+
		${.CURDIR}/../external/libelf/elf_scn.c \
+
		${.CURDIR}/../external/libelf/elf_strptr.c \
+
		${.CURDIR}/../external/libelf/elf_update.c \
+
		${.CURDIR}/../external/libelf/elf_version.c \
+
		${.CURDIR}/../external/libelf/gelf_cap.c \
+
		${.CURDIR}/../external/libelf/gelf_checksum.c \
+
		${.CURDIR}/../external/libelf/gelf_dyn.c \
+
		${.CURDIR}/../external/libelf/gelf_ehdr.c \
+
		${.CURDIR}/../external/libelf/gelf_getclass.c \
+
		${.CURDIR}/../external/libelf/gelf_fsize.c \
+
		${.CURDIR}/../external/libelf/gelf_move.c \
+
		${.CURDIR}/../external/libelf/gelf_phdr.c \
+
		${.CURDIR}/../external/libelf/gelf_rel.c \
+
		${.CURDIR}/../external/libelf/gelf_rela.c \
+
		${.CURDIR}/../external/libelf/gelf_shdr.c \
+
		${.CURDIR}/../external/libelf/gelf_sym.c \
+
		${.CURDIR}/../external/libelf/gelf_syminfo.c \
+
		${.CURDIR}/../external/libelf/gelf_symshndx.c \
+
		${.CURDIR}/../external/libelf/gelf_xlate.c \
+
		${.CURDIR}/../external/libelf/libelf_align.c \
+
		${.CURDIR}/../external/libelf/libelf_allocate.c \
+
		${.CURDIR}/../external/libelf/libelf_ar.c \
+
		${.CURDIR}/../external/libelf/libelf_ar_util.c \
+
		${.CURDIR}/../external/libelf/libelf_checksum.c \
+
		${.CURDIR}/../external/libelf/libelf_data.c \
+
		${.CURDIR}/../external/libelf/libelf_ehdr.c \
+
		${.CURDIR}/../external/libelf/libelf_extended.c \
+
		${.CURDIR}/../external/libelf/libelf_memory.c \
+
		${.CURDIR}/../external/libelf/libelf_open.c \
+
		${.CURDIR}/../external/libelf/libelf_phdr.c \
+
		${.CURDIR}/../external/libelf/libelf_shdr.c \
+
		${.CURDIR}/../external/libelf/libelf_xlate.c \
+
		${GENSRCS}
+
GENSRCS=	libelf_fsize.c libelf_msize.c libelf_convert.c
+
CLEANFILES=	${GENSRCS}
+

+
libelf_convert.c:	${.CURDIR}/../external/libelf/elf_types.m4 ${.CURDIR}/../external/libelf/libelf_convert.m4
+
libelf_fsize.c:		${.CURDIR}/../external/libelf/elf_types.m4 ${.CURDIR}/../external/libelf/libelf_fsize.m4
+
libelf_msize.c:		${.CURDIR}/../external/libelf/elf_types.m4 ${.CURDIR}/../external/libelf/libelf_msize.m4
+

PC=		pkg.pc
PKGH=		pkg.h
-
SRCS=		${PC} \
+
LIBPKGSRCS=	${PC} \
		${PKGH} \
		backup.c \
		dns_utils.c \
@@ -24,6 +103,7 @@ SRCS= ${PC} \
		pkg_attributes.c \
		pkg_config.c \
		pkg_create.c \
+
		pkg_cudf.c \
		pkg_delete.c \
		pkg_elf.c \
		pkg_event.c \
@@ -32,6 +112,7 @@ SRCS= ${PC} \
		pkg_ports.c \
		pkg_printf.c \
		pkg_repo.c \
+
		pkg_solve.c \
		pkg_status.c \
		pkg_version.c \
		pkgdb.c \
@@ -77,53 +158,149 @@ realinstall: ${PC}
#
DEFAULT_MIRROR_TYPE?=	1

-
CFLAGS+=	-DDEFAULT_MIRROR_TYPE=${DEFAULT_MIRROR_TYPE}
-
CFLAGS+=	-std=c99
-
CFLAGS+=	-I. \
+
CCV!=	${CC} --version
+
.if ${CCV:M*clang*}
+
USING_CLANG=	yes
+
.endif
+

+
EXPATCFLAGS=	-I${.CURDIR}/../external/expat/ \
+
		-DHAVE_EXPAT_CONFIG_H
+

+
.if defined(USING_CLANG)
+
EXPATCFLAGS+=	-Wno-enum-conversion
+
.endif
+

+
UCLCFLAGS=	-I${.CURDIR}/../external/libucl/include \
+
		-I${.CURDIR}/../external/libucl/src \
+
		-I${.CURDIR}/../external/libucl/uthash \
+
		-Wno-pointer-sign \
+
		-Wno-strict-aliasing \
+
		-Wno-unused-function
+

+
OSNAME!=	uname -s
+
.if ${OSNAME} == FreeBSD
+
SQLITECFLAGS+=	-DHAVE_POSIX_FALLOCATE=1
+
.endif
+
SQLITECFLAGS+=	-DSTDC_HEADERS=1 \
+
		-DHAVE_SYS_TYPES_H=1 \
+
		-DHAVE_SYS_STAT_H=1 \
+
		-DHAVE_STDLIB_H=1 \
+
		-DHAVE_STRING_H=1 \
+
		-DHAVE_MEMORY_H=1 \
+
		-DHAVE_STRINGS_H=1 \
+
		-DHAVE_INTTYPES_H=1 \
+
		-DHAVE_STDINT_H=1 \
+
		-DHAVE_UNISTD_H=1 \
+
		-DHAVE_DLFCN_H=1 \
+
		-DHAVE_USLEEP=1 \
+
		-DHAVE_LOCALTIME_R=1 \
+
		-DHAVE_GMTIME_R=1 \
+
		-DHAVE_DECL_STRERROR_R=1 \
+
		-DHAVE_STRERROR_R=1 \
+
		-Wno-unused-variable \
+
		-Wno-unused-function \
+
		-Wno-strict-aliasing
+
# http://www.sqlite.org/compile.html
+
SQLITECFLAGS+=	-DSQLITE_OMIT_AUTOVACUUM \
+
		-DSQLITE_OMIT_BLOB_LITERAL \
+
		-DSQLITE_OMIT_DECLTYPE \
+
		-DSQLITE_OMIT_EXPLAIN \
+
		-DSQLITE_OMIT_DEPRECATED \
+
		-DSQLITE_OMIT_LOAD_EXTENSION \
+
		-DSQLITE_OMIT_PROGRESS_CALLBACK \
+
		-DSQLITE_OMIT_TCL_VARIABLE \
+
		-DSQLITE_OMIT_UTF16 \
+
		-DSQLITE_OMIT_CAT \
+
		-DSQLITE_OMIT_CHECK \
+
		-DSQLITE_OMIT_AUTOINIT \
+
		-DSQLITE_OMIT_COMPILEOPTION_DIAGS \
+
		-DSQLITE_OMIT_INTEGRITY_CHECK \
+
		-DSQLITE_OMIT_BUILTIN_TEST \
+
		-DSQLITE_OMIT_SHARED_CACHE \
+
		-DSQLITE_ENABLE_UNLOCK_NOTIFY=1 \
+
		-DUSE_PREAD \
+
		-DSQLITE_THREADSAFE=1 \
+
		-DSQLITE_TEMP_STORE=3 \
+
		-Dmain=sqlite3_shell \
+
		-DNDEBUG
+
YAMLCFLAGS=	-I${.CURDIR}/../external/libyaml/include \
+
		-I${.CURDIR}/../external/libyaml \
+
		-DHAVE_CONFIG_H \
+
		-Wno-strict-aliasing \
+
		-Wno-unused
+
LIBPKGCFLAGS+=	-DDEFAULT_MIRROR_TYPE=${DEFAULT_MIRROR_TYPE} \
+
		-std=gnu99 \
+
		-I. \
		-I${.CURDIR} \
		-I${.CURDIR}/../external/sqlite \
		-I${.CURDIR}/../external/uthash \
		-I${.CURDIR}/../external/libucl/include \
-
		-I${.CURDIR}/../external/libyaml/include
-

+
		-I${.CURDIR}/../external/libyaml/include \
+
		-DPREFIX=\"${PREFIX}\" \
+
		-Wno-pointer-sign
.if !exists(/usr/lib/libelf.so)
-
CFLAGS+=	-I${.CURDIR}/../external/libelf \
+
LIBPKGCFLAGS+=	-I${.CURDIR}/../external/libelf \
		-DBUNDLED_LIBELF
+
ELFCFLAGS+=	-I${.CURDIR}/../external/libelf
+
SRCS+=		${ELFSRCS}
+
.endif
+

+
.if defined(PKG_PORTSDIR)
+
LIBPKGCFLAGS+=	-DPORTSDIR=\"${PKG_PORTSDIR}\"
+
.endif
+

+
SRCS+=	${UCLSRCS} ${EXPATSRCS} ${SQLITESRCS} ${YAMLSRCS} ${LIBPKGSRCS}
+

+
.for c in ${UCLSRCS}
+
CFLAGS.${c}+=	${UCLCFLAGS}
+
.endfor
+

+
.for c in ${EXPATSRCS}  
+
CFLAGS.${c}+=	${EXPATCFLAGS}
+
.endfor
+

+
.for c in ${SQLITESRCS}
+
CFLAGS.${c}+=	${SQLITECFLAGS}
+
.endfor
+

+
.for c in ${YAMLSRCS}
+
CFLAGS.${c}+=	${YAMLCFLAGS}
+
.endfor
+

+
.for c in ${LIBPKGSRCS}
+
CFLAGS.${c}+=	${LIBPKGCFLAGS}
+
.endfor
+

+
.for c in ${ELFSRCS}
+
CFLAGS.${c}+=	${ELFCFLAGS}
+
.endfor
+

+

+
.if ${.TARGETS} == "depend"
+
CFLAGS+=	${UCLCFLAGS}  ${EXPATCFLAGS} ${SQLITECFLAGS} ${YAMLCFLAGS} ${LIBPKGCFLAGS}
.endif

+
CFLAGS+=	-Wall -Werror
+
CFLAGS+=	${CFLAGS.${.IMPSRC}}
+

LDADD+=		-L../external/sqlite \
		-L../external/libyaml \
		-L../external/libucl

-
.if !exists(/usr/lib/libelf.so)
-
LDADD+=		-L../external/libelf
-
DPADD+=		${.OBJDIR}/external/libelf/libelf.a
+
.if exists(/usr/lib/libelf.so)
+
LDADD+=		-lelf
.endif

-
LDADD+=		-lsqlite3 \
-
		-larchive \
+
LDADD+=		-larchive \
		-lsbuf \
		-lfetch \
-
		-lelf \
		-lutil \
-
		-lpthread \
-
		-lucl \
-
		-lyaml
-

-
DPADD+=		${.OBJDIR}/../external/sqlite/libsqlite3.a \
-
		${.OBJDIR}/../external/libyaml/libyaml.a \
-
		${.OBJDIR}/../external/libucl/libucl.a
+
		-lpthread

.if exists(/usr/include/edit/readline/readline.h)
LDADD+=		-ledit
.endif

-
CFLAGS+=	-DPREFIX=\"${PREFIX}\"
-

-
.if defined(PKG_PORTSDIR)
-
CFLAGS+=	-DPORTSDIR=\"${PKG_PORTSDIR}\"
-
.endif
-

.if defined(DEBUG_FLAGS)
.if ${DEBUG_FLAGS} == 1
DEBUG_FLAGS=	-ggdb -O0
@@ -137,7 +314,7 @@ DEBUG_FLAGS+= -pg

.include <bsd.lib.mk>

-
.SUFFIXES: .pc.in .pc .h.in .h
+
.SUFFIXES: .pc.in .pc .h.in .h .m4 .c

.PHONY:	${_snapshot}

@@ -151,11 +328,13 @@ DEBUG_FLAGS+= -pg
	    -e "s,__VERSION__,${PKGVERSION},g" \
	    ${.IMPSRC} > ${.TARGET}

+
.m4.c:
+
	(cd ${.CURDIR}/../external/libelf ; m4 -D SRCDIR=${.CURDIR}/../external/libelf ${.IMPSRC}) > ${.TARGET}

-
depend: staticdepend
-
.ORDER: ${DEPENDFILE} staticdepend
+
# Workaround 8.3/8.4 bug
+
OSVERSION!=    /sbin/sysctl -n kern.osreldate

-
staticdepend:
-
.if defined(DPADD) && !empty(DPADD)
-
	echo lib${LIB}.a: ${DPADD} >> ${DEPENDFILE}
+
.if ${OSNAME} == FreeBSD && ${OSVERSION} < 901000
+
.c.o:
+
	${CC} ${STATIC_CFLAGS} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET}
.endif
modified libpkg/dns_utils.c
@@ -122,7 +122,7 @@ dns_getsrvinfo(const char *zone)

	while(qdcount > 0 && p < end) {
		qdcount--;
-
		if((len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN)) < 0)
+
		if((len = dn_expand(q.buf, end, p, host, sizeof(host))) < 0)
			return (NULL);
		p += len + NS_QFIXEDSZ;
	}
@@ -134,7 +134,7 @@ dns_getsrvinfo(const char *zone)
	n = 0;
	while (ancount > 0 && p < end) {
		ancount--;
-
		len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN);
+
		len = dn_expand(q.buf, end, p, host, sizeof(host));
		if (len < 0) {
			for (i = 0; i < n; i++)
				free(res[i]);
@@ -158,7 +158,7 @@ dns_getsrvinfo(const char *zone)
		NS_GET16(weight, p);
		NS_GET16(port, p);

-
		len = dn_expand(q.buf, end, p, host, MAXHOSTNAMELEN);
+
		len = dn_expand(q.buf, end, p, host, sizeof(host));
		if (len < 0) {
			for (i = 0; i < n; i++)
				free(res[i]);
@@ -181,7 +181,7 @@ dns_getsrvinfo(const char *zone)
		res[n]->port = port;
		res[n]->next = NULL;
		res[n]->finalweight = 0;
-
		strlcpy(res[n]->host, host, MAXHOSTNAMELEN);
+
		strlcpy(res[n]->host, host, sizeof(res[n]->host));

		p += len;
		n++;
modified libpkg/fetch.c
@@ -265,7 +265,7 @@ static int
start_ssh(struct pkg_repo *repo, struct url *u, off_t *sz)
{
	char *line = NULL;
-
	ssize_t linecap = 0;
+
	size_t linecap = 0;
	size_t linelen;
	struct sbuf *cmd = NULL;
	const char *errstr;
@@ -483,7 +483,7 @@ pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest, time_t *t
		else if (repo != NULL && repo->mirror_type == HTTP && repo->http != NULL) {
			strlcpy(u->scheme, http_current->url->scheme, sizeof(u->scheme));
			strlcpy(u->host, http_current->url->host, sizeof(u->host));
-
			snprintf(docpath, MAXPATHLEN, "%s%s", http_current->url->doc, doc);
+
			snprintf(docpath, sizeof(docpath), "%s%s", http_current->url->doc, doc);
			u->doc = docpath;
			u->port = http_current->url->port;
		}
modified libpkg/packing.c
@@ -75,7 +75,9 @@ packing_init(struct packing **pack, const char *path, pkg_formats format)
		archive_write_set_format_pax_restricted((*pack)->awrite);
		ext = packing_set_format((*pack)->awrite, format);
		if (ext == NULL) {
+
			archive_read_close((*pack)->aread);
			archive_read_free((*pack)->aread);
+
			archive_write_close((*pack)->awrite);
			archive_write_free((*pack)->awrite);
			*pack = NULL;
			return EPKG_FATAL; /* error set by _set_format() */
@@ -88,7 +90,9 @@ packing_init(struct packing **pack, const char *path, pkg_formats format)
		    (*pack)->awrite, archive_path) != ARCHIVE_OK) {
			pkg_emit_errno("archive_write_open_filename",
			    archive_path);
+
			archive_read_close((*pack)->aread);
			archive_read_free((*pack)->aread);
+
			archive_write_close((*pack)->awrite);
			archive_write_free((*pack)->awrite);
			*pack = NULL;
			return EPKG_FATAL;
@@ -329,6 +333,7 @@ packing_finish(struct packing *pack)
{
	assert(pack != NULL);

+
	archive_read_close(pack->aread);
	archive_read_free(pack->aread);

	archive_write_close(pack->awrite);
modified libpkg/pkg.c
@@ -61,7 +61,7 @@ static struct _fields {
	[PKG_OLD_VERSION] = {"oldversion", PKG_REMOTE, 1},
	[PKG_REPONAME] = {"reponame", PKG_REMOTE, 1},
	[PKG_REPOURL] = {"repourl", PKG_REMOTE, 1},
-
	[PKG_DIGEST] = {"manifestdigest", PKG_REMOTE, 1},
+
	[PKG_DIGEST] = {"manifestdigest", PKG_REMOTE|PKG_INSTALLED, 1},
	[PKG_REASON] = {"reason", PKG_REMOTE, 1}
};

@@ -465,6 +465,22 @@ pkg_shlibs_provided(const struct pkg *pkg, struct pkg_shlib **s)
}

int
+
pkg_conflicts(const struct pkg *pkg, struct pkg_conflict **c)
+
{
+
	assert(pkg != NULL);
+

+
	HASH_NEXT(pkg->conflicts, (*c));
+
}
+

+
int
+
pkg_provides(const struct pkg *pkg, struct pkg_provide **c)
+
{
+
	assert(pkg != NULL);
+

+
	HASH_NEXT(pkg->provides, (*c));
+
}
+

+
int
pkg_annotations(const struct pkg *pkg, struct pkg_note **an)
{
	assert(pkg != NULL);
@@ -974,6 +990,55 @@ pkg_addshlib_provided(struct pkg *pkg, const char *name)
}

int
+
pkg_addconflict(struct pkg *pkg, const char *name)
+
{
+
	struct pkg_conflict *c = NULL;
+
	const char *origin;
+

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

+
	HASH_FIND_STR(pkg->conflicts, __DECONST(char *, name), c);
+
	/* silently ignore duplicates in case of conflicts */
+
	if (c != NULL)
+
		return (EPKG_OK);
+

+
	pkg_conflict_new(&c);
+
	sbuf_set(&c->origin, name);
+
	pkg_get(pkg, PKG_ORIGIN, &origin);
+
	pkg_debug(3, "Pkg: add a new conflict origin: %s, with %s", origin, name);
+

+
	HASH_ADD_KEYPTR(hh, pkg->conflicts,
+
	    __DECONST(char *, pkg_conflict_origin(c)),
+
	    sbuf_size(c->origin), c);
+

+
	return (EPKG_OK);
+
}
+

+
int
+
pkg_addprovide(struct pkg *pkg, const char *name)
+
{
+
	struct pkg_provide *p = NULL;
+

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

+
	HASH_FIND_STR(pkg->provides, __DECONST(char *, name), p);
+
	/* silently ignore duplicates in case of conflicts */
+
	if (p != NULL)
+
		return (EPKG_OK);
+

+
	pkg_provide_new(&p);
+
	sbuf_set(&p->provide, name);
+

+
	HASH_ADD_KEYPTR(hh, pkg->provides,
+
	    __DECONST(char *, pkg_provide_name(p)),
+
	    sbuf_size(p->provide), p);
+

+
	return (EPKG_OK);
+
}
+

+
int
pkg_addannotation(struct pkg *pkg, const char *tag, const char *value)
{
	struct pkg_note *an = NULL;
@@ -1064,6 +1129,10 @@ pkg_list_count(const struct pkg *pkg, pkg_list list)
		return (HASH_COUNT(pkg->shlibs_provided));
	case PKG_ANNOTATIONS:
		return (HASH_COUNT(pkg->annotations));
+
	case PKG_CONFLICTS:
+
		return (HASH_COUNT(pkg->conflicts));
+
	case PKG_PROVIDES:
+
		return (HASH_COUNT(pkg->provides));
	}
	
	return (0);
@@ -1120,6 +1189,14 @@ pkg_list_free(struct pkg *pkg, pkg_list list) {
		HASH_FREE(pkg->annotations, pkg_note, pkg_annotation_free);
		pkg->flags &= ~PKG_LOAD_ANNOTATIONS;
		break;
+
	case PKG_CONFLICTS:
+
		HASH_FREE(pkg->conflicts, pkg_conflict, pkg_conflict_free);
+
		pkg->flags &= ~PKG_LOAD_CONFLICTS;
+
		break;
+
	case PKG_PROVIDES:
+
		HASH_FREE(pkg->provides, pkg_provide, pkg_provide_free);
+
		pkg->flags &= ~PKG_LOAD_PROVIDES;
+
		break;
	}
}

@@ -1135,6 +1212,7 @@ pkg_open(struct pkg **pkg_p, const char *path, struct pkg_manifest_key *keys, in
	if (ret != EPKG_OK && ret != EPKG_END)
		return (EPKG_FATAL);

+
	archive_read_close(a);
	archive_read_free(a);

	return (EPKG_OK);
@@ -1153,9 +1231,8 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae,
	size_t		  size;
	off_t		  offset = 0;
	struct sbuf	**sbuf;
-
	int		  i, r, fd = -1;
+
	int		  i, r;
	bool		  read_from_stdin = 0;
-
	struct stat	  sb;

	struct {
		const char *name;
@@ -1179,43 +1256,10 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae,

	read_from_stdin = (strncmp(path, "-", 2) == 0);

-
	if (read_from_stdin) {
-
		fd = STDIN_FILENO;
-
	} else {
-
		fd = open(path, O_RDONLY);
-
		if (fd == -1) {
-
			pkg_emit_error("open(%s) failed: %s", path,
-
				       strerror(errno));
-
			retcode = EPKG_FATAL;
-
			goto cleanup;
-
		}
-
	}
-

-
	if (fstat(fd, &sb) == -1) {
-
		pkg_emit_error("fstat() %s: %s", path, strerror(errno));
-
		retcode = EPKG_FATAL;
-
		goto cleanup;
-
	}
-

-
	/* Is this a file type we can't read a package from?  S_ISLNK
-
	   shouldn't happen -- we should be resolving sym-links during
-
	   the call to open() -- so assume S_ISLNK means something
-
	   went wrong. S_ISBLK shouldn't happen on modern FreeBSD, so
-
	   we'll ignore it.  S_ISCHR would result from opening
-
	   /dev/stdin or /dev/fd/0 which should be fine.  Ditto
-
	   S_ISFIFO.  Not sure about S_ISSOCK or S_ISWHT (?) */
-

-
	if (S_ISDIR(sb.st_mode) || S_ISLNK(sb.st_mode)) {
-
		pkg_emit_error("can't read %s -- is a directory or broken link",
-
			       path);
-
		retcode = EPKG_FATAL;
-
		goto cleanup;
-
	}
-

-
	ret = archive_read_open_fd(*a, fd, 4096);
-
	if (ret != ARCHIVE_OK) {
-
		pkg_emit_error("archive_read_open_fd() %s: %s", path,
-
			       archive_error_string(*a));
+
	if (archive_read_open_filename(*a,
+
	    read_from_stdin ? NULL : path, 4096) != ARCHIVE_OK) {
+
		pkg_emit_error("archive_read_open_filename(%s): %s", path,
+
		    archive_error_string(*a));
		retcode = EPKG_FATAL;
		goto cleanup;
	}
@@ -1229,11 +1273,6 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae,

	pkg = *pkg_p;

-
	if (S_ISREG(sb.st_mode) && !read_from_stdin) 
-
		pkg->type = PKG_FILE;
-
	else
-
		pkg->type = PKG_STREAM;
-

	while ((ret = archive_read_next_header(*a, ae)) == ARCHIVE_OK) {
		fpath = archive_entry_pathname(*ae);
		if (fpath[0] != '+')
@@ -1242,7 +1281,7 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae,
		if (!manifest &&
			(flags & PKG_OPEN_MANIFEST_COMPACT) &&
			strcmp(fpath, "+COMPACT_MANIFEST") == 0) {
-
			unsigned char *buffer;
+
			char *buffer;
			manifest = true;

			size_t len = archive_entry_size(*ae);
@@ -1259,7 +1298,7 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae,
		}
		if (!manifest && strcmp(fpath, "+MANIFEST") == 0) {
			manifest = true;
-
			unsigned char *buffer;
+
			char *buffer;

			size_t len = archive_entry_size(*ae);
			buffer = malloc(len);
@@ -1307,11 +1346,8 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae,
		retcode = EPKG_FATAL;
	}

-
	if (ret == ARCHIVE_EOF) {
-
		if (!read_from_stdin) 
-
			close(fd);
+
	if (ret == ARCHIVE_EOF)
		retcode = EPKG_END;
-
	}

	if (!manifest) {
		retcode = EPKG_FATAL;
@@ -1320,12 +1356,12 @@ pkg_open2(struct pkg **pkg_p, struct archive **a, struct archive_entry **ae,

	cleanup:
	if (retcode != EPKG_OK && retcode != EPKG_END) {
-
		if (*a != NULL)
+
		if (*a != NULL) {
+
			archive_read_close(*a);
			archive_read_free(*a);
+
		}
		*a = NULL;
		*ae = NULL;
-
		if (!read_from_stdin && fd >= 0) 
-
			close(fd);
	}

	return (retcode);
@@ -1337,8 +1373,8 @@ pkg_copy_tree(struct pkg *pkg, const char *src, const char *dest)
	struct packing *pack;
	struct pkg_file *file = NULL;
	struct pkg_dir *dir = NULL;
-
	char spath[MAXPATHLEN + 1];
-
	char dpath[MAXPATHLEN + 1];
+
	char spath[MAXPATHLEN];
+
	char dpath[MAXPATHLEN];
	bool disable_mtree;
	const char *prefix;
	char *mtree;
modified libpkg/pkg.h.in
@@ -58,6 +58,7 @@

struct pkg;
struct pkg_dep;
+
struct pkg_conflict;
struct pkg_file;
struct pkg_dir;
struct pkg_category;
@@ -67,11 +68,13 @@ struct pkg_user;
struct pkg_group;
struct pkg_shlib;
struct pkg_note;
+
struct pkg_provide;

struct pkgdb;
struct pkgdb_it;

struct pkg_jobs;
+
struct pkg_solve_problem;

struct pkg_repo;

@@ -275,6 +278,8 @@ typedef enum {
	PKG_SHLIBS_REQUIRED,
	PKG_SHLIBS_PROVIDED,
	PKG_ANNOTATIONS,
+
	PKG_CONFLICTS,
+
	PKG_PROVIDES
} pkg_list;

typedef enum {
@@ -361,6 +366,8 @@ typedef enum _pkg_config_key {
	PKG_CONFIG_DISABLE_MTREE,
	PKG_CONFIG_DEBUG_LEVEL,
	PKG_CONFIG_ALIAS,
+
	PKG_CONFIG_CUDF_SOLVER,
+
	PKG_CONFIG_SAT_SOLVER,
} pkg_config_key;

typedef enum {
@@ -622,6 +629,20 @@ int pkg_shlibs_required(const struct pkg *pkg, struct pkg_shlib **shlib);
int pkg_shlibs_provided(const struct pkg *pkg, struct pkg_shlib **shlib);

/**
+
 * Iterates over the conflicts registered in the package.
+
 * @param conflict must be set to NULL for the first call.
+
 * @return An error code
+
 */
+
int pkg_conflicts(const struct pkg *pkg, struct pkg_conflict **conflict);
+

+
/**
+
 * Iterates over the provides registered in the package.
+
 * @param provide must be set to NULL for the first call.
+
 * @return An error code
+
 */
+
int pkg_provides(const struct pkg *pkg, struct pkg_provide **provide);
+

+
/**
 * Iterates over the annotations associated with the package.
 * @param note must be set to NULL for the first call.
 * @return An error code
@@ -786,18 +807,18 @@ int pkg_appendscript(struct pkg *pkg, const char *cmd, pkg_script type);
int pkg_addoption(struct pkg *pkg, const char *name, const char *value);

/**
-
 * Add a default value to an already exsting struct pkg_option
-
 * @return An error code.
+
 * Add a default value to an already existing struct pkg_option
+
 * @return An error code
 */
int pkg_addoption_default(struct pkg *pkg, const char *key,
-
			  const char *default_value);
+
                          const char *default_value);

/**
 * Add a description to an already exsting struct pkg_option
 * @return An error code.
 */
int pkg_addoption_description(struct pkg *pkg, const char *key,
-
			      const char *description);
+
                              const char *description);

/**
 * Add a shared library used by this package
@@ -812,6 +833,18 @@ int pkg_addshlib_required(struct pkg *pkg, const char *name);
int pkg_addshlib_provided(struct pkg *pkg, const char *name);

/**
+
 * Add a conflict registered with this package
+
 * @return An error code.
+
 */
+
int pkg_addconflict(struct pkg *pkg, const char *name);
+

+
/**
+
 * Add a provide registered with this package
+
 * @return An error code.
+
 */
+
int pkg_addprovide(struct pkg *pkg, const char *name);
+

+
/**
 * Add annotation key+value pair
 * @return An error code
 */
@@ -904,6 +937,12 @@ const char *pkg_option_description(struct pkg_option const * const);
/* pkg_shlib */
const char *pkg_shlib_name(struct pkg_shlib const * const);

+
/* pkg_conflict */
+
const char *pkg_conflict_origin(const struct pkg_conflict *);
+

+
/* pkg_provide */
+
const char *pkg_provide_name(const struct pkg_provide *);
+

/* pkg_note */
const char *pkg_annotation_tag(struct pkg_note const * const);
const char *pkg_annotation_value(struct pkg_note const * const);
@@ -1029,6 +1068,9 @@ struct pkgdb_it * pkgdb_query_which(struct pkgdb *db, const char *path, bool glo
struct pkgdb_it * pkgdb_query_shlib_required(struct pkgdb *db, const char *shlib);
struct pkgdb_it * pkgdb_query_shlib_provided(struct pkgdb *db, const char *shlib);

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

/**
 * Add/Modify/Delete an annotation for a package
 * @param key -- tag for the annotation
@@ -1057,6 +1099,8 @@ int pkgdb_delete_annotation(struct pkgdb *db, struct pkg *pkg,
#define PKG_LOAD_SHLIBS_REQUIRED	(1U << 11)
#define PKG_LOAD_SHLIBS_PROVIDED	(1U << 12)
#define PKG_LOAD_ANNOTATIONS		(1U << 13)
+
#define PKG_LOAD_CONFLICTS		(1U << 14)
+
#define PKG_LOAD_PROVIDES		(1U << 15)
/* Make sure new PKG_LOAD don't conflict with PKG_CONTAINS_* */

/**
@@ -1133,7 +1177,8 @@ int pkg_jobs_count(struct pkg_jobs *jobs);
 * @param pkg Must be set to NULL for the first call.
 * @return An error code.
 */
-
int pkg_jobs(struct pkg_jobs *jobs, struct pkg **pkg);
+
int pkg_jobs_add_iter(struct pkg_jobs *jobs, struct pkg **pkg);
+
int pkg_jobs_delete_iter(struct pkg_jobs *jobs, struct pkg **pkg);

/**
 * Apply the jobs in the queue (fetch and install).
@@ -1142,6 +1187,53 @@ int pkg_jobs(struct pkg_jobs *jobs, struct pkg **pkg);
int pkg_jobs_apply(struct pkg_jobs *jobs);

/**
+
 * Emit CUDF spec to a file for a specified jobs request
+
 * @return error code
+
 */
+
int pkg_jobs_cudf_emit_file(struct pkg_jobs *, pkg_jobs_t , FILE *);
+

+
/**
+
 * Parse the output of an external CUDF solver
+
 * @return error code
+
 */
+
int pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f);
+

+
/**
+
 * Solve a SAT problem
+
 * @return true if a problem is solvable
+
 */
+
bool pkg_solve_sat_problem(struct pkg_solve_problem *problem);
+

+
/**
+
 * Convert package jobs to a SAT problem
+
 * @return SAT problem or NULL if failed
+
 */
+
struct pkg_solve_problem * pkg_solve_jobs_to_sat(struct pkg_jobs *j);
+

+
/**
+
 * Export sat problem to the DIMACS format
+
 * @return error code
+
 */
+
int pkg_solve_dimacs_export(struct pkg_solve_problem *problem, FILE *f);
+

+
/**
+
 * Move solved problem to the jobs structure
+
 * @return error code
+
 */
+
int pkg_solve_sat_to_jobs(struct pkg_solve_problem *problem, struct pkg_jobs *j);
+

+
/**
+
 * Parse SAT solver output and convert it to jobs
+
 * @return error code
+
 */
+
int pkg_solve_parse_sat_output(FILE *f, struct pkg_solve_problem *problem, struct pkg_jobs *j);
+

+
/**
+
 * Free a SAT problem structure
+
 */
+
void pkg_solve_problem_free(struct pkg_solve_problem *problem);
+

+
/**
 * Archive formats options.
 */
typedef enum pkg_formats { TAR, TGZ, TBZ, TXZ } pkg_formats;
modified libpkg/pkg_add.c
@@ -46,7 +46,7 @@ do_extract(struct archive *a, struct archive_entry *ae)
{
	int	retcode = EPKG_OK;
	int	ret = 0;
-
	char	path[MAXPATHLEN + 1];
+
	char	path[MAXPATHLEN];
	struct stat st;

	do {
@@ -163,7 +163,7 @@ pkg_add(struct pkgdb *db, const char *path, unsigned flags, struct pkg_manifest_
	bool		 extract = true;
	bool		 handle_rc = false;
	bool		 disable_mtree;
-
	char		 dpath[MAXPATHLEN + 1];
+
	char		 dpath[MAXPATHLEN];
	const char	*basedir;
	const char	*ext;
	char		*mtree;
@@ -269,6 +269,13 @@ pkg_add(struct pkgdb *db, const char *path, unsigned flags, struct pkg_manifest_
					retcode = EPKG_FATAL;
					goto cleanup;
				}
+
			} else {
+
				pkg_emit_error("Missing dependency matching "
+
				    "Origin: '%s' Version: '%s'",
+
				    pkg_dep_get(dep, PKG_DEP_ORIGIN),
+
				    pkg_dep_get(dep, PKG_DEP_VERSION));
+
				retcode = EPKG_FATAL;
+
				goto cleanup;
			}
		} else {
			retcode = EPKG_FATAL;
@@ -344,8 +351,10 @@ pkg_add(struct pkgdb *db, const char *path, unsigned flags, struct pkg_manifest_
		pkg_emit_install_finished(pkg);

	cleanup:
-
	if (a != NULL)
+
	if (a != NULL) {
+
		archive_read_close(a);
		archive_read_free(a);
+
	}

	pkg_free(pkg);

modified libpkg/pkg_attributes.c
@@ -451,6 +451,68 @@ pkg_shlib_name(struct pkg_shlib const * const sl)
}

/*
+
 * Conflicts
+
 */
+

+
int
+
pkg_conflict_new(struct pkg_conflict **c)
+
{
+
	if ((*c = calloc(1, sizeof(struct pkg_conflict))) == NULL)
+
		return (EPKG_FATAL);
+

+
	return (EPKG_OK);
+
}
+

+
void
+
pkg_conflict_free(struct pkg_conflict *c)
+
{
+
	if (c == NULL)
+
		return;
+

+
	sbuf_free(c->origin);
+
	free(c);
+
}
+

+
const char *
+
pkg_conflict_origin(const struct pkg_conflict *c)
+
{
+
	assert(c != NULL);
+

+
	return (sbuf_get(c->origin));
+
}
+

+
/*
+
 * Provides
+
 */
+
int
+
pkg_provide_new(struct pkg_provide **c)
+
{
+
	if ((*c = calloc(1, sizeof(struct pkg_provide))) == NULL)
+
		return (EPKG_FATAL);
+

+
	return (EPKG_OK);
+
}
+

+
void
+
pkg_provide_free(struct pkg_provide *c)
+
{
+
	if (c == NULL)
+
		return;
+

+
	sbuf_free(c->provide);
+
	free(c);
+
}
+

+
const char *
+
pkg_provide_name(const struct pkg_provide *c)
+
{
+
	assert(c != NULL);
+

+
	return (sbuf_get(c->provide));
+
}
+

+

+
/*
 * Annotations
 */

modified libpkg/pkg_config.c
@@ -31,6 +31,7 @@
#include <ctype.h>
#include <dirent.h>
#include <dlfcn.h>
+
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
@@ -287,12 +288,25 @@ static struct config_entry c[] = {
		NULL,
		"Command aliases",
	},
+
	[PKG_CONFIG_CUDF_SOLVER] = {
+
		PKG_CONFIG_STRING,
+
		"CUDF_SOLVER",
+
		NULL,
+
		"Experimental: tells pkg to use an external CUDF solver",
+
	},
+
	[PKG_CONFIG_SAT_SOLVER] = {
+
		PKG_CONFIG_STRING,
+
		"SAT_SOLVER",
+
		NULL,
+
		"Experimental: tells pkg to use an external SAT solver",
+
	},
};

static bool parsed = false;
static size_t c_size = NELEM(c);

static void		 pkg_config_kv_free(struct pkg_config_kv *);
+
static void		 pkg_config_value_free(struct pkg_config_value *);
static struct pkg_repo	*pkg_repo_new(const char *name, const char *url);

static void
@@ -438,8 +452,11 @@ pkg_object_walk(ucl_object_t *obj, struct pkg_config *conf_by_key)
					    " ignoring...", key);
					continue;
				}
-
				if (!conf->fromenv)
+
				if (!conf->fromenv) {
+
					HASH_FREE(conf->list, pkg_config_value, pkg_config_value_free);
+
					conf->list = NULL;
					obj_walk_array(cur, conf);
+
				}
				break;
			case PKG_CONFIG_KVLIST:
				if (cur->type != UCL_OBJECT) {
@@ -447,8 +464,11 @@ pkg_object_walk(ucl_object_t *obj, struct pkg_config *conf_by_key)
					    " ignoring...", key);
					continue;
				}
-
				if (!conf->fromenv)
+
				if (!conf->fromenv) {
+
					HASH_FREE(conf->kvlist, pkg_config_kv, pkg_config_kv_free);
+
					conf->kvlist = NULL;
					obj_walk_object(cur, conf);
+
				}
				break;
			}
		}
@@ -481,35 +501,6 @@ subst_packagesite_str(const char *oldstr)
	return res;
}

-
/**
-
 * @brief Substitute PACKAGESITE variables
-
 */
-
static void
-
subst_packagesite(void)
-
{
-
	const char *oldval;
-
	char *newval;
-

-
	struct pkg_config *conf;
-
	pkg_config_key k = PKG_CONFIG_REPO;
-

-
	HASH_FIND_INT(config, &k, conf);
-

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

-
	oldval = conf->string;
-

-
	if (oldval == NULL || strstr(oldval, ABI_VAR_STRING) == NULL)
-
		return;
-

-
	newval = subst_packagesite_str(oldval);
-
	if (newval != NULL) {
-
		free(conf->string);
-
		conf->string = newval;
-
	}
-
}
-

int
pkg_initialized(void)
{
@@ -697,6 +688,7 @@ add_repo(ucl_object_t *obj, struct pkg_repo *r, const char *rname)
	const char *signature_type = NULL, *fingerprints = NULL;
	const char *key;

+
	pkg_debug(1, "PkgConfig: parsing repository object %s", rname);
	while ((cur = ucl_iterate_object(obj, &it, true))) {
		key = ucl_object_key(cur);
		if (key == NULL)
@@ -771,11 +763,6 @@ add_repo(ucl_object_t *obj, struct pkg_repo *r, const char *rname)
	if (r == NULL)
		r = pkg_repo_new(rname, url);

-
	if (r == NULL && url != NULL) {
-
		free(r->url);
-
		r->url = subst_packagesite_str(url);
-
	}
-

	if (signature_type != NULL) {
		if (strcasecmp(signature_type, "pubkey") == 0)
			r->signature_type = SIG_PUBKEY;
@@ -809,7 +796,7 @@ add_repo(ucl_object_t *obj, struct pkg_repo *r, const char *rname)
}

static void
-
walk_repo_obj(ucl_object_t *obj)
+
walk_repo_obj(ucl_object_t *obj, const char *file)
{
	ucl_object_t *cur;
	ucl_object_iter_t it = NULL;
@@ -820,10 +807,15 @@ walk_repo_obj(ucl_object_t *obj)
		key = ucl_object_key(cur);
		if (key == NULL)
			continue;
+
		pkg_debug(1, "PkgConfig: parsing key '%s'", key);
		r = pkg_repo_find_ident(key);
		if (r != NULL)
			pkg_debug(1, "PkgConfig: overwriting repository %s", key);
-
		add_repo(cur, r, key);
+
		if (cur->type == UCL_OBJECT)
+
			add_repo(cur, r, key);
+
		else
+
			pkg_emit_error("Ignoring bad configuration entry in %s: %s",
+
			    file, ucl_object_emit(cur, UCL_EMIT_YAML));
	}
}

@@ -831,14 +823,19 @@ static void
load_repo_file(const char *repofile)
{
	struct ucl_parser *p;
-
	ucl_object_t *obj = NULL, *cur;
-
	ucl_object_iter_t it = NULL;
+
	ucl_object_t *obj = NULL;
	bool fallback = false;
+
	const char *myarch;

	p = ucl_parser_new(0);

+
	pkg_config_string(PKG_CONFIG_ABI, &myarch);
+
	ucl_parser_register_variable (p, "ABI", myarch);
+

+
	pkg_debug(1, "PKgConfig: loading %s", repofile);
	if (!ucl_parser_add_file(p, repofile)) {
-
		printf("%s\n", ucl_parser_get_error(p));
+
		pkg_emit_error("Error parsing: %s: %s", repofile,
+
		    ucl_parser_get_error(p));
		if (errno == ENOENT) {
			ucl_parser_free(p);
			return;
@@ -846,25 +843,7 @@ load_repo_file(const char *repofile)
		fallback = true;
	}

-
	if (!fallback) {
-
		obj = ucl_parser_get_object(p);
-
		if (obj->type == UCL_OBJECT) {
-
			while ((cur = ucl_iterate_object(obj, &it, true))) {
-
				if (cur->type != UCL_OBJECT)
-
					fallback = true;
-
				if (fallback)
-
					break;
-
			}
-
		} else {
-
			fallback = true;
-
		}
-
	}
-

	if (fallback) {
-
		if (obj != NULL) {
-
			ucl_object_free(obj);
-
			ucl_parser_free(p);
-
		}
		obj = yaml_to_ucl(repofile, NULL, 0);
		if (obj == NULL)
			return;
@@ -881,8 +860,10 @@ load_repo_file(const char *repofile)
		    repofile);
	}

+
	obj = ucl_parser_get_object(p);
+

	if (obj->type == UCL_OBJECT)
-
		walk_repo_obj(obj);
+
		walk_repo_obj(obj, repofile);

	ucl_object_free(obj);
}
@@ -905,7 +886,10 @@ load_repo_files(const char *repodir)
			continue;
		p = &ent->d_name[n - 5];
		if (strcmp(p, ".conf") == 0) {
-
			snprintf(path, MAXPATHLEN, "%s/%s", repodir, ent->d_name);
+
			snprintf(path, sizeof(path), "%s%s%s",
+
			    repodir,
+
			    repodir[strlen(repodir) - 1] == '/' ? "" : "/",
+
			    ent->d_name);
			load_repo_file(path);
		}
	}
@@ -949,6 +933,7 @@ load_repositories(const char *repodir)
		load_repo_files(pkg_config_value(v));
}

+

int
pkg_init(const char *path, const char *reposdir)
{
@@ -985,7 +970,8 @@ pkg_init(const char *path, const char *reposdir)
		switch (c[i].type) {
		case PKG_CONFIG_STRING:
			if (val != NULL) {
-
				conf->string = strdup(val);
+
				if (strcmp(c[i].key, "PACKAGESITE") == 0)
+
					conf->string = subst_packagesite_str(val);
				conf->fromenv = true;
			}
			else if (c[i].def != NULL)
@@ -1162,8 +1148,6 @@ parsed:

	pkg_debug(1, "%s", "pkg initialized");

-
	subst_packagesite();
-

	/* Start the event pipe */
	pkg_config_string(PKG_CONFIG_EVENT_PIPE, &evpipe);
	if (evpipe != NULL)
@@ -1216,6 +1200,9 @@ pkg_config_kv_free(struct pkg_config_kv *k)
static void
pkg_config_value_free(struct pkg_config_value *v)
{
+
	if (v == NULL)
+
		return;
+

	free(v->value);
	free(v);
}
modified libpkg/pkg_create.c
@@ -44,7 +44,7 @@ static int
pkg_create_from_dir(struct pkg *pkg, const char *root,
    struct packing *pkg_archive)
{
-
	char		 fpath[MAXPATHLEN + 1];
+
	char		 fpath[MAXPATHLEN];
	struct pkg_file	*file = NULL;
	struct pkg_dir	*dir = NULL;
	char		*m;
added libpkg/pkg_cudf.c
@@ -0,0 +1,395 @@
+
/*-
+
 * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
+
 * All rights reserved.
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 *
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

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

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

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

+
/*
+
 * CUDF does not support packages with '_' in theirs names, therefore
+
 * use this ugly function to replace '_' to '@'
+
 */
+
static inline int
+
cudf_print_package_name(FILE *f, const char *name)
+
{
+
	const char *p, *c;
+
	int r = 0;
+

+
	p = c = name;
+
	while (*p) {
+
		if (*p == '_') {
+
			r += fprintf(f, "%.*s", (int)(p - c), c);
+
			fputc('@', f);
+
			r ++;
+
			c = p + 1;
+
		}
+
		p ++;
+
	}
+
	if (p > c) {
+
		r += fprintf(f, "%.*s", (int)(p - c), c);
+
	}
+

+
	return r;
+
}
+

+
static inline int
+
cudf_print_element(FILE *f, const char *line, bool has_next, int *column)
+
{
+
	int ret = 0;
+
	if (*column > 80) {
+
		*column = 0;
+
		ret += fprintf(f, "\n ");
+
	}
+

+
	ret += cudf_print_package_name(f, line);
+

+
	if (has_next)
+
		ret += fprintf(f, ", ");
+
	else
+
		ret += fprintf(f, "\n");
+

+
	if (ret > 0)
+
		*column += ret;
+

+
	return (ret);
+
}
+

+
static int
+
cudf_emit_pkg(struct pkg *pkg, int version, FILE *f)
+
{
+
	const char *origin;
+
	struct pkg_dep *dep, *dtmp;
+
	struct pkg_provide *prov, *ptmp;
+
	struct pkg_conflict *conflict, *ctmp;
+
	int column = 0;
+

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

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

+
	if (fprintf(f, "\nversion: %d\n", version) < 0)
+
		return (EPKG_FATAL);
+

+
	if (HASH_COUNT(pkg->deps) > 0) {
+
		if (fprintf(f, "depends: ") < 0)
+
			return (EPKG_FATAL);
+
		HASH_ITER(hh, pkg->deps, dep, dtmp) {
+
			if (cudf_print_element(f, pkg_dep_get(dep, PKG_DEP_ORIGIN),
+
					(dep->hh.next != NULL), &column) < 0) {
+
				return (EPKG_FATAL);
+
			}
+
		}
+
	}
+

+
	column = 0;
+
	if (HASH_COUNT(pkg->provides) > 0) {
+
		if (fprintf(f, "provides: ") < 0)
+
			return (EPKG_FATAL);
+
		HASH_ITER(hh, pkg->provides, prov, ptmp) {
+
			if (cudf_print_element(f, pkg_provide_name(prov),
+
					(prov->hh.next != NULL), &column) < 0) {
+
				return (EPKG_FATAL);
+
			}
+
		}
+
	}
+

+
	column = 0;
+
	if (HASH_COUNT(pkg->conflicts) > 0) {
+
		if (fprintf(f, "conflicts: ") < 0)
+
			return (EPKG_FATAL);
+
		HASH_ITER(hh, pkg->conflicts, conflict, ctmp) {
+
			if (cudf_print_element(f, pkg_conflict_origin(conflict),
+
					(conflict->hh.next != NULL), &column) < 0) {
+
				return (EPKG_FATAL);
+
			}
+
		}
+
	}
+

+
	if (fprintf(f, "installed: %s\n\n", pkg->type == PKG_INSTALLED ?
+
			"true" : "false") < 0)
+
		return (EPKG_FATAL);
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
cudf_emit_request_packages(const char *op, struct pkg_jobs *j, FILE *f)
+
{
+
	struct pkg_job_request *req, *tmp;
+
	const char *origin;
+
	int column = 0;
+
	bool printed = false;
+

+
	if (fprintf(f, "%s: ", op) < 0)
+
		return (EPKG_FATAL);
+
	HASH_ITER(hh, j->request_add, req, tmp) {
+
		if (req->skip)
+
			continue;
+
		pkg_get(req->pkg, PKG_ORIGIN, &origin);
+
		if (cudf_print_element(f, origin,
+
				(req->hh.next != NULL), &column) < 0) {
+
			return (EPKG_FATAL);
+
		}
+
		printed = true;
+
	}
+

+
	if (!printed)
+
		if (fputc('\n', f) < 0)
+
			return (EPKG_FATAL);
+

+
	column = 0;
+
	printed = false;
+
	if (fprintf(f, "remove: ") < 0)
+
		return (EPKG_FATAL);
+
	HASH_ITER(hh, j->request_delete, req, tmp) {
+
		if (req->skip)
+
			continue;
+
		pkg_get(req->pkg, PKG_ORIGIN, &origin);
+
		if (cudf_print_element(f, origin,
+
				(req->hh.next != NULL), &column) < 0) {
+
			return (EPKG_FATAL);
+
		}
+
		printed = true;
+
	}
+

+
	if (!printed)
+
		if (fputc('\n', f) < 0)
+
			return (EPKG_FATAL);
+

+
	return (EPKG_OK);
+
}
+

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

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

+
	return (pkg_version_cmp(vera, verb));
+
}
+

+
int
+
pkg_jobs_cudf_emit_file(struct pkg_jobs *j, pkg_jobs_t t, FILE *f)
+
{
+
	struct pkg *pkg;
+
	struct pkg_job_universe_item *it, *itmp, *icur;
+
	int version;
+

+
	if (fprintf(f, "preamble: \n\n") < 0)
+
		return (EPKG_FATAL);
+

+
	HASH_ITER(hh, j->universe, it, itmp) {
+
		LL_SORT(it, pkg_cudf_version_cmp);
+
		version = 1;
+
		LL_FOREACH(it, icur) {
+
			pkg = icur->pkg;
+
			if (cudf_emit_pkg(pkg, version++, f) != EPKG_OK)
+
				return (EPKG_FATAL);
+
		}
+
	}
+

+
	if (fprintf(f, "request: \n") < 0)
+
			return (EPKG_FATAL);
+

+
	switch (t) {
+
	case PKG_JOBS_FETCH:
+
	case PKG_JOBS_INSTALL:
+
	case PKG_JOBS_DEINSTALL:
+
	case PKG_JOBS_AUTOREMOVE:
+
		if (cudf_emit_request_packages("install", j, f) != EPKG_OK)
+
			return (EPKG_FATAL);
+
		break;
+
	case PKG_JOBS_UPGRADE:
+
		if (cudf_emit_request_packages("upgrade", j, f) != EPKG_OK)
+
			return (EPKG_FATAL);
+
		break;
+
	}
+
	return (EPKG_OK);
+
}
+

+
/*
+
 * Perform backward conversion of an origin replacing '@' to '_'
+
 */
+
static char *
+
cudf_strdup(const char *in)
+
{
+
	size_t len = strlen(in);
+
	char *out, *d;
+
	const char *s;
+

+
	out = malloc(len + 1);
+
	if (out == NULL)
+
		return (NULL);
+

+
	s = in;
+
	d = out;
+
	while (isspace(*s))
+
		s++;
+
	while (*s) {
+
		if (!isspace(*s))
+
			*d++ = (*s == '@') ? '_' : *s;
+
		s++;
+
	}
+

+
	*d = '\0';
+
	return (out);
+
}
+

+
struct pkg_cudf_entry {
+
	char *origin;
+
	bool was_installed;
+
	bool installed;
+
	char *version;
+
};
+

+
static int
+
pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
+
{
+
	struct pkg_job_universe_item *it, *cur, *selected = NULL;
+
	const char *origin;
+
	int ver;
+

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

+
	/*
+
	 * Now we need to select an appropriate version. We assume that
+
	 * the order of packages in list is the same as was passed to the
+
	 * cudf solver.
+
	 */
+
	ver = strtoul(entry->version, NULL, 10);
+

+
	LL_FOREACH(it, cur) {
+
		if (--ver == 0) {
+
			selected = cur;
+
			break;
+
		}
+
	}
+

+
	if (selected == NULL) {
+
		pkg_emit_error("package %s is found in CUDF output but the universe has no such version", entry->origin);
+
		return (EPKG_FATAL);
+
	}
+

+
	pkg_get(selected->pkg, PKG_ORIGIN, &origin);
+
	/* XXX: handle forced versions here including reinstall */
+
	if (entry->installed && selected->pkg->type != PKG_INSTALLED)
+
		HASH_ADD_KEYPTR(hh, j->jobs_add, origin, strlen(origin), selected->pkg);
+
	else if (!entry->installed && selected->pkg->type == PKG_INSTALLED)
+
		HASH_ADD_KEYPTR(hh, j->jobs_delete, origin, strlen(origin), selected->pkg);
+

+
	return (EPKG_OK);
+
}
+

+
int
+
pkg_jobs_cudf_parse_output(struct pkg_jobs *j, FILE *f)
+
{
+
	char *line = NULL, *begin, *param, *value;
+
	size_t linecap = 0;
+
	ssize_t linelen;
+
	struct pkg_cudf_entry cur_pkg;
+

+
	memset(&cur_pkg, 0, sizeof(cur_pkg));
+

+
	while ((linelen = getline(&line, &linecap, f)) > 0) {
+
		/* Split line, cut spaces */
+
		begin = line;
+
		param = strsep(&begin, ": \t");
+
		value = begin;
+
		while(begin != NULL)
+
			value = strsep(&begin, " \t");
+

+
		if (strcmp(param, "package") == 0) {
+
			if (cur_pkg.origin != NULL) {
+
				if (!pkg_jobs_cudf_add_package(j, &cur_pkg))  {
+
					free(line);
+
					return (EPKG_FATAL);
+
				}
+
			}
+
			cur_pkg.origin = cudf_strdup(value);
+
			cur_pkg.was_installed = false;
+
			cur_pkg.installed = false;
+
			cur_pkg.version = NULL;
+
		}
+
		else if (strcmp(param, "version") == 0) {
+
			if (cur_pkg.origin == NULL) {
+
				free(line);
+
				return (EPKG_FATAL);
+
			}
+
			cur_pkg.version = cudf_strdup(value);
+
		}
+
		else if (strcmp(param, "installed") == 0) {
+
			if (cur_pkg.origin == NULL) {
+
				free(line);
+
				return (EPKG_FATAL);
+
			}
+
			if (strncmp(value, "true", 4) == 0)
+
				cur_pkg.installed = true;
+
		}
+
		else if (strcmp(param, "was-installed") == 0) {
+
			if (cur_pkg.origin == NULL) {
+
				free(line);
+
				return (EPKG_FATAL);
+
			}
+
			if (strncmp(value, "true", 4) == 0)
+
				cur_pkg.was_installed = true;
+
		}
+
	}
+

+
	if (cur_pkg.origin != NULL) {
+
		if (!pkg_jobs_cudf_add_package(j, &cur_pkg))  {
+
			free(line);
+
			return (EPKG_FATAL);
+
		}
+
	}
+

+
	if (line != NULL)
+
		free(line);
+

+
	return (EPKG_OK);
+
}
modified libpkg/pkg_elf.c
@@ -427,9 +427,9 @@ pkg_analyse_files(struct pkgdb *db, struct pkg *pkg, const char *stage)

	while (pkg_files(pkg, &file) == EPKG_OK) {
		if (stage != NULL)
-
			snprintf(fpath, MAXPATHLEN, "%s/%s", stage, pkg_file_path(file));
+
			snprintf(fpath, sizeof(fpath), "%s/%s", stage, pkg_file_path(file));
		else
-
			strlcpy(fpath, pkg_file_path(file), MAXPATHLEN);
+
			strlcpy(fpath, pkg_file_path(file), sizeof(fpath));

		ret = analyse_elf(pkg, fpath, action, db);
		if (developer) {
@@ -466,7 +466,7 @@ pkg_register_shlibs(struct pkg *pkg, const char *root)

	while(pkg_files(pkg, &file) == EPKG_OK) {
		if (root != NULL) {
-
			snprintf(fpath, MAXPATHLEN, "%s%s", root, pkg_file_path(file));
+
			snprintf(fpath, sizeof(fpath), "%s%s", root, pkg_file_path(file));
			analyse_elf(pkg, fpath, add_shlibs_to_pkg, NULL);
		} else
			analyse_elf(pkg, pkg_file_path(file), add_shlibs_to_pkg, NULL);
modified libpkg/pkg_event.c
@@ -41,7 +41,7 @@ static char *
sbuf_json_escape(struct sbuf *buf, const char *str)
{
	sbuf_clear(buf);
-
	while (*str != '\0') {
+
	while (str != NULL && *str != '\0') {
		if (*str == '"' || *str == '\\')
			sbuf_putc(buf, '\\');
		sbuf_putc(buf, *str);
@@ -55,7 +55,6 @@ sbuf_json_escape(struct sbuf *buf, const char *str)
static void
pipeevent(struct pkg_event *ev)
{
-
	struct pkg *pkg = NULL;
	struct pkg_dep *dep = NULL;
	struct sbuf *msg, *buf;
	const char *message;
@@ -225,7 +224,7 @@ pipeevent(struct pkg_event *ev)
		    ev->e_required.pkg,
		    ev->e_required.pkg,
		    ev->e_required.force == 1 ? "true": "false");
-
		while (pkg_rdeps(pkg, &dep) == EPKG_OK)
+
		while (pkg_rdeps(ev->e_required.pkg, &dep) == EPKG_OK)
			sbuf_printf(msg, "{ \"pkgname\": \"%s\", "
			    "\"pkgversion\": \"%s\" }, ",
			    pkg_dep_name(dep),
modified libpkg/pkg_jobs.c
@@ -3,6 +3,7 @@
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
+
 * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -29,6 +30,7 @@

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

#include <assert.h>
#include <errno.h>
@@ -36,18 +38,20 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
+
#include <sys/wait.h>

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

-
static int get_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root);
+
static int find_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root);
static struct pkg *get_local_pkg(struct pkg_jobs *j, const char *origin, unsigned flag);
+
static struct pkg *get_remote_pkg(struct pkg_jobs *j, const char *origin, unsigned flag);
static int pkg_jobs_fetch(struct pkg_jobs *j);
static bool newer_than_local_pkg(struct pkg_jobs *j, struct pkg *rp, bool force);
+
static bool pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive);
static bool new_pkg_version(struct pkg_jobs *j);
-
static int order_pool(struct pkg_jobs *j, bool force);

int
pkg_jobs_new(struct pkg_jobs **j, pkg_jobs_t t, struct pkgdb *db)
@@ -90,6 +94,10 @@ pkg_jobs_set_repository(struct pkg_jobs *j, const char *ident)
void
pkg_jobs_free(struct pkg_jobs *j)
{
+
	struct pkg_job_request *req, *tmp;
+
	struct pkg_job_universe_item *un, *untmp, *cur;
+

+

	if (j == NULL)
		return;

@@ -97,7 +105,22 @@ pkg_jobs_free(struct pkg_jobs *j)
		j->type != PKG_JOBS_FETCH)
		pkgdb_release_lock(j->db);

-
	HASH_FREE(j->jobs, pkg, pkg_free);
+
	HASH_ITER(hh, j->request_add, req, tmp) {
+
		HASH_DEL(j->request_add, req);
+
		free(req);
+
	}
+
	HASH_ITER(hh, j->request_delete, req, tmp) {
+
		HASH_DEL(j->request_delete, req);
+
		free(req);
+
	}
+
	HASH_ITER(hh, j->universe, un, untmp) {
+
		HASH_DEL(j->universe, un);
+
		LL_FOREACH(un, cur) {
+
			pkg_free(cur->pkg);
+
		}
+
		free(un);
+
	}
+
	HASH_FREE(j->seen, pkg_job_seen, free);
	LL_FREE(j->patterns, job_pattern, free);

	free(j);
@@ -132,114 +155,319 @@ pkg_jobs_add(struct pkg_jobs *j, match_t match, char **argv, int argc)
	return (EPKG_OK);
}

+
int
+
pkg_jobs_add_iter(struct pkg_jobs *jobs, struct pkg **pkg)
+
{
+
	assert(pkg != NULL);
+

+
	HASH_NEXT(jobs->jobs_add, (*pkg));
+
}
+

+
int
+
pkg_jobs_delete_iter(struct pkg_jobs *jobs, struct pkg **pkg)
+
{
+
	assert(pkg != NULL);
+

+
	HASH_NEXT(jobs->jobs_delete, (*pkg));
+
}
+

+
static void
+
pkg_jobs_add_req(struct pkg_jobs *j, const char *origin, struct pkg *pkg, bool add)
+
{
+
	struct pkg_job_request *req;
+

+
	req = calloc(1, sizeof (struct pkg_job_request));
+
	if (req == NULL) {
+
		pkg_emit_errno("malloc", "struct pkg_job_request");
+
		return;
+
	}
+
	req->pkg = pkg;
+
	if (add)
+
		HASH_ADD_KEYPTR(hh, j->request_add, origin, strlen(origin), req);
+
	else
+
		HASH_ADD_KEYPTR(hh, j->request_delete, origin, strlen(origin), req);
+
}
+

+
/**
+
 * Check whether a package is in the universe already or add it
+
 * @return item or NULL
+
 */
static int
-
populate_local_rdeps(struct pkg_jobs *j, struct pkg *p)
+
pkg_jobs_handle_pkg_universe(struct pkg_jobs *j, struct pkg *pkg)
{
-
	struct pkg *pkg;
-
	struct pkg_dep *d = NULL;
-
	char *origin;
+
	struct pkg_job_universe_item *item, *cur, *tmp = NULL;
+
	const char *origin, *digest, *digest_cur, *version, *name;
+
	char *new_digest;
+
	int rc;
+
	struct sbuf *sb;
+
	struct pkg_job_seen *seen;
+

+
	pkg_get(pkg, PKG_ORIGIN, &origin, PKG_DIGEST, &digest,
+
			PKG_VERSION, &version, PKG_NAME, &name);
+
	if (digest == NULL) {
+
		/* We need to calculate digest of this package */
+
		sb = sbuf_new_auto();
+
		rc = pkg_emit_manifest_sbuf(pkg, sb, PKG_MANIFEST_EMIT_COMPACT, &new_digest);
+
		if (rc == EPKG_OK) {
+
			pkg_set(pkg, PKG_DIGEST, new_digest);
+
			pkg_get(pkg, PKG_DIGEST, &digest);
+
			free(new_digest);
+
		}
+
		else {
+
			return (rc);
+
		}
+
	}

-
	while (pkg_rdeps(p, &d) == EPKG_OK) {
-
		HASH_FIND_STR(j->bulk, pkg_dep_get(d, PKG_DEP_ORIGIN), pkg);
-
		if (pkg != NULL)
-
			continue;
-
		HASH_FIND_STR(j->seen, pkg_dep_get(d, PKG_DEP_ORIGIN), pkg);
-
		if (pkg != NULL)
-
			continue;
-
		if ((pkg = get_local_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), PKG_LOAD_BASIC|PKG_LOAD_RDEPS)) == NULL) {
-
			pkg_emit_error("Missing reverse dependency matching '%s'", pkg_dep_get(d, PKG_DEP_ORIGIN));
+
	HASH_FIND_STR(j->seen, digest, seen);
+
	if (seen != NULL)
+
		return (EPKG_END);
+

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

+
	HASH_FIND_STR(j->universe, __DECONST(char *, origin), item);
+
	if (item == NULL) {
+
		/* Insert new origin */
+
		item = calloc(1, sizeof (struct pkg_job_universe_item));
+

+
		if (item == NULL) {
+
			pkg_emit_errno("pkg_jobs_handle_universe", "calloc: struct pkg_job_universe_item");
			return (EPKG_FATAL);
		}
-
		pkg_get(pkg, PKG_ORIGIN, &origin);
-
		HASH_ADD_KEYPTR(hh, j->bulk, origin, strlen(origin), pkg);
-
		populate_local_rdeps(j, pkg);
+
		item->pkg = pkg;
+
		HASH_ADD_KEYPTR(hh, j->universe, __DECONST(char *, origin), strlen(origin), item);
+
	}
+
	else {
+
		/* Search for the same package added */
+
		LL_FOREACH(item, cur) {
+
			pkg_get(cur->pkg, PKG_DIGEST, &digest_cur);
+
			if (strcmp (digest, digest_cur) == 0) {
+
				/* Free new package */
+
				pkg_free(pkg);
+
				return (EPKG_OK);
+
			}
+
			tmp = cur;
+
		}
	}

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

+
	pkg_debug(2, "universe: add new %s pkg: %s, (%s-%s)",
+
			(pkg->type == PKG_INSTALLED ? "local" : "remote"), origin, name, version);
+
	item->pkg = pkg;
+
	if (tmp != NULL)
+
		tmp->next = item;
+

	return (EPKG_OK);
}

-
static void
-
remove_from_rdeps(struct pkg_jobs *j, const char *origin)
+
static int
+
pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg, bool recursive)
{
-
	struct pkg *pkg, *tmp;
-
	struct pkg_dep *d;
-

-
	HASH_ITER(hh, j->bulk, pkg, tmp) {
-
		HASH_FIND_STR(pkg->rdeps, origin, d);
-
		if (d != NULL) {
-
			HASH_DEL(pkg->rdeps, d);
-
			pkg_dep_free(d);
+
	struct pkg_dep *d = NULL;
+
	struct pkg_conflict *c = NULL;
+
	struct pkg *npkg, *rpkg;
+
	int ret;
+
	struct pkg_job_universe_item *unit;
+

+
	/* Add the requested package itself */
+
	ret = pkg_jobs_handle_pkg_universe(j, pkg);
+

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

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

+
		rpkg = NULL;
+
		npkg = get_local_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), 0);
+
		if (npkg == NULL) {
+
			/*
+
			 * We have a package installed, but its dependencies are not,
+
			 * try to search a remote dependency
+
			 */
+
			npkg = get_remote_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), 0);
+
			if (npkg == NULL) {
+
				/* Cannot continue */
+
				pkg_emit_error("Missing dependency matching '%s'", pkg_dep_get(d, PKG_DEP_ORIGIN));
+
				return (EPKG_FATAL);
+
			}
+
		}
+
		else if (j->type == PKG_JOBS_UPGRADE) {
+
			/* 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) {
+
				if (!pkg_need_upgrade(rpkg, npkg, j->flags & PKG_FLAG_RECURSIVE)) {
+
					pkg_free(rpkg);
+
					rpkg = NULL;
+
				}
+
			}
+
		}
+
		if (pkg_jobs_add_universe(j, npkg, recursive) != EPKG_OK)
+
			return (EPKG_FATAL);
+
		if (rpkg != NULL && pkg_jobs_add_universe(j, rpkg, recursive) != EPKG_OK)
+
			return (EPKG_FATAL);
+
	}
+

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

+
		npkg = get_local_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), 0);
+
		if (npkg == NULL) {
+
			/*
+
			 * We have a package installed, but its dependencies are not,
+
			 * try to search a remote dependency
+
			 */
+
			npkg = get_remote_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), 0);
+
			if (npkg == NULL) {
+
				/* Cannot continue */
+
				pkg_emit_error("Missing dependency matching '%s'", pkg_dep_get(d, PKG_DEP_ORIGIN));
+
				return (EPKG_FATAL);
+
			}
		}
+
		if (pkg_jobs_add_universe(j, npkg, recursive) != EPKG_OK)
+
			return (EPKG_FATAL);
	}
+

+
	/* Examine conflicts */
+
	while (pkg_conflicts(pkg, &c) == EPKG_OK) {
+
		/* XXX: this assumption can be applied only for the current plain dependencies */
+
		HASH_FIND_STR(j->universe, __DECONST(char *, pkg_conflict_origin(c)), unit);
+
		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) != EPKG_OK)
+
			return (EPKG_FATAL);
+
		npkg = get_local_pkg(j, pkg_conflict_origin(c), 0);
+
		if (npkg == NULL) {
+
			continue;
+
		}
+

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

+
	return (EPKG_OK);
}

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

static int
-
reverse_order_pool(struct pkg_jobs *j, bool force)
+
conflict_chain_cmp_cb(struct pkg_conflict_chain *a, struct pkg_conflict_chain *b)
{
-
	struct pkg *pkg, *tmp;
-
	struct pkg_dep *d, *dtmp;
-
	char *origin;
-
	unsigned int nb;
-
	struct sbuf *errb;
+
	const char *vera, *verb;

-
	nb = HASH_COUNT(j->bulk);
-
	HASH_ITER(hh, j->bulk, pkg, tmp) {
-
		pkg_get(pkg, PKG_ORIGIN, &origin);
-
		if (HASH_COUNT(pkg->rdeps) == 0) {
-
			HASH_DEL(j->bulk, pkg);
-
			HASH_ADD_KEYPTR(hh, j->jobs, origin, strlen(origin), pkg);
-
			remove_from_rdeps(j, origin);
+
	pkg_get(a->req->pkg, PKG_VERSION, &vera);
+
	pkg_get(b->req->pkg, PKG_VERSION, &verb);
+

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

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

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

-
	if (nb == HASH_COUNT(j->bulk)) {
-
		errb = sbuf_new_auto();
-
		HASH_ITER(hh, j->bulk, pkg, tmp) {
-
			pkg_get(pkg, PKG_ORIGIN, &origin);
-
			sbuf_printf(errb, "%s: ", origin);
-
			HASH_ITER(hh, pkg->rdeps, d, dtmp) {
-
				if (d->hh.next != NULL)
-
					sbuf_printf(errb, "%s, ", pkg_dep_get(d, PKG_DEP_ORIGIN));
-
				else
-
					sbuf_printf(errb, "%s\n", pkg_dep_get(d, PKG_DEP_ORIGIN));
+
	if (selected == NULL) {
+
		/* XXX: add manual selection here */
+
		/* Sort list by version of package */
+
		LL_SORT(chain, conflict_chain_cmp_cb);
+
		selected = chain;
+
	}
+

+
	/* Disable conflicts from a request */
+
	LL_FOREACH(chain, elt) {
+
		if (elt != selected)
+
			elt->req->skip = true;
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
resolve_request_conflicts(struct pkg_jobs *j)
+
{
+
	struct pkg_job_request *req, *rtmp, *found;
+
	struct pkg_conflict *c, *ctmp;
+
	struct pkg_conflict_chain *chain, *elt;
+

+
	HASH_ITER(hh, j->request_add, req, rtmp) {
+
		chain = NULL;
+
		HASH_ITER(hh, req->pkg->conflicts, c, ctmp) {
+
			HASH_FIND_STR(j->request_add, pkg_conflict_origin(c), found);
+
			if (found && !found->skip) {
+
				elt = calloc(1, sizeof(struct pkg_conflict_chain));
+
				if (elt == NULL) {
+
					pkg_emit_errno("resolve_request_conflicts", "calloc: struct pkg_conflict_chain");
+
					return (EPKG_FATAL);
+
				}
+
				elt->req = found;
+
				LL_PREPEND(chain, elt);
			}
-
			if (force) {
-
				HASH_DEL(j->bulk, pkg);
-
				HASH_ADD_KEYPTR(hh, j->jobs, origin, strlen(origin), pkg);
-
				remove_from_rdeps(j, origin);
+
			if (chain != NULL) {
+
				/* We need to handle conflict chain here */
+
				if (resolve_request_conflicts_chain (req->pkg, chain) != EPKG_OK) {
+
					LL_FREE(chain, pkg_conflict_chain, free);
+
					return (EPKG_FATAL);
+
				}
+
				LL_FREE(chain, pkg_conflict_chain, free);
			}
-

-
		}
-
		sbuf_finish(errb);
-
		if (!force) {
-
			pkg_emit_error("Error while trying to delete packages, "
-
					"dependencies that are still required:\n%s", sbuf_data(errb));
-
			sbuf_delete(errb);
-
			return (EPKG_FATAL);
-
		}
-
		else {
-
			pkg_emit_notice("You are trying to delete package(s) which has "
-
							"dependencies that are still required:\n%s"
-
							"... delete these packages anyway in forced mode",
-
							sbuf_data(errb));
-
			sbuf_delete(errb);
-
			return (EPKG_END);
		}
	}

	return (EPKG_OK);
}
+

static int
jobs_solve_deinstall(struct pkg_jobs *j)
{
	struct job_pattern *jp = NULL;
	struct pkg *pkg = NULL;
-
	struct pkg *tmp, *p;
-
	struct pkg_dep *d, *dtmp;
	struct pkgdb_it *it;
	int64_t oldsize;
	char *origin;
-
	int ret;
	bool recursive = false;

	if ((j->flags & PKG_FLAG_RECURSIVE) == PKG_FLAG_RECURSIVE)
@@ -252,63 +480,18 @@ jobs_solve_deinstall(struct pkg_jobs *j)
		while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_RDEPS) == EPKG_OK) {
			pkg_get(pkg, PKG_ORIGIN, &origin, PKG_FLATSIZE, &oldsize);
			pkg_set(pkg, PKG_OLD_FLATSIZE, oldsize, PKG_FLATSIZE, (int64_t)0);
-
			HASH_ADD_KEYPTR(hh, j->bulk, origin, strlen(origin), pkg);
-
			if (recursive)
-
				populate_local_rdeps(j, pkg);
+
			pkg_jobs_add_req(j, origin, pkg, false);
+
			pkg_jobs_add_universe(j, pkg, recursive);
			pkg = NULL;
		}
		pkgdb_it_free(it);
	}

-
	/* remove everything seen from deps */
-
	HASH_ITER(hh, j->bulk, pkg, tmp) {
-
		d = NULL;
-
		HASH_ITER(hh, pkg->rdeps, d, dtmp) {
-
			HASH_FIND_STR(j->seen, pkg_dep_get(d, PKG_DEP_ORIGIN), p);
-
			if (p != NULL) {
-
				HASH_DEL(pkg->rdeps, d);
-
				pkg_dep_free(d);
-
			}
-
		}
-
	}
-
	HASH_FREE(j->seen, pkg, pkg_free);
-

-
	while (HASH_COUNT(j->bulk) > 0) {
-
		if ((ret = reverse_order_pool(j, (j->flags & PKG_FLAG_FORCE) == PKG_FLAG_FORCE))
-
				!= EPKG_OK) {
-
			if (ret == EPKG_END)
-
				break;
-
			else
-
				return (EPKG_FATAL);
-
		}
-
	}
-

	j->solved = true;

	return( EPKG_OK);
}

-
static bool
-
recursive_autoremove(struct pkg_jobs *j)
-
{
-
	struct pkg *pkg1, *tmp1;
-
	int64_t oldsize;
-
	char *origin;
-

-
	HASH_ITER(hh, j->bulk, pkg1, tmp1) {
-
		if (HASH_COUNT(pkg1->rdeps) == 0) {
-
			HASH_DEL(j->bulk, pkg1);
-
			pkg_get(pkg1, PKG_ORIGIN, &origin, PKG_FLATSIZE, &oldsize);
-
			pkg_set(pkg1, PKG_OLD_FLATSIZE, oldsize, PKG_FLATSIZE, (int64_t)0);
-
			HASH_ADD_KEYPTR(hh, j->jobs, origin, strlen(origin), pkg1);
-
			remove_from_rdeps(j, origin);
-
			return (true);
-
		}
-
	}
-

-
	return (false);
-
}
-

static int
jobs_solve_autoremove(struct pkg_jobs *j)
{
@@ -321,15 +504,12 @@ jobs_solve_autoremove(struct pkg_jobs *j)

	while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_RDEPS) == EPKG_OK) {
		pkg_get(pkg, PKG_ORIGIN, &origin);
-
		HASH_ADD_KEYPTR(hh, j->bulk, origin, strlen(origin), pkg);
+
		pkg_jobs_add_req(j, origin, pkg, false);
+
		pkg_jobs_add_universe(j, pkg, false);
		pkg = NULL;
	}
	pkgdb_it_free(it);

-
	while (recursive_autoremove(j));
-

-
	HASH_FREE(j->bulk, pkg, pkg_free);
-

	j->solved = true;

	return (EPKG_OK);
@@ -339,14 +519,10 @@ static int
jobs_solve_upgrade(struct pkg_jobs *j)
{
	struct pkg *pkg = NULL;
-
	struct pkg *p, *tmp;
	struct pkgdb_it *it;
	char *origin;
-
	struct pkg_dep *d, *dtmp;
-
	int ret;

-
	if ((j->flags & PKG_FLAG_SKIP_INSTALL) == 0 &&
-
	    (j->flags & PKG_FLAG_PKG_VERSION_TEST) == PKG_FLAG_PKG_VERSION_TEST)
+
	if ((j->flags & PKG_FLAG_PKG_VERSION_TEST) == PKG_FLAG_PKG_VERSION_TEST)
		if (new_pkg_version(j)) {
			pkg_emit_newpkgversion();
			goto order;
@@ -358,156 +534,18 @@ jobs_solve_upgrade(struct pkg_jobs *j)
	while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
		pkg_get(pkg, PKG_ORIGIN, &origin);
		/* Do not test we ignore what doesn't exists remotely */
-
		get_remote_pkg(j, origin, MATCH_EXACT, false);
+
		find_remote_pkg(j, origin, MATCH_EXACT, false);
		pkg = NULL;
	}
	pkgdb_it_free(it);

-
	/* remove everything seen from deps */
-
	HASH_ITER(hh, j->bulk, pkg, tmp) {
-
		d = NULL;
-
		HASH_ITER(hh, pkg->deps, d, dtmp) {
-
			HASH_FIND_STR(j->seen, pkg_dep_get(d, PKG_DEP_ORIGIN), p);
-
			if (p != NULL) {
-
				HASH_DEL(pkg->deps, d);
-
				pkg_dep_free(d);
-
			}
-
		}
-
	}
order:
-
	HASH_FREE(j->seen, pkg, pkg_free);
-

-
	/* now order the pool */
-
	while (HASH_COUNT(j->bulk) > 0) {
-
		/* XXX: see comment at jobs_solve_install */
-
		ret = order_pool(j, false);
-
		if (ret == EPKG_FATAL)
-
			return (EPKG_FATAL);
-
		else if (ret == EPKG_END)
-
			break;
-
	}

	j->solved = true;

	return (EPKG_OK);
}

-
static void
-
remove_from_deps(struct pkg_jobs *j, const char *origin)
-
{
-
	struct pkg *pkg, *tmp;
-
	struct pkg_dep *d;
-

-
	HASH_ITER(hh, j->bulk, pkg, tmp) {
-
		HASH_FIND_STR(pkg->deps, origin, d);
-
		if (d != NULL) {
-
			HASH_DEL(pkg->deps, d);
-
			pkg_dep_free(d);
-
		}
-
	}
-
}
-

-
static int
-
order_pool(struct pkg_jobs *j, bool force)
-
{
-
	struct pkg *pkg, *tmp;
-
	char *origin;
-
	unsigned int nb;
-
	struct sbuf *errb;
-
	struct pkg_dep *d, *dtmp;
-

-
	nb = HASH_COUNT(j->bulk);
-
	HASH_ITER(hh, j->bulk, pkg, tmp) {
-
		pkg_get(pkg, PKG_ORIGIN, &origin);
-
		if (HASH_COUNT(pkg->deps) == 0) {
-
			HASH_DEL(j->bulk, pkg);
-
			HASH_ADD_KEYPTR(hh, j->jobs, origin, strlen(origin), pkg);
-
			remove_from_deps(j, origin);
-
		}
-
	}
-

-
	if (nb == HASH_COUNT(j->bulk)) {
-
		errb = sbuf_new_auto();
-
		HASH_ITER(hh, j->bulk, pkg, tmp) {
-
			pkg_get(pkg, PKG_ORIGIN, &origin);
-
			sbuf_printf(errb, "%s: ", origin);
-
			HASH_ITER(hh, pkg->deps, d, dtmp) {
-
				if (d->hh.next != NULL)
-
					sbuf_printf(errb, "%s, ", pkg_dep_get(d, PKG_DEP_ORIGIN));
-
				else
-
					sbuf_printf(errb, "%s\n", pkg_dep_get(d, PKG_DEP_ORIGIN));
-
			}
-
			if (force) {
-
				HASH_DEL(j->bulk, pkg);
-
				HASH_ADD_KEYPTR(hh, j->jobs, origin, strlen(origin), pkg);
-
				remove_from_rdeps(j, origin);
-
			}
-

-
		}
-
		sbuf_finish(errb);
-
		if (force) {
-
			pkg_emit_notice("Warning while trying to install/upgrade packages, "
-
					"as there are unresolved dependencies, "
-
					"but installation is forced:\n%s",
-
					sbuf_data(errb));
-
			sbuf_delete(errb);
-
			return (EPKG_END);
-
		}
-
		else {
-
			pkg_emit_error("Error while trying to install/upgrade packages, "
-
					"as there are unresolved dependencies:\n%s", sbuf_data(errb));
-
			sbuf_delete(errb);
-
			return (EPKG_FATAL);
-
		}
-
	}
-

-
	return (EPKG_OK);
-
}
-

-
static int
-
populate_rdeps(struct pkg_jobs *j, struct pkg *p)
-
{
-
	struct pkg *pkg;
-
	struct pkg_dep *d = NULL;
-

-
	while (pkg_rdeps(p, &d) == EPKG_OK) {
-
		HASH_FIND_STR(j->bulk, pkg_dep_get(d, PKG_DEP_ORIGIN), pkg);
-
		if (pkg != NULL)
-
			continue;
-
		HASH_FIND_STR(j->seen, pkg_dep_get(d, PKG_DEP_ORIGIN), pkg);
-
		if (pkg != NULL)
-
			continue;
-
		if (get_remote_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), MATCH_EXACT, true) != EPKG_OK) {
-
			pkg_emit_error("Missing reverse dependency matching '%s'", pkg_dep_get(d, PKG_DEP_ORIGIN));
-
			return (EPKG_FATAL);
-
		}
-
	}
-

-
	return (EPKG_OK);
-
}
-

-
static int
-
populate_deps(struct pkg_jobs *j, struct pkg *p)
-
{
-
	struct pkg *pkg;
-
	struct pkg_dep *d = NULL;
-

-
	while (pkg_deps(p, &d) == EPKG_OK) {
-
		HASH_FIND_STR(j->bulk, pkg_dep_get(d, PKG_DEP_ORIGIN), pkg);
-
		if (pkg != NULL)
-
			continue;
-
		HASH_FIND_STR(j->seen, pkg_dep_get(d, PKG_DEP_ORIGIN), pkg);
-
		if (pkg != NULL)
-
			continue;
-
		if (get_remote_pkg(j, pkg_dep_get(d, PKG_DEP_ORIGIN), MATCH_EXACT, false) != EPKG_OK) {
-
			pkg_emit_error("Missing dependency matching '%s'", pkg_dep_get(d, PKG_DEP_ORIGIN));
-
			return (EPKG_FATAL);
-
		}
-
	}
-

-
	return (EPKG_OK);
-
}
-

static bool
new_pkg_version(struct pkg_jobs *j)
{
@@ -534,15 +572,11 @@ new_pkg_version(struct pkg_jobs *j)
		goto end;
	}

-
	if (get_remote_pkg(j, origin, MATCH_EXACT, false) == EPKG_OK && HASH_COUNT(j->bulk) == 1) {
+
	if (find_remote_pkg(j, origin, MATCH_EXACT, false) == EPKG_OK) {
		ret = true;
		goto end;
	}

-
	/* Remove from seen in case it was explicitly requested. */
-
	HASH_FIND_STR(j->seen, origin, p);
-
	HASH_DEL(j->seen, p);
-

end:
	j->flags = old_flags;

@@ -550,16 +584,18 @@ end:
}

static int
-
get_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root)
+
find_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root)
{
	struct pkg *p = NULL;
	struct pkg *p1;
	struct pkgdb_it *it;
+
	struct pkg_job_universe_item *jit;
	char *origin;
	const char *buf1, *buf2;
	bool force = false, seen = false;
	int rc = EPKG_FATAL;
-
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS;
+
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|
+
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;

	if (root && (j->flags & PKG_FLAG_FORCE) == PKG_FLAG_FORCE)
		force = true;
@@ -585,10 +621,11 @@ get_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root)

	while (pkgdb_it_next(it, &p, flags) == EPKG_OK) {
		seen = false;
+
		p1 = NULL;
		pkg_get(p, PKG_ORIGIN, &origin);
-
		HASH_FIND_STR(j->bulk, origin, p1);
-
		if (p1 == NULL) {
-
			HASH_FIND_STR(j->seen, origin, p1);
+
		HASH_FIND_STR(j->universe, origin, jit);
+
		if (jit != NULL) {
+
			p1 = jit->pkg;
			seen = true;
		}

@@ -596,15 +633,9 @@ get_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root)
			pkg_get(p1, PKG_VERSION, &buf1);
			pkg_get(p, PKG_VERSION, &buf2);
			p->direct = root;
-
			if (seen) {
-
				if (pkg_version_cmp(buf1, buf2) >= 0)
-
					continue;
-
			} else {
-
				if (pkg_version_cmp(buf1, buf2) == 1)
-
					continue;
-
				HASH_DEL(j->bulk, p1);
-
				pkg_free(p1);
-
			}
+
			/* We have a more recent package */
+
			if (pkg_version_cmp(buf1, buf2) >= 0)
+
				continue;
		}

		if (j->type != PKG_JOBS_FETCH) {
@@ -612,7 +643,6 @@ get_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root)
				if (root)
					pkg_emit_already_installed(p);
				rc = EPKG_OK;
-
				HASH_ADD_KEYPTR(hh, j->seen, origin, strlen(origin), p);
				p = NULL;
				continue;
			}
@@ -620,16 +650,10 @@ get_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root)

		rc = EPKG_OK;
		p->direct = root;
-
		HASH_ADD_KEYPTR(hh, j->bulk, origin, strlen(origin), p);
-
		if (populate_deps(j, p) == EPKG_FATAL) {
-
			rc = EPKG_FATAL;
-
			break;
-
		}
+
		/* Add a package to request chain and populate universe */
+
		pkg_jobs_add_req(j, origin, p, true);
+
		rc = pkg_jobs_add_universe(j, p, true);

-
		if (populate_rdeps(j, p) == EPKG_FATAL) {
-
			rc = EPKG_FATAL;
-
			break;
-
		}
		p = NULL;
	}

@@ -645,7 +669,9 @@ get_local_pkg(struct pkg_jobs *j, const char *origin, unsigned flag)
	struct pkgdb_it *it;

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

	if ((it = pkgdb_query(j->db, origin, MATCH_EXACT)) == NULL)
@@ -659,91 +685,67 @@ get_local_pkg(struct pkg_jobs *j, const char *origin, unsigned flag)
	return (pkg);
}

-
static bool
-
newer_than_local_pkg(struct pkg_jobs *j, struct pkg *rp, bool force)
+
static struct pkg *
+
get_remote_pkg(struct pkg_jobs *j, const char *origin, unsigned flag)
{
-
	char *origin, *newversion, *oldversion, *reponame;
-
	struct pkg_note *an;
-
	int64_t oldsize;
-
	struct pkg *lp;
-
	struct pkg_option *lo = NULL, *ro = NULL;
-
	struct pkg_dep *ld = NULL, *rd = NULL;
-
	struct pkg_shlib *ls = NULL, *rs = NULL;
-
	bool automatic;
-
	int	ret1, ret2;
-
	pkg_change_t cmp;
-

-
	pkg_get(rp, PKG_ORIGIN, &origin,
-
	    PKG_REPONAME, &reponame);
-
	lp = get_local_pkg(j, origin, 0);
+
	struct pkg *pkg = NULL;
+
	struct pkgdb_it *it;

-
	/* obviously yes because local doesn't exists */
-
	if (lp == NULL) {
-
		pkg_set(rp, PKG_AUTOMATIC, (int64_t)true);
-
		return (true);
+
	if (flag == 0) {
+
		flag = PKG_LOAD_BASIC|PKG_LOAD_DEPS|PKG_LOAD_OPTIONS|
+
				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|
+
				PKG_LOAD_CONFLICTS;
	}

-
	if (pkg_is_locked(lp)) {
-
		pkg_free(lp);
-
		return (false);
-
	}
+
	if ((it = pkgdb_rquery(j->db, origin, MATCH_EXACT, j->reponame)) == NULL)
+
		return (NULL);

-
	pkg_get(lp, PKG_AUTOMATIC, &automatic,
-
	    PKG_VERSION, &oldversion,
-
	    PKG_FLATSIZE, &oldsize);
+
	if (pkgdb_it_next(it, &pkg, flag) != EPKG_OK)
+
		pkg = NULL;

-
	an = pkg_annotation_lookup(lp, "repository");
-
	if (an != NULL)  {
-
		if (strcmp(pkg_repo_ident(pkg_repo_find_name(reponame)),
-
		    pkg_annotation_value(an)) != 0)  {
-
			pkg_free(lp);
-
			return (false);
-
		} else {
-
			pkg_addannotation(rp, "repository", pkg_annotation_value(an));
-
		}
-
	}
+
	pkgdb_it_free(it);

-
	pkg_get(rp, PKG_VERSION, &newversion);
-
	pkg_set(rp, PKG_OLD_VERSION, oldversion,
-
	    PKG_OLD_FLATSIZE, oldsize,
-
	    PKG_AUTOMATIC, (int64_t)automatic);
+
	return (pkg);
+
}

-
	if (force) {
-
		pkg_free(lp);
-
		return (true);
-
	}
+
static bool
+
pkg_need_upgrade(struct pkg *rp, struct pkg *lp, bool recursive)
+
{
+
	int ret, ret1, ret2;
+
	const char *lversion, *rversion;
+
	struct pkg_option *lo = NULL, *ro = NULL;
+
	struct pkg_dep *ld = NULL, *rd = NULL;
+
	struct pkg_shlib *ls = NULL, *rs = NULL;
+
	struct pkg_conflict *lc = NULL, *rc = NULL;
+
	struct pkg_provide *lpr = NULL, *rpr = NULL;

-
	/* compare versions */
-
	cmp = pkg_version_change(rp);
+
	/* Do not upgrade locked packages */
+
	if (pkg_is_locked(lp))
+
		return (false);

-
	if (cmp == PKG_UPGRADE) {
-
		pkg_free(lp);
-
		return (true);
-
	}
+
	pkg_get(lp, PKG_VERSION, &lversion);
+
	pkg_get(rp, PKG_VERSION, &rversion);

-
	if (cmp == PKG_REINSTALL && (j->flags & PKG_FLAG_RECURSIVE) == PKG_FLAG_RECURSIVE) {
-
		pkg_free(lp);
+
	ret = pkg_version_cmp(lversion, rversion);
+
	if (ret == 0 && recursive)
		return (true);
-
	}
-

-
	if (cmp == PKG_DOWNGRADE) {
-
		pkg_free(lp);
+
	else if (ret < 0)
+
		return (true);
+
	else
		return (false);
-
	}
+


	/* compare options */
	for (;;) {
		ret1 = pkg_options(rp, &ro);
		ret2 = pkg_options(lp, &lo);
		if (ret1 != ret2) {
-
			pkg_free(lp);
			pkg_set(rp, PKG_REASON, "options changed");
			return (true);
		}
		if (ret1 == EPKG_OK) {
			if (strcmp(pkg_option_opt(lo), pkg_option_opt(ro)) != 0 ||
-
				strcmp(pkg_option_value(lo), pkg_option_value(ro)) != 0) {
-
				pkg_free(lp);
+
					strcmp(pkg_option_value(lo), pkg_option_value(ro)) != 0) {
				pkg_set(rp, PKG_REASON, "options changed");
				return (true);
			}
@@ -752,20 +754,17 @@ newer_than_local_pkg(struct pkg_jobs *j, struct pkg *rp, bool force)
			break;
	}

-

	/* What about the direct deps */
	for (;;) {
		ret1 = pkg_deps(rp, &rd);
		ret2 = pkg_deps(lp, &ld);
		if (ret1 != ret2) {
-
			pkg_free(lp);
			pkg_set(rp, PKG_REASON, "direct dependency changed");
			return (true);
		}
		if (ret1 == EPKG_OK) {
			if (strcmp(pkg_dep_get(rd, PKG_DEP_NAME),
-
			    pkg_dep_get(ld, PKG_DEP_NAME)) != 0) {
-
				pkg_free(lp);
+
					pkg_dep_get(ld, PKG_DEP_NAME)) != 0) {
				pkg_set(rp, PKG_REASON, "direct dependency changed");
				return (true);
			}
@@ -774,19 +773,55 @@ newer_than_local_pkg(struct pkg_jobs *j, struct pkg *rp, bool force)
			break;
	}

+
	/* Conflicts */
+
	for (;;) {
+
		ret1 = pkg_conflicts(rp, &rc);
+
		ret2 = pkg_conflicts(lp, &lc);
+
		if (ret1 != ret2) {
+
			pkg_set(rp, PKG_REASON, "direct conflict changed");
+
			return (true);
+
		}
+
		if (ret1 == EPKG_OK) {
+
			if (strcmp(pkg_conflict_origin(rc),
+
					pkg_conflict_origin(lc)) != 0) {
+
				pkg_set(rp, PKG_REASON, "direct conflict changed");
+
				return (true);
+
			}
+
		}
+
		else
+
			break;
+
	}
+

+
	/* Provides */
+
	for (;;) {
+
		ret1 = pkg_provides(rp, &rpr);
+
		ret2 = pkg_provides(lp, &lpr);
+
		if (ret1 != ret2) {
+
			pkg_set(rp, PKG_REASON, "provides changed");
+
			return (true);
+
		}
+
		if (ret1 == EPKG_OK) {
+
			if (strcmp(pkg_provide_name(rpr),
+
					pkg_provide_name(lpr)) != 0) {
+
				pkg_set(rp, PKG_REASON, "provides changed");
+
				return (true);
+
			}
+
		}
+
		else
+
			break;
+
	}
+

	/* Finish by the shlibs */
	for (;;) {
		ret1 = pkg_shlibs_required(rp, &rs);
		ret2 = pkg_shlibs_required(lp, &ls);
		if (ret1 != ret2) {
-
			pkg_free(lp);
			pkg_set(rp, PKG_REASON, "needed shared library changed");
			return (true);
		}
		if (ret1 == EPKG_OK) {
			if (strcmp(pkg_shlib_name(rs),
-
			    pkg_shlib_name(ls)) != 0) {
-
				pkg_free(lp);
+
					pkg_shlib_name(ls)) != 0) {
				pkg_set(rp, PKG_REASON, "needed shared library changed");
				return (true);
			}
@@ -798,109 +833,96 @@ newer_than_local_pkg(struct pkg_jobs *j, struct pkg *rp, bool force)
	return (false);
}

-
#define	NO_PKGS_MATCHING_STR \
-
	"No packages matching '%s' available in the repositories"
+
static bool
+
newer_than_local_pkg(struct pkg_jobs *j, struct pkg *rp, bool force)
+
{
+
	char *origin, *newversion, *oldversion, *reponame;
+
	struct pkg_note *an;
+
	int64_t oldsize;
+
	struct pkg *lp;
+
	bool automatic;
+
	int ret;
+

+
	pkg_get(rp, PKG_ORIGIN, &origin,
+
	    PKG_REPONAME, &reponame);
+
	lp = get_local_pkg(j, origin, 0);
+

+
	/* obviously yes because local doesn't exists */
+
	if (lp == NULL) {
+
		pkg_set(rp, PKG_AUTOMATIC, (int64_t)true);
+
		return (true);
+
	}
+

+
	pkg_get(lp, PKG_AUTOMATIC, &automatic,
+
	    PKG_VERSION, &oldversion,
+
	    PKG_FLATSIZE, &oldsize);
+

+
	/* Add repo name to the annotation */
+
	an = pkg_annotation_lookup(lp, "repository");
+
	if (an != NULL)  {
+
		if (strcmp(pkg_repo_ident(pkg_repo_find_name(reponame)),
+
		    pkg_annotation_value(an)) != 0)  {
+
			pkg_free(lp);
+
			return (false);
+
		} else {
+
			pkg_addannotation(rp, "repository", pkg_annotation_value(an));
+
		}
+
	}
+

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

+
	if (force) {
+
		pkg_free(lp);
+
		return (true);
+
	}
+

+
	ret = pkg_need_upgrade(rp, lp, j->flags & PKG_FLAG_RECURSIVE);
+
	pkg_free(lp);
+

+
	return (ret);
+
}

static int
jobs_solve_install(struct pkg_jobs *j)
{
	struct job_pattern *jp = NULL;
-
	struct pkg *pkg, *tmp, *p;
-
	struct pkg_dep *d, *dtmp;
+
	struct pkg *pkg;
	struct pkgdb_it *it;
	const char *origin;
-
	int ret;

-
	if ((j->flags & PKG_FLAG_SKIP_INSTALL) == 0 &&
-
	    (j->flags & PKG_FLAG_PKG_VERSION_TEST) == PKG_FLAG_PKG_VERSION_TEST)
+
	if ((j->flags & PKG_FLAG_PKG_VERSION_TEST) == PKG_FLAG_PKG_VERSION_TEST)
		if (new_pkg_version(j)) {
			pkg_emit_newpkgversion();
			goto order;
		}

-
	ret = EPKG_OK;
	LL_FOREACH(j->patterns, jp) {
		if ((j->flags & PKG_FLAG_RECURSIVE) == PKG_FLAG_RECURSIVE) {
-
			it = pkgdb_query(j->db, jp->pattern, jp->match);
-
			if (it == NULL)
+
			if ((it = pkgdb_query(j->db, jp->pattern, jp->match)) == NULL)
				return (EPKG_FATAL);

			pkg = NULL;
-
			while (pkgdb_it_next(it, &pkg,
-
			    PKG_LOAD_BASIC|PKG_LOAD_RDEPS) == EPKG_OK) {
-
				d = NULL;
+
			while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_RDEPS) == EPKG_OK) {
				pkg_get(pkg, PKG_ORIGIN, &origin);
-
				if (get_remote_pkg(j, origin, MATCH_EXACT,
-
				    true) != EPKG_OK) {
-
					ret = EPKG_FATAL;
-
					pkg_emit_error(NO_PKGS_MATCHING_STR,
-
					    origin);
-
				}
-

-
				while (pkg_rdeps(pkg, &d) == EPKG_OK) {
-
					const char *dep_origin;
-
					dep_origin = pkg_dep_origin(d);
-

-
					if (get_remote_pkg(j, dep_origin,
-
					    MATCH_EXACT, false) == EPKG_OK)
-
						continue;
-
					ret = EPKG_FATAL;
-
					pkg_emit_error(NO_PKGS_MATCHING_STR,
-
					    dep_origin);
-
				}
+
				if (find_remote_pkg(j, origin, MATCH_EXACT, true) == EPKG_FATAL)
+
					pkg_emit_error("No packages matching '%s', has been found in the repositories", origin);
			}
			pkgdb_it_free(it);
		} else {
-
			if (get_remote_pkg(j, jp->pattern, jp->match,
-
			    true) == EPKG_FATAL) {
-
				ret = EPKG_FATAL;
-
				pkg_emit_error(NO_PKGS_MATCHING_STR,
-
				    jp->pattern);
-
			}
+
			if (find_remote_pkg(j, jp->pattern, jp->match, true) == EPKG_FATAL)
+
				pkg_emit_error("No packages matching '%s' has been found in the repositories", jp->pattern);
		}
	}
-
	if (ret != EPKG_OK)
-
		return (ret);

-
	if (HASH_COUNT(j->bulk) == 0)
-
		return (EPKG_OK);
-

-
	/* remove everything seen from deps */
-
	HASH_ITER(hh, j->bulk, pkg, tmp) {
-
		d = NULL;
-
		HASH_ITER(hh, pkg->deps, d, dtmp) {
-
			HASH_FIND_STR(j->seen, pkg_dep_get(d, PKG_DEP_ORIGIN), p);
-
			if (p != NULL) {
-
				HASH_DEL(pkg->deps, d);
-
				pkg_dep_free(d);
-
			}
-
		}
-
		if (pkg->direct) {
-
			if ((j->flags & PKG_FLAG_AUTOMATIC) == PKG_FLAG_AUTOMATIC)
-
				pkg_set(pkg, PKG_AUTOMATIC, (int64_t)true);
-
			else
-
				pkg_set(pkg, PKG_AUTOMATIC, (int64_t)false);
-
		}
+
	if (resolve_request_conflicts(j) != EPKG_OK) {
+
		pkg_emit_error("Cannot resolve conflicts in a request");
+
		return (EPKG_FATAL);
	}

order:
-
	HASH_FREE(j->seen, pkg, pkg_free);
-

-
	/* now order the pool */
-
	while (HASH_COUNT(j->bulk) > 0) {
-
		/*
-
		 * XXX: create specific flag that allows to install or upgrade
-
		 * a package even if it misses some dependencies, PKG_FORCE
-
		 * should not logically apply to this situation, as it is
-
		 * designed only for reinstalling packages, but not for
-
		 * installing packages with missing dependencies...
-
		 */
-
		ret = order_pool(j, false);
-
		if (ret == EPKG_FATAL)
-
			return (EPKG_FATAL);
-
		else if (ret == EPKG_END)
-
			break;
-
	}

	j->solved = true;

@@ -926,21 +948,17 @@ jobs_solve_fetch(struct pkg_jobs *j)
		while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
			pkg_get(pkg, PKG_ORIGIN, &origin);
			/* Do not test we ignore what doesn't exists remotely */
-
			get_remote_pkg(j, origin, MATCH_EXACT, false);
+
			find_remote_pkg(j, origin, MATCH_EXACT, false);
			pkg = NULL;
		}
		pkgdb_it_free(it);
	} else {
		LL_FOREACH(j->patterns, jp) {
-
			if (get_remote_pkg(j, jp->pattern, jp->match, true) == EPKG_FATAL)
+
			if (find_remote_pkg(j, jp->pattern, jp->match, true) == EPKG_FATAL)
				pkg_emit_error("No packages matching '%s' has been found in the repositories", jp->pattern);
		}
	}

-
	HASH_FREE(j->seen, pkg, pkg_free);
-
	/* No need to order we are just fetching */
-
	j->jobs = j->bulk;
-

	j->solved = true;

	return (EPKG_OK);
@@ -950,31 +968,93 @@ int
pkg_jobs_solve(struct pkg_jobs *j)
{
	bool dry_run = false;
+
	int ret, pstatus;
+
	struct pkg_solve_problem *problem;
+
	const char *solver;
+
	FILE *spipe[2];
+
	pid_t pchild;

	if ((j->flags & PKG_FLAG_DRY_RUN) == PKG_FLAG_DRY_RUN)
		dry_run = true;

-
	if (j->type == PKG_JOBS_FETCH)
-
		dry_run = true;
-

	if (!dry_run && pkgdb_obtain_lock(j->db) != EPKG_OK)
		return (EPKG_FATAL);


	switch (j->type) {
	case PKG_JOBS_AUTOREMOVE:
-
		return (jobs_solve_autoremove(j));
+
		ret =jobs_solve_autoremove(j);
+
		break;
	case PKG_JOBS_DEINSTALL:
-
		return (jobs_solve_deinstall(j));
+
		ret = jobs_solve_deinstall(j);
+
		break;
	case PKG_JOBS_UPGRADE:
-
		return (jobs_solve_upgrade(j));
+
		ret = jobs_solve_upgrade(j);
+
		break;
	case PKG_JOBS_INSTALL:
-
		return (jobs_solve_install(j));
+
		ret = jobs_solve_install(j);
+
		break;
	case PKG_JOBS_FETCH:
-
		return (jobs_solve_fetch(j));
+
		ret = jobs_solve_fetch(j);
+
		break;
	default:
		return (EPKG_FATAL);
	}
+

+
	if (ret == EPKG_OK) {
+
		if (pkg_config_string(PKG_CONFIG_CUDF_SOLVER, &solver) == EPKG_OK
+
				&& solver != NULL) {
+
			pchild = process_spawn_pipe(spipe, solver);
+
			if (pchild == -1)
+
				return (EPKG_FATAL);
+

+
			ret = pkg_jobs_cudf_emit_file(j, j->type, spipe[1]);
+
			fclose(spipe[1]);
+

+
			if (ret == EPKG_OK)
+
				ret = pkg_jobs_cudf_parse_output(j, spipe[0]);
+

+
			fclose(spipe[0]);
+
			waitpid(pchild, &pstatus, WNOHANG);
+
		}
+
		else {
+
			problem = pkg_solve_jobs_to_sat(j);
+
			if (problem != NULL) {
+
				if (pkg_config_string(PKG_CONFIG_SAT_SOLVER, &solver) == EPKG_OK
+
						&& solver != NULL) {
+
					pchild = process_spawn_pipe(spipe, solver);
+
					if (pchild == -1)
+
						return (EPKG_FATAL);
+

+
					ret = pkg_solve_dimacs_export(problem, spipe[1]);
+
					fclose(spipe[1]);
+

+
					if (ret == EPKG_OK) {
+
						ret = pkg_solve_parse_sat_output(spipe[0], problem, j);
+
					}
+

+
					fclose(spipe[0]);
+
					waitpid(pchild, &pstatus, WNOHANG);
+
				}
+
				else {
+
					if (!pkg_solve_sat_problem(problem)) {
+
						pkg_emit_error("cannot solve job using SAT solver");
+
						ret = EPKG_FATAL;
+
						j->solved = false;
+
					}
+
					else {
+
						ret = pkg_solve_sat_to_jobs(problem, j);
+
					}
+
				}
+
			}
+
			else {
+
				pkg_emit_error("cannot convert job to SAT problem");
+
				ret = EPKG_FATAL;
+
				j->solved = false;
+
			}
+
		}
+
	}
+
	return (ret);
}

int
@@ -982,9 +1062,12 @@ pkg_jobs_find(struct pkg_jobs *j, const char *origin, struct pkg **p)
{
	struct pkg *pkg;

-
	HASH_FIND_STR(j->jobs, origin, pkg);
-
	if (pkg == NULL)
-
		return (EPKG_FATAL);
+
	HASH_FIND_STR(j->jobs_add, __DECONST(char *, origin), pkg);
+
	if (pkg == NULL) {
+
		HASH_FIND_STR(j->jobs_add, __DECONST(char *, origin), pkg);
+
		if (pkg == NULL)
+
			return (EPKG_FATAL);
+
	}

	if (p != NULL)
		*p = pkg;
@@ -997,7 +1080,7 @@ pkg_jobs_count(struct pkg_jobs *j)
{
	assert(j != NULL);

-
	return (HASH_COUNT(j->jobs));
+
	return (HASH_COUNT(j->jobs_add) + HASH_COUNT(j->jobs_delete));
}

pkg_jobs_t
@@ -1006,14 +1089,6 @@ pkg_jobs_type(struct pkg_jobs *j)
	return (j->type);
}

-
int
-
pkg_jobs(struct pkg_jobs *j, struct pkg **pkg)
-
{
-
	assert(j != NULL);
-

-
	HASH_NEXT(j->jobs, (*pkg));
-
}
-

static int
pkg_jobs_keep_files_to_del(struct pkg *p1, struct pkg *p2)
{
@@ -1040,14 +1115,14 @@ pkg_jobs_keep_files_to_del(struct pkg *p1, struct pkg *p2)
static int
pkg_jobs_install(struct pkg_jobs *j)
{
-
	struct pkg *p = NULL;
+
	struct pkg *p = NULL, *ptmp;
	struct pkg *pkg = NULL;
	struct pkg *newpkg = NULL;
	struct pkg *pkg_temp = NULL;
	struct pkgdb_it *it = NULL;
	struct pkg *pkg_queue = NULL;
	struct pkg_manifest_key *keys = NULL;
-
	char path[MAXPATHLEN + 1];
+
	char path[MAXPATHLEN];
	const char *cachedir = NULL;
	int flags = 0;
	int retcode = EPKG_FATAL;
@@ -1072,7 +1147,7 @@ pkg_jobs_install(struct pkg_jobs *j)
	/* Install */
	pkgdb_transaction_begin(j->db->sqlite, "upgrade");

-
	while (pkg_jobs(j, &p) == EPKG_OK) {
+
	HASH_ITER(hh, j->jobs_add, p, ptmp) {
		const char *pkgorigin, *oldversion, *origin;
		struct pkg_note *an;
		bool automatic;
@@ -1212,7 +1287,7 @@ pkg_jobs_install(struct pkg_jobs *j)
static int
pkg_jobs_deinstall(struct pkg_jobs *j)
{
-
	struct pkg *p = NULL;
+
	struct pkg *p = NULL, *ptmp;
	int retcode;
	int flags = 0;

@@ -1225,7 +1300,8 @@ pkg_jobs_deinstall(struct pkg_jobs *j)
	if ((j->flags & PKG_FLAG_NOSCRIPT) == PKG_FLAG_NOSCRIPT)
		flags |= PKG_DELETE_NOSCRIPT;

-
	while (pkg_jobs(j, &p) == EPKG_OK) {
+
	HASH_ITER(hh, j->jobs_delete, p, ptmp) {
+

		retcode = pkg_delete(p, j->db, flags);

		if (retcode != EPKG_OK)
@@ -1248,7 +1324,9 @@ pkg_jobs_apply(struct pkg_jobs *j)
	switch (j->type) {
	case PKG_JOBS_INSTALL:
		pkg_plugins_hook_run(PKG_PLUGIN_HOOK_PRE_INSTALL, j, j->db);
-
		rc = pkg_jobs_install(j);
+
		rc = pkg_jobs_deinstall(j);
+
		if (rc == EPKG_OK)
+
			rc = pkg_jobs_install(j);
		pkg_plugins_hook_run(PKG_PLUGIN_HOOK_POST_INSTALL, j, j->db);
		break;
	case PKG_JOBS_DEINSTALL:
@@ -1282,11 +1360,11 @@ pkg_jobs_apply(struct pkg_jobs *j)
static int
pkg_jobs_fetch(struct pkg_jobs *j)
{
-
	struct pkg *p = NULL;
+
	struct pkg *p = NULL, *ptmp;
	struct pkg *pkg = NULL;
	struct statfs fs;
	struct stat st;
-
	char path[MAXPATHLEN + 1];
+
	char path[MAXPATHLEN];
	int64_t dlsize = 0;
	const char *cachedir = NULL;
	const char *repopath = NULL;
@@ -1298,10 +1376,10 @@ pkg_jobs_fetch(struct pkg_jobs *j)
		return (EPKG_FATAL);

	/* check for available size to fetch */
-
	while (pkg_jobs(j, &p) == EPKG_OK) {
+
	HASH_ITER(hh, j->jobs_add, p, ptmp) {
		int64_t pkgsize;
		pkg_get(p, PKG_PKGSIZE, &pkgsize, PKG_REPOPATH, &repopath);
-
		snprintf(cachedpath, MAXPATHLEN, "%s/%s", cachedir, repopath);
+
		snprintf(cachedpath, sizeof(cachedpath), "%s/%s", cachedir, repopath);
		if (stat(cachedpath, &st) == -1)
			dlsize += pkgsize;
		else
@@ -1334,7 +1412,7 @@ pkg_jobs_fetch(struct pkg_jobs *j)

	/* Fetch */
	p = NULL;
-
	while (pkg_jobs(j, &p) == EPKG_OK) {
+
	HASH_ITER(hh, j->jobs_add, p, ptmp) {
		if (pkg_repo_fetch(p) != EPKG_OK)
			return (EPKG_FATAL);
	}
@@ -1344,7 +1422,7 @@ pkg_jobs_fetch(struct pkg_jobs *j)
	pkg_emit_integritycheck_begin();

	pkg_manifest_keys_new(&keys);
-
	while (pkg_jobs(j, &p) == EPKG_OK) {
+
	HASH_ITER(hh, j->jobs_add, p, ptmp) {
		const char *pkgrepopath;

		pkg_get(p, PKG_REPOPATH, &pkgrepopath);
modified libpkg/pkg_manifest.c
@@ -1,7 +1,6 @@
/*-
 * Copyright (c) 2011-2013 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
-
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -58,7 +57,9 @@
#define PKG_SHLIBS_REQUIRED	-14
#define PKG_SHLIBS_PROVIDED	-15
#define PKG_ANNOTATIONS		-16
-
#define PKG_INFOS		-17	/* Deprecated field: treat as an annotation for backwards compatibility */
+
#define PKG_INFOS		-17	
+
#define PKG_CONFLICTS -18
+
#define PKG_PROVIDES -19 /* Deprecated field: treat as an annotation for backwards compatibility */

static int pkg_string(struct pkg *, ucl_object_t *, int);
static int pkg_object(struct pkg *, ucl_object_t *, int);
@@ -81,6 +82,7 @@ static struct manifest_key {
	{ "arch",                PKG_ARCH,                UCL_STRING, pkg_string},
	{ "categories",          PKG_CATEGORIES,          UCL_ARRAY,  pkg_array},
	{ "comment",             PKG_COMMENT,             UCL_STRING, pkg_string},
+
	{ "conflicts",           PKG_CONFLICTS,           UCL_ARRAY,  pkg_array},
	{ "deps",                PKG_DEPS,                UCL_OBJECT, pkg_object},
	{ "desc",                PKG_DESC,                UCL_STRING, pkg_string},
	{ "directories",         PKG_DIRECTORIES,         UCL_OBJECT, pkg_object},
@@ -103,6 +105,7 @@ static struct manifest_key {
	{ "path",                PKG_REPOPATH,            UCL_STRING, pkg_string},
	{ "pkgsize",             PKG_PKGSIZE,             UCL_INT,    pkg_int},
	{ "prefix",              PKG_PREFIX,              UCL_STRING, pkg_string},
+
	{ "provides",            PKG_PROVIDES,            UCL_ARRAY,  pkg_array},
	{ "scripts",             PKG_SCRIPTS,             UCL_OBJECT, pkg_object},
	{ "shlibs",              PKG_SHLIBS_REQUIRED,     UCL_ARRAY,  pkg_array}, /* Backwards compat with 1.0.x packages */
	{ "shlibs_provided",     PKG_SHLIBS_PROVIDED,     UCL_ARRAY,  pkg_array},
@@ -357,6 +360,18 @@ pkg_array(struct pkg *pkg, ucl_object_t *obj, int attr)
			else
				pkg_addshlib_provided(pkg, ucl_object_tostring(cur));
			break;
+
		case PKG_CONFLICTS:
+
			if (cur->type != UCL_STRING)
+
				pkg_emit_error("Skipping malformed conflict name");
+
			else
+
				pkg_addconflict(pkg, ucl_object_tostring(cur));
+
			break;
+
		case PKG_PROVIDES:
+
			if (cur->type != UCL_STRING)
+
				pkg_emit_error("Skipping malformed provide name");
+
			else
+
				pkg_addprovide(pkg, ucl_object_tostring(cur));
+
			break;
		}
	}

@@ -803,7 +818,8 @@ pkg_emit_filelist(struct pkg *pkg, FILE *f)
		urlencode(pkg_file_path(file), &b);
		seq = ucl_array_append(seq, ucl_object_fromlstring(sbuf_data(b), sbuf_len(b)));
	}
-
	obj = ucl_object_insert_key(obj, seq, "files", 5, false);
+
	if (seq != NULL)
+
		obj = ucl_object_insert_key(obj, seq, "files", 5, false);

	output = ucl_object_emit(obj, UCL_EMIT_JSON_COMPACT);
	fprintf(f, "%s", output);
@@ -830,6 +846,8 @@ emit_manifest(struct pkg *pkg, char **out, short flags)
	struct pkg_group	*group    = NULL;
	struct pkg_shlib	*shlib    = NULL;
	struct pkg_note		*note     = NULL;
+
	struct pkg_conflict	*conflict = NULL;
+
	struct pkg_provide	*provide  = NULL;
	struct sbuf		*tmpsbuf  = NULL;
	int i;
	const char *comment, *desc, *message, *name, *pkgarch;
@@ -884,7 +902,9 @@ emit_manifest(struct pkg *pkg, char **out, short flags)
		obj = ucl_object_insert_key(top, ucl_object_fromint(pkgsize), "pkgsize", 7, false);

	urlencode(desc, &tmpsbuf);
-
	obj = ucl_object_insert_key(top, ucl_object_fromlstring(sbuf_data(tmpsbuf), sbuf_len(tmpsbuf)), "desc", 4, false);
+
	obj = ucl_object_insert_key(top,
+
	    ucl_object_fromstring_common(sbuf_data(tmpsbuf), sbuf_len(tmpsbuf), UCL_STRING_TRIM),
+
	    "desc", 4, false);

	pkg_debug(1, "Emitting deps");
	map = NULL;
@@ -926,6 +946,22 @@ emit_manifest(struct pkg *pkg, char **out, short flags)
		seq = ucl_array_append(seq, ucl_object_fromstring(pkg_shlib_name(shlib)));
	obj = ucl_object_insert_key(top, seq, "shlibs_provided", 15, false);

+
	pkg_debug(1, "Emitting conflicts");
+
	map = NULL;
+
	while (pkg_conflicts(pkg, &conflict) == EPKG_OK)
+
		map = ucl_object_insert_key(map,
+
		    ucl_object_fromstring(pkg_option_value(option)),
+
		    pkg_conflict_origin(conflict), 0, false);
+
	obj = ucl_object_insert_key(top, map, "conflicts", 9, false);
+

+
	pkg_debug(1, "Emitting provides");
+
	map = NULL;
+
	while (pkg_provides(pkg, &provide) == EPKG_OK)
+
		map = ucl_object_insert_key(map,
+
		    ucl_object_fromstring(pkg_option_value(option)),
+
		    pkg_provide_name(provide), 0, false);
+
	obj = ucl_object_insert_key(top, map, "provides", 8, false);
+

	pkg_debug(1, "Emitting options");
	map = NULL;
	while (pkg_options(pkg, &option) == EPKG_OK) {
@@ -934,9 +970,6 @@ emit_manifest(struct pkg *pkg, char **out, short flags)
		    ucl_object_fromstring(pkg_option_value(option)),
		    pkg_option_opt(option), 0, false);
	}
-
	obj = ucl_object_insert_key(top, map, "options", 7, false);
-

-
	pkg_debug(1, "Emitting annotations");
	map = NULL;
	while (pkg_annotations(pkg, &note) == EPKG_OK) {
		map = ucl_object_insert_key(map,
modified libpkg/pkg_old.c
@@ -68,11 +68,11 @@ pkg_old_load_from_path(struct pkg *pkg, const char *path)
	if (!is_dir(path))
		return (EPKG_FATAL);

-
	snprintf(fpath, MAXPATHLEN, "%s/+CONTENTS", path);
+
	snprintf(fpath, sizeof(fpath), "%s/+CONTENTS", path);
	if (ports_parse_plist(pkg, fpath, NULL) != EPKG_OK)
		return (EPKG_FATAL);

-
	snprintf(fpath, MAXPATHLEN, "%s/+COMMENT", path);
+
	snprintf(fpath, sizeof(fpath), "%s/+COMMENT", path);
	if (access(fpath, F_OK) == 0)
		pkg_set_from_file(pkg, PKG_COMMENT, fpath, true);

@@ -238,29 +238,29 @@ pkg_register_old(struct pkg *pkg)
	pkg_old_emit_content(pkg, &content);

	pkg_config_string(PKG_CONFIG_DBDIR, &pkgdbdir);
-
	snprintf(path, MAXPATHLEN, "%s/%s-%s", pkgdbdir, name, version);
+
	snprintf(path, sizeof(path), "%s/%s-%s", pkgdbdir, name, version);
	mkdir(path, 0755);

-
	snprintf(path, MAXPATHLEN, "%s/%s-%s/+CONTENTS", pkgdbdir, name, version);
+
	snprintf(path, sizeof(path), "%s/%s-%s/+CONTENTS", pkgdbdir, name, version);
	fp = fopen(path, "w");
	fputs(content, fp);
	fclose(fp);

	pkg_get(pkg, PKG_DESC, &buf);
-
	snprintf(path, MAXPATHLEN, "%s/%s-%s/+DESC", pkgdbdir, name, version);
+
	snprintf(path, sizeof(path), "%s/%s-%s/+DESC", pkgdbdir, name, version);
	fp = fopen(path, "w");
	fputs(buf, fp);
	fclose(fp);

	pkg_get(pkg, PKG_COMMENT, &buf);
-
	snprintf(path, MAXPATHLEN, "%s/%s-%s/+COMMENT", pkgdbdir, name, version);
+
	snprintf(path, sizeof(path), "%s/%s-%s/+COMMENT", pkgdbdir, name, version);
	fp = fopen(path, "w");
	fprintf(fp, "%s\n", buf);
	fclose(fp);

	pkg_get(pkg, PKG_MESSAGE, &buf);
	if (buf != NULL && *buf != '\0') {
-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+DISPLAY", pkgdbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s/+DISPLAY", pkgdbdir, name, version);
		fp = fopen(path, "w");
		fputs(buf, fp);
		fclose(fp);
@@ -297,7 +297,7 @@ pkg_register_old(struct pkg *pkg)
	}
	if (sbuf_len(install_script) > 0) {
		sbuf_finish(install_script);
-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+INSTALL", pkgdbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s/+INSTALL", pkgdbdir, name, version);
		fp = fopen(path, "w");
		fputs(sbuf_data(install_script), fp);
		fclose(fp);
@@ -334,14 +334,14 @@ pkg_register_old(struct pkg *pkg)
	}
	if (sbuf_len(deinstall_script) > 0) {
		sbuf_finish(deinstall_script);
-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+DEINSTALL", pkgdbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s/+DEINSTALL", pkgdbdir, name, version);
		fp = fopen(path, "w");
		fputs(sbuf_data(deinstall_script), fp);
		fclose(fp);
	}

	while (pkg_deps(pkg, &dep)) {
-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+REQUIRED_BY", pkgdbdir,
+
		snprintf(path, sizeof(path), "%s/%s-%s/+REQUIRED_BY", pkgdbdir,
		    pkg_dep_name(dep), pkg_dep_version(dep));
		fp = fopen(path, "a");
		fprintf(fp, "%s-%s\n", name, version);
modified libpkg/pkg_ports.c
@@ -490,7 +490,7 @@ meta_exec(struct plist *p, char *line, struct file_attr *a, bool unexec)
{
	char *cmd, *buf, *tmp;
	char comment[2];
-
	char path[MAXPATHLEN + 1];
+
	char path[MAXPATHLEN];
	regmatch_t pmatch[2];
	int ret;

modified libpkg/pkg_repo.c
@@ -42,6 +42,7 @@
#include <stdbool.h>
#include <sysexits.h>
#include <unistd.h>
+
#include <errno.h>

#include "pkg.h"
#include "private/event.h"
@@ -54,8 +55,8 @@
int
pkg_repo_fetch(struct pkg *pkg)
{
-
	char dest[MAXPATHLEN + 1];
-
	char url[MAXPATHLEN + 1];
+
	char dest[MAXPATHLEN];
+
	char url[MAXPATHLEN];
	int fetched = 0;
	char cksum[SHA256_DIGEST_LENGTH * 2 +1];
	char *path = NULL;
@@ -148,18 +149,101 @@ struct digest_list_entry {
	struct digest_list_entry *next;
};

+
struct pkg_conflict_bulk {
+
	struct pkg_conflict *conflicts;
+
	char *file;
+
	UT_hash_handle hh;
+
};
+

static int
digest_sort_compare_func(struct digest_list_entry *d1, struct digest_list_entry *d2)
{
	return strcmp(d1->origin, d2->origin);
}

+
static void
+
pkg_repo_new_conflict(const char *origin, struct pkg_conflict_bulk *bulk)
+
{
+
	struct pkg_conflict *new;
+

+
	pkg_conflict_new(&new);
+
	sbuf_set(&new->origin, origin);
+

+
	HASH_ADD_KEYPTR(hh, bulk->conflicts,
+
			__DECONST(char *, pkg_conflict_origin(new)),
+
			sbuf_size(new->origin), new);
+
}
+

+
static void
+
pkg_repo_write_conflicts (struct pkg_conflict_bulk *bulk, FILE *out)
+
{
+
	struct pkg_conflict_bulk	*pkg_bulk = NULL, *cur, *tmp, *s;
+
	struct pkg_conflict	*c1, *c1tmp, *c2, *c2tmp, *ctmp;
+
	bool new;
+

+
	/*
+
	 * Here we reorder bulk hash from hash by file
+
	 * to hash indexed by a package, so we iterate over the
+
	 * original hash and create a new hash indexed by package name
+
	 */
+

+
	HASH_ITER (hh, bulk, cur, tmp) {
+
		HASH_ITER (hh, cur->conflicts, c1, c1tmp) {
+
			HASH_FIND_STR(pkg_bulk, sbuf_get(c1->origin), s);
+
			if (s == NULL) {
+
				/* New entry required */
+
				s = malloc(sizeof(struct pkg_conflict_bulk));
+
				if (s == NULL) {
+
					pkg_emit_errno("malloc", "struct pkg_conflict_bulk");
+
					goto out;
+
				}
+
				memset(s, 0, sizeof(struct pkg_conflict_bulk));
+
				s->file = sbuf_get(c1->origin);
+
				HASH_ADD_KEYPTR(hh, pkg_bulk, s->file, strlen(s->file), s);
+
			}
+
			/* Now add all new entries from this file to this conflict structure */
+
			HASH_ITER (hh, cur->conflicts, c2, c2tmp) {
+
				new = true;
+
				if (strcmp(sbuf_get(c1->origin), sbuf_get(c2->origin)) == 0)
+
					continue;
+

+
				HASH_FIND_STR(s->conflicts, sbuf_get(c2->origin), ctmp);
+
				if (ctmp == NULL)
+
					pkg_repo_new_conflict(sbuf_get(c2->origin), s);
+
			}
+
		}
+
	}
+

+
	HASH_ITER (hh, pkg_bulk, cur, tmp) {
+
		fprintf(out, "%s:", cur->file);
+
		HASH_ITER (hh, cur->conflicts, c1, c1tmp) {
+
			if (c1->hh.next != NULL)
+
				fprintf(out, "%s,", sbuf_get(c1->origin));
+
			else
+
				fprintf(out, "%s\n", sbuf_get(c1->origin));
+
		}
+
	}
+
out:
+
	HASH_ITER (hh, pkg_bulk, cur, tmp) {
+
		HASH_ITER (hh, cur->conflicts, c1, c1tmp) {
+
			HASH_DEL(cur->conflicts, c1);
+
			sbuf_free(c1->origin);
+
			free(c1);
+
		}
+
		HASH_DEL(pkg_bulk, cur);
+
		free(cur);
+
	}
+
	return;
+
}
+

int
pkg_create_repo(char *path, const char *output_dir, bool filelist,
		void (progress)(struct pkg *pkg, void *data), void *data)
{
	FTS *fts = NULL;
	struct thd_data thd_data;
+
	struct pkg_conflict *c, *ctmp;
+
	struct pkg_conflict_bulk *conflicts = NULL, *curcb, *tmpcb;
	int num_workers;
	size_t len;
	pthread_t *tids = NULL;
@@ -168,11 +252,11 @@ pkg_create_repo(char *path, const char *output_dir, bool filelist,
	int retcode = EPKG_OK;

	char *repopath[2];
-
	char repodb[MAXPATHLEN + 1];
+
	char repodb[MAXPATHLEN];
	char *manifest_digest;
-
	FILE *psyml, *fsyml, *mandigests;
+
	FILE *psyml, *fsyml, *mandigests, *fconflicts;

-
	psyml = fsyml = mandigests = NULL;
+
	psyml = fsyml = mandigests = fconflicts = NULL;

	if (!is_dir(path)) {
		pkg_emit_error("%s is not a directory", path);
@@ -218,6 +302,12 @@ pkg_create_repo(char *path, const char *output_dir, bool filelist,
		goto cleanup;
	}

+
	snprintf(repodb, sizeof(repodb), "%s/%s", output_dir, repo_conflicts_file);
+
	if ((fconflicts = fopen(repodb, "w")) == NULL) {
+
		retcode = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

	thd_data.root_path = path;
	thd_data.max_results = num_workers;
	thd_data.num_results = 0;
@@ -296,7 +386,18 @@ pkg_create_repo(char *path, const char *output_dir, bool filelist,

	/* Now sort all digests */
	LL_SORT(dlist, digest_sort_compare_func);
+

+
	pkg_repo_write_conflicts(conflicts, fconflicts);
cleanup:
+
	HASH_ITER (hh, conflicts, curcb, tmpcb) {
+
		HASH_ITER (hh, curcb->conflicts, c, ctmp) {
+
			sbuf_free(c->origin);
+
			HASH_DEL(curcb->conflicts, c);
+
			free(c);
+
		}
+
		HASH_DEL(conflicts, curcb);
+
		free(curcb);
+
	}
	LL_FOREACH_SAFE(dlist, cur_dig, dtmp) {
		fprintf(mandigests, "%s:%s:%ld:%ld:%ld\n", cur_dig->origin,
		    cur_dig->digest, cur_dig->manifest_pos, cur_dig->files_pos,
@@ -328,6 +429,9 @@ cleanup:
	if (psyml != NULL)
		fclose(psyml);

+
	if (fconflicts != NULL)
+
		fclose(fconflicts);
+

	if (mandigests != NULL)
		fclose(mandigests);

@@ -342,9 +446,9 @@ read_pkg_file(void *data)
	struct pkg_manifest_key *keys = NULL;

	FTSENT *fts_ent = NULL;
-
	char fts_accpath[MAXPATHLEN + 1];
-
	char fts_path[MAXPATHLEN + 1];
-
	char fts_name[MAXPATHLEN + 1];
+
	char fts_accpath[MAXPATHLEN];
+
	char fts_path[MAXPATHLEN];
+
	char fts_name[MAXPATHLEN];
	off_t st_size;
	int fts_info, flags;

@@ -397,7 +501,8 @@ read_pkg_file(void *data)
		if (strcmp(fts_name, repo_db_archive) == 0 ||
			strcmp(fts_name, repo_packagesite_archive) == 0 ||
			strcmp(fts_name, repo_filesite_archive) == 0 ||
-
			strcmp(fts_name, repo_digests_archive) == 0)
+
			strcmp(fts_name, repo_digests_archive) == 0 ||
+
			strcmp(fts_name, repo_conflicts_archive) == 0)
			continue;
		*ext = '.';

@@ -586,8 +691,8 @@ int
pkg_finish_repo(const char *output_dir, pem_password_cb *password_cb,
    char **argv, int argc, bool filelist)
{
-
	char repo_path[MAXPATHLEN + 1];
-
	char repo_archive[MAXPATHLEN + 1];
+
	char repo_path[MAXPATHLEN];
+
	char repo_archive[MAXPATHLEN];
	struct rsa_key *rsa = NULL;
	struct stat st;
	int ret = EPKG_OK;
@@ -637,6 +742,14 @@ pkg_finish_repo(const char *output_dir, pem_password_cb *password_cb,
		ret = EPKG_FATAL;
		goto cleanup;
	}
+
	snprintf(repo_path, sizeof(repo_path), "%s/%s", output_dir, 
+
		repo_conflicts_file);
+
	snprintf(repo_archive, sizeof(repo_archive), "%s/%s", output_dir,
+
		repo_conflicts_archive);
+
	if (pack_db(repo_conflicts_file, repo_archive, repo_path, rsa, argv, argc) != EPKG_OK) {
+
		ret = EPKG_FATAL;
+
		goto cleanup;
+
	}

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

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

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

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

+
struct pkg_solve_variable {
+
	struct pkg *pkg;
+
	bool to_install;
+
	const char *digest;
+
	const char *origin;
+
	bool resolved;
+
	UT_hash_handle hd;
+
	UT_hash_handle ho;
+
	struct pkg_solve_variable *next;
+
};
+

+
struct pkg_solve_item {
+
	struct pkg_solve_variable *var;
+
	bool inverse;
+
	struct pkg_solve_item *next;
+
};
+

+
struct pkg_solve_rule {
+
	struct pkg_solve_item *items;
+
	struct pkg_solve_rule *next;
+
};
+

+
struct pkg_solve_problem {
+
	unsigned int rules_count;
+
	struct pkg_solve_rule *rules;
+
	struct pkg_solve_variable *variables_by_origin;
+
	struct pkg_solve_variable *variables_by_digest;
+
};
+

+
/*
+
 * Use XOR here to implement the following logic:
+
 * atom is true if it is installed and not inverted or
+
 * if it is not installed but inverted
+
 */
+
#define PKG_SOLVE_CHECK_ITEM(item)				\
+
	((item)->var->to_install ^ (item)->inverse)
+

+
/**
+
 * Check whether SAT rule is TRUE
+
 * @param rules list of rules
+
 * @return true or false
+
 */
+
static bool
+
pkg_solve_check_rules(struct pkg_solve_rule *rules)
+
{
+
	struct pkg_solve_rule *cur;
+
	struct pkg_solve_item *it;
+
	bool ret;
+

+
	LL_FOREACH(rules, cur) {
+

+
		ret = false;
+
		LL_FOREACH(cur->items, it) {
+
			if (it->var->resolved) {
+
				if (PKG_SOLVE_CHECK_ITEM(it))
+
					ret = true;
+
			}
+
		}
+
		if (!ret)
+
			return (false);
+

+
	}
+

+
	return (true);
+
}
+

+
/**
+
 * Propagate all units, must be called recursively
+
 * @param rules
+
 * @return true if there are units propagated
+
 */
+
static bool
+
pkg_solve_propagate_units(struct pkg_solve_rule *rules)
+
{
+
	struct pkg_solve_rule *cur;
+
	struct pkg_solve_item *it, *unresolved = NULL;
+
	int resolved = 0, total = 0, solved_vars;
+
	bool ret;
+

+
	do {
+
		solved_vars = 0;
+
		LL_FOREACH(rules, cur) {
+

+
			total = resolved = 0;
+
			LL_FOREACH(cur->items, it) {
+
				if (it->var->resolved && !PKG_SOLVE_CHECK_ITEM(it))
+
					resolved++;
+
				else
+
					unresolved = it;
+
				total ++;
+
			}
+
			/* It is a unit */
+
			if (total == resolved + 1 && unresolved != NULL) {
+
				if (!unresolved->var->resolved) {
+
					/* Propagate unit */
+
					unresolved->var->resolved = true;
+
					unresolved->var->to_install = !unresolved->inverse;
+
					solved_vars ++;
+
					pkg_debug(2, "propagate %s to %d", unresolved->var->origin, unresolved->var->to_install);
+
				}
+
				/* Now check for a conflict */
+
				ret = false;
+
				LL_FOREACH(cur->items, it) {
+
					if (PKG_SOLVE_CHECK_ITEM(it))
+
						ret = true;
+
				}
+
				/* A conflict found */
+
				if (!ret)
+
					return (false);
+
			}
+
		}
+
	} while (solved_vars > 0);
+

+
	return (true);
+
}
+

+

+
/**
+
 * Propagate pure clauses
+
 */
+
static bool
+
pkg_solve_propagate_pure(struct pkg_solve_rule *rules)
+
{
+
	struct pkg_solve_rule *cur;
+
	struct pkg_solve_item *it;
+

+
	LL_FOREACH(rules, cur) {
+
		it = cur->items;
+
		/* Unary rules */
+
		if (!it->var->resolved && it->next == NULL) {
+
			it->var->to_install = !it->inverse;
+
			it->var->resolved = true;
+
		}
+
	}
+

+
	return (false);
+
}
+

+
static bool
+
pkg_solve_check_conflicts(struct pkg_solve_rule *rules)
+
{
+
	struct pkg_solve_rule *cur;
+
	struct pkg_solve_item *it, *next;
+

+
	/*
+
	 * Conflicts are presented as:
+
	 * (!A | !B), so check for them
+
	 */
+
	LL_FOREACH(rules, cur) {
+
		it = cur->items;
+
		next = it->next;
+
		if (next != NULL && next->next == NULL) {
+
			if (it->var->resolved && !next->var->resolved) {
+
				if (!PKG_SOLVE_CHECK_ITEM(it))
+
					return (false);
+
			}
+
			else if (!it->var->resolved && next->var->resolved) {
+
				if (!PKG_SOLVE_CHECK_ITEM(next))
+
					return (false);
+
			}
+
		}
+
	}
+

+
	return (true);
+
}
+

+
/**
+
 * Use the default propagation policy:
+
 * - do not deinstall packages that are not in conflict
+
 * - do not install additional new packages
+
 *
+
 * This must be called after the explicit propagation
+
 */
+
static void
+
pkg_solve_propagate_default(struct pkg_solve_rule *rules)
+
{
+
	struct pkg_solve_rule *cur;
+
	struct pkg_solve_item *it;
+

+
	LL_FOREACH(rules, cur) {
+
		LL_FOREACH(cur->items, it) {
+
			if (!it->var->resolved) {
+
				if (it->var->pkg->type == PKG_INSTALLED) {
+
					it->var->to_install = true;
+
					if (pkg_solve_check_conflicts(rules)) {
+
						pkg_debug(2, "assume %s to %d", it->var->origin, it->var->to_install);
+
						it->var->resolved = true;
+
					}
+
				}
+
				else {
+
					it->var->to_install = false;
+
					if (pkg_solve_check_conflicts(rules)) {
+
						pkg_debug(2, "assume %s to %d", it->var->origin, it->var->to_install);
+
						it->var->resolved = true;
+
					}
+
				}
+
			}
+
		}
+
	}
+
}
+

+
/**
+
 * Try to solve sat problem
+
 * @param rules incoming rules to solve
+
 * @param nrules number of rules
+
 * @param nvars number of variables
+
 * @return
+
 */
+
bool
+
pkg_solve_sat_problem(struct pkg_solve_problem *problem)
+
{
+

+
	/* Initially propagate explicit rules */
+
	pkg_solve_propagate_pure(problem->rules);
+

+
	while (!pkg_solve_check_rules(problem->rules)) {
+
		/* TODO:
+
		 * 1) assign a free variable
+
		 * 2) check for contradictions
+
		 * 3) analyse and learn
+
		 * 4) undo an assignment
+
		 */
+
		if (!pkg_solve_propagate_units(problem->rules)) {
+
			pkg_emit_error("unimplemented: cannot solve SAT problem as units propagation has fallen");
+
			return (false);
+
		}
+
		pkg_solve_propagate_default(problem->rules);
+
	}
+

+
	return (true);
+
}
+

+
/*
+
 * Utilities to convert jobs to SAT rule
+
 */
+

+
static struct pkg_solve_item *
+
pkg_solve_item_new(struct pkg_solve_variable *var)
+
{
+
	struct pkg_solve_item *result;
+

+
	result = calloc(1, sizeof(struct pkg_solve_item));
+

+
	if(result == NULL) {
+
		pkg_emit_errno("calloc", "pkg_solve_item");
+
		return (NULL);
+
	}
+

+
	result->var = var;
+

+
	return (result);
+
}
+

+
static struct pkg_solve_rule *
+
pkg_solve_rule_new(void)
+
{
+
	struct pkg_solve_rule *result;
+

+
	result = calloc(1, sizeof(struct pkg_solve_rule));
+

+
	if(result == NULL) {
+
		pkg_emit_errno("calloc", "pkg_solve_rule");
+
		return (NULL);
+
	}
+

+
	return (result);
+
}
+

+
static struct pkg_solve_variable *
+
pkg_solve_variable_new(struct pkg *pkg)
+
{
+
	struct pkg_solve_variable *result;
+
	const char *digest, *origin;
+

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

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

+
	result->pkg = pkg;
+
	pkg_get(pkg, PKG_ORIGIN, &origin, PKG_DIGEST, &digest);
+
	/* XXX: Is it safe to save a ptr here ? */
+
	result->digest = digest;
+
	result->origin = origin;
+

+
	return (result);
+
}
+

+
static void
+
pkg_solve_rule_free(struct pkg_solve_rule *rule)
+
{
+
	struct pkg_solve_item *it, *tmp;
+

+
	LL_FOREACH_SAFE(rule->items, it, tmp) {
+
		free(it);
+
	}
+
	free(rule);
+
}
+

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

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

+
static int
+
pkg_solve_add_universe_variable(struct pkg_jobs *j,
+
		struct pkg_solve_problem *problem, const char *origin, struct pkg_solve_variable **var)
+
{
+
	struct pkg_job_universe_item *unit;
+
	struct pkg_solve_variable *nvar, *tvar;
+

+
	HASH_FIND_STR(j->universe, __DECONST(char *, origin), unit);
+
	/* If there is no package in universe, refuse continue */
+
	if (unit == NULL) {
+
		pkg_emit_error("package %s is not found in universe", origin);
+
		return (EPKG_FATAL);
+
	}
+
	/* Need to add a variable */
+
	nvar = pkg_solve_variable_new(unit->pkg);
+
	if (nvar == NULL)
+
		return (EPKG_FATAL);
+
	HASH_ADD_KEYPTR(ho, problem->variables_by_origin, nvar->origin, strlen(nvar->origin), nvar);
+
	HASH_ADD_KEYPTR(hd, problem->variables_by_digest, nvar->digest, strlen(nvar->digest), nvar);
+
	unit = unit->next;
+
	tvar = nvar;
+
	while (unit != NULL) {
+
		/* Add all alternatives as independent variables */
+
		tvar->next = pkg_solve_variable_new(unit->pkg);
+
		tvar = tvar->next;
+
		if (tvar == NULL)
+
			return (EPKG_FATAL);
+
		HASH_ADD_KEYPTR(hd, problem->variables_by_digest, tvar->digest, strlen(tvar->digest), tvar);
+
		unit = unit->next;
+
	}
+

+
	*var = nvar;
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
+
		struct pkg_solve_variable *pvar, bool conflicting)
+
{
+
	struct pkg_dep *dep, *dtmp;
+
	struct pkg_conflict *conflict, *ctmp;
+
	struct pkg *pkg;
+
	struct pkg_solve_rule *rule;
+
	struct pkg_solve_item *it = NULL;
+
	struct pkg_solve_variable *var, *tvar, *cur_var;
+

+
	const char *origin;
+

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

+
			origin = pkg_dep_get(dep, PKG_DEP_ORIGIN);
+
			HASH_FIND(ho, problem->variables_by_origin,
+
					__DECONST(char *, origin), strlen(origin), var);
+
			if (var == NULL) {
+
				if (pkg_solve_add_universe_variable(j, problem, origin, &var) != EPKG_OK)
+
					goto err;
+
			}
+
			/* Dependency rule: (!A | B) */
+
			rule = pkg_solve_rule_new();
+
			if (rule == NULL)
+
				goto err;
+
			/* !A */
+
			it = pkg_solve_item_new(cur_var);
+
			if (it == NULL)
+
				goto err;
+

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

+
				it->inverse = false;
+
				LL_PREPEND(rule->items, it);
+
			}
+

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

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

+
			origin = pkg_conflict_origin(conflict);
+
			HASH_FIND(ho, problem->variables_by_origin,
+
					__DECONST(char *, origin), strlen(origin), var);
+
			if (var == NULL) {
+
				if (pkg_solve_add_universe_variable(j, problem, origin, &var) != EPKG_OK)
+
					goto err;
+
			}
+
			/* Add conflict rule from each of the alternative */
+
			LL_FOREACH(var, tvar) {
+
				/* Conflict rule: (!A | !Bx) */
+
				rule = pkg_solve_rule_new();
+
				if (rule == NULL)
+
					goto err;
+
				/* !A */
+
				it = pkg_solve_item_new(cur_var);
+
				if (it == NULL)
+
					goto err;
+

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

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

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

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

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

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

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

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

+
struct pkg_solve_problem *
+
pkg_solve_jobs_to_sat(struct pkg_jobs *j)
+
{
+
	struct pkg_solve_problem *problem;
+
	struct pkg_job_request *jreq, *jtmp;
+
	struct pkg_solve_rule *rule;
+
	struct pkg_solve_item *it;
+
	struct pkg_job_universe_item *un, *utmp, *ucur;
+
	struct pkg_solve_variable *var, *tvar;
+
	const char *origin, *digest;
+

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

+
	if (problem == NULL) {
+
		pkg_emit_errno("calloc", "pkg_solve_problem");
+
		return (NULL);
+
	}
+

+
	/* Add requests */
+
	HASH_ITER(hh, j->request_add, jreq, jtmp) {
+
		rule = NULL;
+
		it = NULL;
+
		var = NULL;
+

+
		var = pkg_solve_variable_new(jreq->pkg);
+
		if (var == NULL)
+
			goto err;
+

+
		HASH_ADD_KEYPTR(hd, problem->variables_by_digest, var->digest, strlen(var->digest), var);
+
		HASH_ADD_KEYPTR(ho, problem->variables_by_origin, var->origin, strlen(var->origin), var);
+
		it = pkg_solve_item_new(var);
+
		if (it == NULL)
+
			goto err;
+

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

+
		/* Requests are unary rules */
+
		LL_PREPEND(rule->items, it);
+
		LL_PREPEND(problem->rules, rule);
+
		problem->rules_count ++;
+
	}
+
	HASH_ITER(hh, j->request_delete, jreq, jtmp) {
+
		rule = NULL;
+
		it = NULL;
+
		var = NULL;
+

+
		var = pkg_solve_variable_new(jreq->pkg);
+
		if (var == NULL)
+
			goto err;
+

+
		HASH_ADD_KEYPTR(hd, problem->variables_by_digest, var->digest, strlen(var->digest), var);
+
		HASH_ADD_KEYPTR(ho, problem->variables_by_origin, var->origin, strlen(var->origin), var);
+
		it = pkg_solve_item_new(var);
+
		if (it == NULL)
+
			goto err;
+

+
		it->inverse = true;
+
		rule = pkg_solve_rule_new();
+
		if (rule == NULL)
+
			goto err;
+

+
		/* Requests are unary rules */
+
		LL_PREPEND(rule->items, it);
+
		LL_PREPEND(problem->rules, rule);
+
		problem->rules_count ++;
+
	}
+

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

+
		/* Add corresponding variables */
+
		LL_FOREACH(un, ucur) {
+
			pkg_get(ucur->pkg, PKG_ORIGIN, &origin, PKG_DIGEST, &digest);
+
			HASH_FIND(hd, problem->variables_by_digest, digest, strlen(digest), var);
+
			if (var == NULL) {
+
				/* Add new variable */
+
				var = pkg_solve_variable_new(ucur->pkg);
+
				if (var == NULL)
+
					goto err;
+
				HASH_ADD_KEYPTR(hd, problem->variables_by_digest,
+
						var->digest, strlen(var->digest), var);
+

+
				/* Check origin */
+
				HASH_FIND(ho, problem->variables_by_origin, origin, strlen(origin), tvar);
+
				if (tvar == NULL) {
+
					HASH_ADD_KEYPTR(ho, problem->variables_by_origin,
+
							var->origin, strlen(var->origin), var);
+
				}
+
				else {
+
					/* Insert a variable to a chain */
+
					tvar->next = var;
+
				}
+
			}
+
		}
+
		HASH_FIND(ho, problem->variables_by_origin, origin, strlen(origin), var);
+
		/* Now `var' contains a variables chain related to this origin */
+
		if (pkg_solve_add_pkg_rule(j, problem, var, true) == EPKG_FATAL)
+
			goto err;
+
	}
+

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

+
struct pkg_solve_ordered_variable {
+
	struct pkg_solve_variable *var;
+
	int order;
+
	UT_hash_handle hh;
+
};
+

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

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

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

+
	LL_FOREACH(problem->rules, rule) {
+
		LL_FOREACH(rule->items, it) {
+
			HASH_FIND_PTR(ordered_variables, &it->var, nord);
+
			if (nord != NULL) {
+
				fprintf(f, "%s%d ", (it->inverse ? "-" : ""), nord->order);
+
			}
+
		}
+
		fprintf(f, "0\n");
+
	}
+

+
	HASH_FREE(ordered_variables, pkg_solve_ordered_variable, free);
+

+
	return (EPKG_OK);
+
}
+

+
int
+
pkg_solve_sat_to_jobs(struct pkg_solve_problem *problem, struct pkg_jobs *j)
+
{
+
	struct pkg_solve_variable *var, *vtmp;
+
	const char *origin;
+

+
	HASH_ITER(hd, problem->variables_by_digest, var, vtmp) {
+
		if (!var->resolved)
+
			return (EPKG_FATAL);
+

+
		pkg_get(var->pkg, PKG_ORIGIN, &origin);
+
		if (var->to_install && var->pkg->type != PKG_INSTALLED)
+
			HASH_ADD_KEYPTR(hh, j->jobs_add, origin, strlen(origin), var->pkg);
+
		else if (!var->to_install && var->pkg->type == PKG_INSTALLED)
+
			HASH_ADD_KEYPTR(hh, j->jobs_delete, origin, strlen(origin), var->pkg);
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
int
+
pkg_solve_parse_sat_output(FILE *f, struct pkg_solve_problem *problem, struct pkg_jobs *j)
+
{
+
	struct pkg_solve_ordered_variable *ordered_variables = NULL, *nord;
+
	struct pkg_solve_variable *var, *vtmp;
+
	int cur_ord = 1, ret = EPKG_OK;
+
	char *line = NULL, *var_str, *begin;
+
	size_t linecap = 0;
+
	ssize_t linelen;
+
	bool got_sat = false, done = false;
+

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

+
	while ((linelen = getline(&line, &linecap, f)) > 0) {
+
		if (strncmp(line, "SAT", 3) == 0) {
+
			got_sat = true;
+
		}
+
		else if (got_sat) {
+
			begin = line;
+
			do {
+
				var_str = strsep(&begin, " \t");
+
				/* Skip unexpected lines */
+
				if (var_str == NULL || (!isdigit(*var_str) && *var_str != '-'))
+
					continue;
+
				cur_ord = 0;
+
				cur_ord = abs(strtol(var_str, NULL, 10));
+
				if (cur_ord == 0) {
+
					done = true;
+
					break;
+
				}
+

+
				HASH_FIND_INT(ordered_variables, &cur_ord, nord);
+
				if (nord != NULL) {
+
					nord->var->resolved = true;
+
					nord->var->to_install = (*var_str != '-');
+
				}
+
			} while (begin != NULL);
+
		}
+
		else if (strncmp(line, "v ", 2) == 0) {
+
			begin = line + 2;
+
			do {
+
				var_str = strsep(&begin, " \t");
+
				/* Skip unexpected lines */
+
				if (var_str == NULL || (!isdigit(*var_str) && *var_str != '-'))
+
					continue;
+
				cur_ord = 0;
+
				cur_ord = abs(strtol(var_str, NULL, 10));
+
				if (cur_ord == 0) {
+
					done = true;
+
					break;
+
				}
+

+
				HASH_FIND_INT(ordered_variables, &cur_ord, nord);
+
				if (nord != NULL) {
+
					nord->var->resolved = true;
+
					nord->var->to_install = (*var_str != '-');
+
				}
+
			} while (begin != NULL);
+
		}
+
		else {
+
			/* Slightly ignore anything from solver */
+
			continue;
+
		}
+
	}
+

+
	if (done)
+
		ret = pkg_solve_sat_to_jobs(problem, j);
+
	else {
+
		pkg_emit_error("cannot parse sat solver output");
+
		ret = EPKG_FATAL;
+
	}
+

+
	HASH_FREE(ordered_variables, pkg_solve_ordered_variable, free);
+
	if (line != NULL)
+
		free(line);
+
	return (ret);
+
}
modified libpkg/pkgdb.c
@@ -71,7 +71,7 @@
*/

#define DB_SCHEMA_MAJOR	0
-
#define DB_SCHEMA_MINOR	21
+
#define DB_SCHEMA_MINOR	22

#define DBVERSION (DB_SCHEMA_MAJOR * 1000 + DB_SCHEMA_MINOR)

@@ -632,6 +632,23 @@ pkgdb_init(sqlite3 *sdb)
		      " ON DELETE CASCADE ON UPDATE RESTRICT,"
		"UNIQUE (package_id, tag_id)"
	");"
+
	"CREATE TABLE pkg_conflicts ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "conflict_id INTEGER NOT NULL,"
+
	    "UNIQUE(package_id, conflict_id)"
+
	");"
+
	"CREATE TABLE provides("
+
	"    id INTEGER PRIMARY KEY,"
+
	"    provide TEXT NOT NULL"
+
	");"
+
	"CREATE TABLE pkg_provides ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "provide_id INTEGER NOT NULL REFERENCES provides(id)"
+
	    "  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
	    "UNIQUE(package_id, provide_id)"
+
	");"

	/* Mark the end of the array */

@@ -649,6 +666,9 @@ pkgdb_init(sqlite3 *sdb)
	"CREATE INDEX pkg_directories_directory_id ON pkg_directories (directory_id);"
	"CREATE INDEX pkg_annotation_package_id ON pkg_annotation(package_id);"
	"CREATE INDEX pkg_digest_id ON packages(origin, manifestdigest);"
+
	"CREATE INDEX pkg_conflicts_pid ON pkg_conflicts(package_id);"
+
	"CREATE INDEX pkg_conflicts_cid ON pkg_conflicts(conflict_id);"
+
	"CREATE INDEX pkg_provides_id ON pkg_provides(package_id);"

	"CREATE VIEW pkg_shlibs AS SELECT * FROM pkg_shlibs_required;"
	"CREATE TRIGGER pkg_shlibs_update "
@@ -764,6 +784,9 @@ pkgdb_remote_init(struct pkgdb *db, const char *repo)
	"BEGIN;"
	"CREATE INDEX IF NOT EXISTS '%s'.deps_origin ON deps(origin);"
	"CREATE INDEX IF NOT EXISTS '%s'.pkg_digest_id ON packages(origin, manifestdigest);"
+
	"CREATE INDEX IF NOT EXISTS '%s'.pkg_conflicts_pid ON pkg_conflicts(package_id);"
+
	"CREATE INDEX IF NOT EXISTS '%s'.pkg_conflicts_cid ON pkg_conflicts(conflict_id);"
+
	"CREATE INDEX IF NOT EXISTS '%s'.pkg_provides_id ON pkg_provides(package_id);"
	"COMMIT;"
	;

@@ -772,7 +795,7 @@ pkgdb_remote_init(struct pkgdb *db, const char *repo)
	}

	sql = sbuf_new_auto();
-
	sbuf_printf(sql, init_sql, reponame, reponame);
+
	sbuf_printf(sql, init_sql, reponame, reponame, reponame, reponame, reponame);

	ret = sql_exec(db->sqlite, sbuf_data(sql));
	sbuf_delete(sql);
@@ -783,7 +806,7 @@ static int
pkgdb_open_multirepos(const char *dbdir, struct pkgdb *db)
{
	int		  ret;
-
	char		  remotepath[MAXPATHLEN + 1];
+
	char		  remotepath[MAXPATHLEN];
	struct pkg_repo	 *r = NULL;
	int		  repocount = 0;

@@ -898,7 +921,7 @@ file_mode_insecure(const char *path, bool install_as_user)
static int
database_access(unsigned mode, const char* dbdir, const char *dbname)
{
-
	char		 dbpath[MAXPATHLEN + 1];
+
	char		 dbpath[MAXPATHLEN];
	int		 retval;
	bool		 database_exists;
	bool		 install_as_user;
@@ -1034,7 +1057,7 @@ pkgdb_open(struct pkgdb **db_p, pkgdb_t type)
	struct pkgdb	*db = NULL;
	struct statfs	 stfs;
	bool		 reopen = false;
-
	char		 localpath[MAXPATHLEN + 1];
+
	char		 localpath[MAXPATHLEN];
	const char	*dbdir = NULL;
	bool		 create = false;
	bool		 createdir = false;
@@ -1348,6 +1371,8 @@ static struct load_on_flag {
	{ PKG_LOAD_SHLIBS_REQUIRED,	pkgdb_load_shlib_required },
	{ PKG_LOAD_SHLIBS_PROVIDED,	pkgdb_load_shlib_provided },
	{ PKG_LOAD_ANNOTATIONS,		pkgdb_load_annotations },
+
	{ PKG_LOAD_CONFLICTS,		pkgdb_load_conflicts },
+
	{ PKG_LOAD_PROVIDES,		pkgdb_load_provides },
	{ -1,			        NULL }
};

@@ -2204,6 +2229,54 @@ pkgdb_load_mtree(struct pkgdb *db, struct pkg *pkg)
	return (load_val(db->sqlite, pkg, sql, PKG_LOAD_MTREE, pkg_set_mtree, -1));
}

+
int
+
pkgdb_load_conflicts(struct pkgdb *db, struct pkg *pkg)
+
{
+
	char		 sql[BUFSIZ];
+
	const char	*reponame = NULL;
+
	const char	*basesql = ""
+
			"SELECT packages.origin "
+
			"FROM %Q.pkg_conflicts "
+
			"LEFT JOIN %Q.packages ON "
+
			"packages.id = pkg_conflicts.conflict_id "
+
			"WHERE package_id = ?1";
+

+
	assert(db != NULL && pkg != NULL);
+

+
	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
+
		pkg_get(pkg, PKG_REPONAME, &reponame);
+
		sqlite3_snprintf(sizeof(sql), sql, basesql, reponame, reponame);
+
	} else
+
		sqlite3_snprintf(sizeof(sql), sql, basesql, "main", "main");
+

+
	return (load_val(db->sqlite, pkg, sql, PKG_LOAD_CONFLICTS,
+
			pkg_addconflict, PKG_CONFLICTS));
+
}
+

+
int
+
pkgdb_load_provides(struct pkgdb *db, struct pkg *pkg)
+
{
+
	char		 sql[BUFSIZ];
+
	const char	*reponame = NULL;
+
	const char	*basesql = ""
+
		"SELECT provide "
+
		"FROM %Q.provides "
+
		"WHERE package_id = ?1";
+

+
	assert(db != NULL && pkg != NULL);
+

+
	if (pkg->type == PKG_REMOTE) {
+
		assert(db->type == PKGDB_REMOTE);
+
		pkg_get(pkg, PKG_REPONAME, &reponame);
+
		sqlite3_snprintf(sizeof(sql), sql, basesql, reponame, reponame);
+
	} else
+
		sqlite3_snprintf(sizeof(sql), sql, basesql, "main", "main");
+

+
	return (load_val(db->sqlite, pkg, sql, PKG_LOAD_PROVIDES,
+
			pkg_addconflict, PKG_PROVIDES));
+
}
+

typedef enum _sql_prstmt_index {
	MTREE = 0,
	PKG,
@@ -2233,6 +2306,9 @@ typedef enum _sql_prstmt_index {
	ANNOTATE_ADD1,
	ANNOTATE_DEL1,
	ANNOTATE_DEL2,
+
	CONFLICT,
+
	PKG_PROVIDE,
+
	PROVIDE,
	PRSTMT_LAST,
} sql_prstmt_index;

@@ -2247,10 +2323,10 @@ static sql_prstmt sql_prepared_statements[PRSTMT_LAST] = {
		"INSERT OR REPLACE INTO packages( "
			"origin, name, version, comment, desc, message, arch, "
			"maintainer, www, prefix, flatsize, automatic, "
-
			"licenselogic, mtree_id, time) "
+
			"licenselogic, mtree_id, time, manifestdigest) "
		"VALUES( ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, "
-
		"?13, (SELECT id FROM mtree WHERE content = ?14), NOW())",
-
		"TTTTTTTTTTIIIT",
+
		"?13, (SELECT id FROM mtree WHERE content = ?14), NOW(), ?14)",
+
		"TTTTTTTTTTIIITT",
	},
	[DEPS_UPDATE] = {
		NULL,
@@ -2413,6 +2489,23 @@ static sql_prstmt sql_prepared_statements[PRSTMT_LAST] = {
		" annotation_id NOT IN (SELECT value_id FROM pkg_annotation)",
		"",
	},
+
	[CONFLICT] = {
+
		NULL,
+
		"INSERT INTO pkg_conflicts(package_id, conflict_id) "
+
		"VALUES (?1, (SELECT id FROM packages WHERE origin = ?2))",
+
		"IT",
+
	},
+
	[PKG_PROVIDE] = {
+
		NULL,
+
		"INSERT INTO pkg_provides(package_id, provide_id) "
+
		"VALUES (?1, (SELECT id FROM provides WHERE provide = ?2))",
+
		"IT",
+
	},
+
	{
+
		NULL,
+
		"INSERT OR IGNORE INTO provides(provide) VALUES(?1)",
+
		"T",
+
	}
	/* PRSTMT_LAST */
};

@@ -2505,6 +2598,7 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
	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;

	sqlite3			*s;
@@ -2516,6 +2610,7 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
	const char		*mtree, *origin, *name, *version, *name2;
	const char		*version2, *comment, *desc, *message;
	const char		*arch, *maintainer, *www, *prefix;
+
	const char		*digest;

	bool			 automatic;
	lic_t			 licenselogic;
@@ -2550,7 +2645,8 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
		PKG_FLATSIZE,	&flatsize,
		PKG_AUTOMATIC,	&automatic,
		PKG_LICENSE_LOGIC, &licenselogic,
-
		PKG_NAME,	&name);
+
		PKG_NAME,	&name,
+
		PKG_DIGEST,	&digest);

	/*
	 * Insert mtree record
@@ -2566,7 +2662,7 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
	 */
	ret = run_prstmt(PKG, origin, name, version, comment, desc, message,
	    arch, maintainer, www, prefix, flatsize, (int64_t)automatic,
-
	    (int64_t)licenselogic, mtree);
+
	    (int64_t)licenselogic, mtree, digest);
	if (ret != SQLITE_DONE) {
		ERROR_SQLITE(s);
		goto cleanup;
@@ -2780,6 +2876,23 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced)
	if (pkgdb_insert_annotations(pkg, package_id, s) != EPKG_OK)
		goto cleanup;

+
	/*
+
	 * Insert conflicts
+
	 */
+
	while (pkg_conflicts(pkg, &conflict) == EPKG_OK) {
+
		if (run_prstmt(CONFLICT, package_id, pkg_conflict_origin(conflict))
+
				!= SQLITE_DONE) {
+
			ERROR_SQLITE(s);
+
			goto cleanup;
+
		}
+
	}
+

+
	/*
+
	 * Insert provides
+
	 */
+
	if (pkgdb_update_provides(pkg, package_id, s) != EPKG_OK)
+
		goto cleanup;
+

	retcode = EPKG_OK;

	cleanup:
@@ -2809,7 +2922,6 @@ pkgdb_insert_scripts(struct pkg *pkg, int64_t package_id, sqlite3 *s)
	return (EPKG_OK);
}

-

int
pkgdb_update_shlibs_required(struct pkg *pkg, int64_t package_id, sqlite3 *s)
{
@@ -2849,6 +2961,25 @@ pkgdb_update_shlibs_provided(struct pkg *pkg, int64_t package_id, sqlite3 *s)
}

int
+
pkgdb_update_provides(struct pkg *pkg, int64_t package_id, sqlite3 *s)
+
{
+
	struct pkg_provide	*provide = NULL;
+

+
	while (pkg_provides(pkg, &provide) == EPKG_OK) {
+
		if (run_prstmt(PROVIDE, pkg_provide_name(provide))
+
		    != SQLITE_DONE
+
		    ||
+
		    run_prstmt(PKG_PROVIDE, package_id, pkg_provide_name(provide))
+
		    != SQLITE_DONE) {
+
			ERROR_SQLITE(s);
+
			return (EPKG_FATAL);
+
		}
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
int
pkgdb_insert_annotations(struct pkg *pkg, int64_t package_id, sqlite3 *s)
{
	struct pkg_note	*note = NULL;
@@ -3404,7 +3535,7 @@ pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match,
		"SELECT id, origin, name, version, comment, "
		"prefix, desc, arch, maintainer, www, "
		"licenselogic, flatsize, pkgsize, "
-
		"cksum, path AS repopath, '%1$s' AS dbname "
+
		"cksum, manifestdigest, path AS repopath, '%1$s' AS dbname "
		"FROM '%1$s'.packages p";

	assert(db != NULL);
@@ -3437,7 +3568,7 @@ pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match,
	sbuf_finish(sql);

	pkg_debug(4, "Pkgdb: running '%s'", sbuf_get(sql));
-
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL);
+
	ret = sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), sbuf_size(sql), &stmt, NULL);
	if (ret != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		sbuf_delete(sql);
@@ -3452,6 +3583,60 @@ pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match,
	return (pkgdb_it_new(db, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE));
}

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

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

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

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

+
	sbuf_finish(sql);
+

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

+
	sbuf_delete(sql);
+

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

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

static int
pkgdb_search_build_search_query(struct sbuf *sql, match_t match,
    pkgdb_field field, pkgdb_field sort)
@@ -3952,7 +4137,7 @@ pkgdb_cmd(int argc, char **argv)
void
pkgshell_open(const char **reponame)
{
-
	char		 localpath[MAXPATHLEN + 1];
+
	char		 localpath[MAXPATHLEN];
	const char	*dbdir;

	sqlite3_auto_extension((void(*)(void))sqlcmd_init);
modified libpkg/pkgdb_repo.c
@@ -60,8 +60,9 @@
/* The package repo schema minor revision.
   Minor schema changes don't prevent older pkgng
   versions accessing the repo. */
-
#define REPO_SCHEMA_MINOR 6
+
#define REPO_SCHEMA_MINOR 7

+
/* REPO_SCHEMA_VERSION=2007 */
#define REPO_SCHEMA_VERSION (REPO_SCHEMA_MAJOR * 1000 + REPO_SCHEMA_MINOR)

typedef enum _sql_prstmt_index {
@@ -197,7 +198,7 @@ file_exists(sqlite3_context *ctx, int argc, sqlite3_value **argv)
		return;
	}

-
	snprintf(fpath, MAXPATHLEN, "%s/%s", path, sqlite3_value_text(argv[0]));
+
	snprintf(fpath, sizeof(fpath), "%s/%s", path, sqlite3_value_text(argv[0]));

	if (access(fpath, R_OK) == 0) {
		sha256_file(fpath, cksum);
@@ -373,10 +374,6 @@ pkgdb_repo_init(sqlite3 *sqlite)
	if (retcode != EPKG_OK)
		return (retcode);

-
	retcode = sql_exec(sqlite, "PRAGMA journal_mode=memory");
-
	if (retcode != EPKG_OK)
-
		return (retcode);
-

	retcode = sql_exec(sqlite, "PRAGMA foreign_keys=on");
	if (retcode != EPKG_OK)
		return (retcode);
@@ -732,7 +729,7 @@ apply_repo_change(struct pkgdb *db, const char *database,

	/* begin transaction */
	if (ret == EPKG_OK)
-
		ret = pkgdb_transaction_begin(db->sqlite, NULL);
+
		ret = pkgdb_transaction_begin(db->sqlite, "SCHEMA");

	/* apply change */
	if (ret == EPKG_OK) {
@@ -752,9 +749,9 @@ apply_repo_change(struct pkgdb *db, const char *database,

	/* commit or rollback */
	if (ret == EPKG_OK)
-
		ret = pkgdb_transaction_commit(db->sqlite, NULL);
+
		ret = pkgdb_transaction_commit(db->sqlite, "SCHEMA");
	else
-
		pkgdb_transaction_rollback(db->sqlite, NULL);
+
		pkgdb_transaction_rollback(db->sqlite, "SCHEMA");

	if (ret == EPKG_OK) {
		pkg_emit_notice("Repo \"%s\" %s schema %d to %d: %s",
@@ -775,7 +772,7 @@ upgrade_repo_schema(struct pkgdb *db, const char *database, int current_version)
	for (version = current_version;
	     version < REPO_SCHEMA_VERSION;
	     version = next_version)  {
-
		pkg_debug(1, "Upgrading remote database from %d to %d",
+
		pkg_debug(1, "Upgrading repo database schema from %d to %d",
		    version, next_version);
		ret = apply_repo_change(db, database, repo_upgrades,
					"upgrade", version, &next_version);
@@ -795,7 +792,7 @@ downgrade_repo_schema(struct pkgdb *db, const char *database, int current_versio
	for (version = current_version;
	     version > REPO_SCHEMA_VERSION;
	     version = next_version)  {
-
		pkg_debug(1, "Upgrading remote database from %d to %d",
+
		pkg_debug(1, "Downgrading repo database schema from %d to %d",
		    version, next_version);
		ret = apply_repo_change(db, database, repo_downgrades,
					"downgrade", version, &next_version);
@@ -913,3 +910,94 @@ pkgdb_repo_origins(sqlite3 *sqlite)

	return pkgdb_it_new(&repodb, stmt, PKG_REMOTE, PKGDB_IT_FLAG_ONCE);
}
+

+
int
+
pkgdb_repo_register_conflicts(const char *origin, char **conflicts,
+
		int conflicts_num, sqlite3 *sqlite)
+
{
+
	const char clean_conflicts_sql[] = ""
+
			"DELETE FROM pkg_conflicts "
+
			"WHERE package_id = ?1;";
+
	const char select_id_sql[] = ""
+
			"SELECT id FROM packages "
+
			"WHERE origin = ?1;";
+
	const char insert_conflict_sql[] = ""
+
			"INSERT INTO pkg_conflicts "
+
			"(package_id, conflict_id) "
+
			"VALUES (?1, ?2);";
+
	sqlite3_stmt *stmt = NULL;
+
	int ret, i;
+
	int64_t origin_id, conflict_id;
+

+
	pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", select_id_sql);
+
	if (sqlite3_prepare_v2(sqlite, select_id_sql, -1, &stmt, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(sqlite);
+
		return (EPKG_FATAL);
+
	}
+

+
	sqlite3_bind_text(stmt, 1, origin, -1, SQLITE_TRANSIENT);
+
	ret = sqlite3_step(stmt);
+

+
	if (ret == SQLITE_ROW) {
+
		origin_id = sqlite3_column_int64(stmt, 0);
+
	}
+
	else {
+
		ERROR_SQLITE(sqlite);
+
		return (EPKG_FATAL);
+
	}
+
	sqlite3_finalize(stmt);
+

+
	pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", clean_conflicts_sql);
+
	if (sqlite3_prepare_v2(sqlite, clean_conflicts_sql, -1, &stmt, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(sqlite);
+
		return (EPKG_FATAL);
+
	}
+

+
	sqlite3_bind_int64(stmt, 1, origin_id);
+
	/* Ignore cleanup result */
+
	(void)sqlite3_step(stmt);
+

+
	sqlite3_finalize(stmt);
+

+
	for (i = 0; i < conflicts_num; i ++) {
+
		/* Select a conflict */
+
		pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", select_id_sql);
+
		if (sqlite3_prepare_v2(sqlite, select_id_sql, -1, &stmt, NULL) != SQLITE_OK) {
+
			ERROR_SQLITE(sqlite);
+
			return (EPKG_FATAL);
+
		}
+

+
		sqlite3_bind_text(stmt, 1, conflicts[i], -1, SQLITE_TRANSIENT);
+
		ret = sqlite3_step(stmt);
+

+
		if (ret == SQLITE_ROW) {
+
			conflict_id = sqlite3_column_int64(stmt, 0);
+
		}
+
		else {
+
			ERROR_SQLITE(sqlite);
+
			return (EPKG_FATAL);
+
		}
+

+
		sqlite3_finalize(stmt);
+

+
		/* Insert a pair */
+
		pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", insert_conflict_sql);
+
		if (sqlite3_prepare_v2(sqlite, insert_conflict_sql, -1, &stmt, NULL) != SQLITE_OK) {
+
			ERROR_SQLITE(sqlite);
+
			return (EPKG_FATAL);
+
		}
+

+
		sqlite3_bind_int64(stmt, 1, origin_id);
+
		sqlite3_bind_int64(stmt, 2, conflict_id);
+
		ret = sqlite3_step(stmt);
+

+
		if (ret != SQLITE_DONE) {
+
			ERROR_SQLITE(sqlite);
+
			return (EPKG_FATAL);
+
		}
+

+
		sqlite3_finalize(stmt);
+
	}
+

+
	return (EPKG_OK);
+
}
modified libpkg/plugins.c
@@ -394,7 +394,7 @@ pkg_plugins_init(void)
		/*
		 * Load the plugin
		 */
-
		snprintf(pluginfile, MAXPATHLEN, "%s/%s.so", plugdir,
+
		snprintf(pluginfile, sizeof(pluginfile), "%s/%s.so", plugdir,
		    pkg_config_value(v));
		p = calloc(1, sizeof(struct pkg_plugin));
		if ((p->lh = dlopen(pluginfile, RTLD_LAZY)) == NULL) {
modified libpkg/private/db_upgrades.h
@@ -2,6 +2,7 @@
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
+
 * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -535,6 +536,26 @@ static struct db_upgrades {
			"( SELECT DISTINCT option_id FROM pkg_option );"
	"END;"
	},
+
	{22,
+
	"CREATE TABLE pkg_conflicts ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "conflict_id INTEGER NOT NULL,"
+
	    "UNIQUE(package_id, conflict_id)"
+
	");"
+
	"CREATE TABLE provides("
+
	"    id INTEGER PRIMARY KEY,"
+
	"    provide TEXT NOT NULL"
+
	");"
+
	"CREATE TABLE pkg_provides ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "provide_id INTEGER NOT NULL REFERENCES provides(id)"
+
	    "  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
	    "UNIQUE(package_id, provide_id)"
+
	");"
+
	},
+


	/* Mark the end of the array */
	{ -1, NULL }
modified libpkg/private/pkg.h
@@ -2,6 +2,7 @@
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
+
 * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -47,8 +48,6 @@
#define PKG_NUM_SCRIPTS 9

#if ARCHIVE_VERSION_NUMBER < 3000002
-
#define archive_read_free(a) archive_read_finish(a)
-
#define archive_write_free(a) archive_write_finish(a)
#define archive_write_add_filter_xz(a) archive_write_set_compression_xz(a)
#define archive_write_add_filter_bzip2(a) archive_write_set_compression_bzip2(a)
#define archive_write_add_filter_gzip(a) archive_write_set_compression_gzip(a)
@@ -128,6 +127,8 @@ struct pkg {
	struct pkg_shlib	*shlibs_required;
	struct pkg_shlib	*shlibs_provided;
	struct pkg_note		*annotations;
+
	struct pkg_conflict *conflicts;
+
	struct pkg_provide	*provides;
	unsigned       	 flags;
	int64_t		 rowid;
	int64_t		 time;
@@ -145,6 +146,16 @@ struct pkg_dep {
	UT_hash_handle	 hh;
};

+
struct pkg_conflict {
+
	struct sbuf		*origin;
+
	UT_hash_handle	hh;
+
};
+

+
struct pkg_provide {
+
	struct sbuf		*provide;
+
	UT_hash_handle	hh;
+
};
+

struct pkg_license {
	/* should be enough to match a license name */
	char name[64];
@@ -157,20 +168,20 @@ struct pkg_category {
};

struct pkg_file {
-
	char		 path[MAXPATHLEN +1];
+
	char		 path[MAXPATHLEN];
	int64_t		 size;
-
	char		 sum[SHA256_DIGEST_LENGTH * 2 +1];
-
	char		 uname[MAXLOGNAME +1];
-
	char		 gname[MAXLOGNAME +1];
+
	char		 sum[SHA256_DIGEST_LENGTH * 2 + 1];
+
	char		 uname[MAXLOGNAME];
+
	char		 gname[MAXLOGNAME];
	bool		 keep;
	mode_t		 perm;
	UT_hash_handle	 hh;
};

struct pkg_dir {
-
	char		 path[MAXPATHLEN +1];
-
	char		 uname[MAXLOGNAME +1];
-
	char		 gname[MAXLOGNAME +1];
+
	char		 path[MAXPATHLEN];
+
	char		 uname[MAXLOGNAME];
+
	char		 gname[MAXLOGNAME];
	mode_t		 perm;
	bool		 keep;
	bool		 try;
@@ -185,10 +196,31 @@ struct pkg_option {
	UT_hash_handle	hh;
};

+
struct pkg_job_request {
+
	struct pkg *pkg;
+
	bool skip;
+
	UT_hash_handle hh;
+
};
+

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

+
struct pkg_job_universe_item {
+
	struct pkg *pkg;
+
	UT_hash_handle hh;
+
	struct pkg_job_universe_item *next;
+
};
+

struct pkg_jobs {
-
	struct pkg	*jobs;
-
	struct pkg 	*bulk;
-
	struct pkg	*seen;
+
	struct pkg_job_universe_item *universe;
+
	struct pkg_job_request	*request_add;
+
	struct pkg_job_request	*request_delete;
+
	struct pkg *jobs_add;
+
	struct pkg *jobs_delete;
+
	struct pkg_job_seen *seen;
	struct pkgdb	*db;
	pkg_jobs_t	 type;
	pkg_flags	 flags;
@@ -204,13 +236,13 @@ struct job_pattern {
};

struct pkg_user {
-
	char		 name[MAXLOGNAME+1];
+
	char		 name[MAXLOGNAME];
	char		 uidstr[8192];/* taken from pw_util.c */
	UT_hash_handle	hh;
};

struct pkg_group {
-
	char		 name[MAXLOGNAME+1];
+
	char		 name[MAXLOGNAME];
	char		 gidstr[8192]; /* taken from gw_util.c */
	UT_hash_handle	hh;
};
@@ -366,6 +398,12 @@ int pkg_jobs_resolv(struct pkg_jobs *jobs);
int pkg_shlib_new(struct pkg_shlib **);
void pkg_shlib_free(struct pkg_shlib *);

+
int pkg_conflict_new(struct pkg_conflict **);
+
void pkg_conflict_free(struct pkg_conflict *);
+

+
int pkg_provide_new(struct pkg_provide **);
+
void pkg_provide_free(struct pkg_provide *);
+

int pkg_annotation_new(struct pkg_note **);
void pkg_annotation_free(struct pkg_note *);

@@ -413,10 +451,13 @@ int pkgdb_load_group(struct pkgdb *db, struct pkg *pkg);
int pkgdb_load_shlib_required(struct pkgdb *db, struct pkg *pkg);
int pkgdb_load_shlib_provided(struct pkgdb *db, struct pkg *pkg);
int pkgdb_load_annotations(struct pkgdb *db, struct pkg *pkg);
+
int pkgdb_load_conflicts(struct pkgdb *db, struct pkg *pkg);
+
int pkgdb_load_provides(struct pkgdb *db, struct pkg *pkg);

int pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete, int forced);
int pkgdb_update_shlibs_required(struct pkg *pkg, int64_t package_id, sqlite3 *s);
int pkgdb_update_shlibs_provided(struct pkg *pkg, int64_t package_id, sqlite3 *s);
+
int pkgdb_update_provides(struct pkg *pkg, int64_t package_id, sqlite3 *s);
int pkgdb_insert_annotations(struct pkg *pkg, int64_t package_id, sqlite3 *s);
int pkgdb_register_finale(struct pkgdb *db, int retcode);

modified libpkg/private/pkgdb.h
@@ -137,4 +137,15 @@ int pkgdb_repo_check_version(struct pkgdb *db, const char *database);
 */
struct pkgdb_it *pkgdb_repo_origins(sqlite3 *sqlite);

+
/**
+
 * Register a conflicts list in a repo
+
 * @param origin the origin of a package
+
 * @param conflicts a list of conflicts origins
+
 * @param conflicts_num number of conflicts for this package
+
 * @param sqlite database
+
 * @return error code
+
 */
+
int pkgdb_repo_register_conflicts(const char *origin, char **conflicts,
+
		int conflicts_num, sqlite3 *sqlite);
+

#endif
modified libpkg/private/repodb.h
@@ -37,6 +37,8 @@ static const char repo_filesite_file[] = "filesite.yaml";
static const char repo_filesite_archive[] = "filesite";
static const char repo_digests_file[] = "digests";
static const char repo_digests_archive[] = "digests";
+
static const char repo_conflicts_file[] = "conflicts";
+
static const char repo_conflicts_archive[] = "conflicts";

static const char initsql[] = ""
	"CREATE TABLE packages ("
@@ -155,6 +157,23 @@ static const char initsql[] = ""
	    " ON DELETE CASCADE ON UPDATE RESTRICT,"
	    "UNIQUE (package_id, tag_id)"
	");"
+
	"CREATE TABLE pkg_conflicts ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "conflict_id INTEGER NOT NULL,"
+
	    "UNIQUE(package_id, conflict_id)"
+
	");"
+
	"CREATE TABLE provides("
+
	"    id INTEGER PRIMARY KEY,"
+
	"    provide TEXT NOT NULL"
+
	");"
+
	"CREATE TABLE pkg_provides ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "provide_id INTEGER NOT NULL REFERENCES provides(id)"
+
	    "  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
	    "UNIQUE(package_id, provide_id)"
+
	");"
	"PRAGMA user_version=%d;"
	;

@@ -279,7 +298,27 @@ static const struct repo_changes repo_upgrades[] = {
			"ON (oo.option = o.option);"
	 "DROP TABLE %Q.options;",
	},
-

+
	{2006,
+
	 2007,
+
	 "Add conflicts and provides",
+
	"CREATE TABLE %Q.pkg_conflicts ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "conflict_id INTEGER NOT NULL,"
+
	    "UNIQUE(package_id, conflict_id)"
+
	");"
+
	"CREATE TABLE %Q.provides("
+
	"    id INTEGER PRIMARY KEY,"
+
	"    provide TEXT NOT NULL"
+
	");"
+
	"CREATE TABLE %Q.pkg_provides ("
+
	    "package_id INTEGER NOT NULL REFERENCES packages(id)"
+
	    "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
	    "provide_id INTEGER NOT NULL REFERENCES provides(id)"
+
	    "  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
	    "UNIQUE(package_id, provide_id)"
+
	");"
+
	},
	/* Mark the end of the array */
	{ -1, -1, NULL, NULL, }

@@ -288,6 +327,13 @@ static const struct repo_changes repo_upgrades[] = {
/* How to downgrade a newer repo to match what the current system
   expects */
static const struct repo_changes repo_downgrades[] = {
+
	{2007,
+
	 2006,
+
	 "Revert conflicts and provides creation",
+
	 "DROP TABLE %Q.pkg_provides;"
+
	 "DROP TABLE %Q.provides;"
+
	 "DROP TABLE %Q.conflicts;"
+
	},
	{2006,
	 2005,
	 "Revert addition of extra options related data",
@@ -327,8 +373,8 @@ static const struct repo_changes repo_downgrades[] = {
	        " SELECT annotation_id, annotation FROM %Q.annotation;"
	 "INSERT INTO %Q.pkg_abstract (package_id,key_id,value_id)"
	        " SELECT package_id,tag_id,value_id FROM %Q.pkg_annotation;"
-
	 "DROP TABLE pkg_annotation;"
-
	 "DROP TABLE annotation;"
+
	 "DROP TABLE %Q.pkg_annotation;"
+
	 "DROP TABLE %Q.annotation;"
	},
	{2004,
	 2003,
modified libpkg/private/thd_repo.h
@@ -32,7 +32,7 @@

struct pkg_result {
	struct pkg *pkg;
-
	char path[MAXPATHLEN + 1];
+
	char path[MAXPATHLEN];
	char cksum[SHA256_DIGEST_LENGTH * 2 + 1];
	off_t size;
	int retcode; /* to pass errors */
modified libpkg/private/utils.h
@@ -82,6 +82,7 @@ int sbuf_set(struct sbuf **, const char *);
char * sbuf_get(struct sbuf *);
void sbuf_reset(struct sbuf *);
void sbuf_free(struct sbuf *);
+
ssize_t sbuf_size(struct sbuf *);

int mkdirs(const char *path);
int file_to_buffer(const char *, char **, off_t *);
@@ -114,5 +115,6 @@ ucl_object_t *yaml_to_ucl(const char *file, const char *buffer, size_t len);
void set_blocking(int fd);
void set_nonblocking(int fd);

+
pid_t process_spawn_pipe(FILE *inout[2], const char *command);

#endif
modified libpkg/rcscripts.c
@@ -45,7 +45,7 @@ int
pkg_start_stop_rc_scripts(struct pkg *pkg, pkg_rc_attr attr)
{
	struct pkg_file *file = NULL;
-
	char rc_d_path[PATH_MAX + 1];
+
	char rc_d_path[PATH_MAX];
	const char *rcfile;
	const char *rc;
	size_t len = 0;
@@ -54,7 +54,7 @@ pkg_start_stop_rc_scripts(struct pkg *pkg, pkg_rc_attr attr)

	pkg_get(pkg, PKG_PREFIX, &prefix);

-
	snprintf(rc_d_path, PATH_MAX, "%s/etc/rc.d/", prefix);
+
	snprintf(rc_d_path, sizeof(rc_d_path), "%s/etc/rc.d/", prefix);
	len = strlen(rc_d_path);

	while (pkg_files(pkg, &file) == EPKG_OK) {
modified libpkg/update.c
@@ -79,13 +79,13 @@ repo_fetch_remote_tmp(struct pkg_repo *repo, const char *filename, const char *e
	mode_t mask;
	const char *tmpdir;

-
	snprintf(url, MAXPATHLEN, "%s/%s.%s", pkg_repo_url(repo), filename, extension);
+
	snprintf(url, sizeof(url), "%s/%s.%s", pkg_repo_url(repo), filename, extension);

	tmpdir = getenv("TMPDIR");
	if (tmpdir == NULL)
		tmpdir = "/tmp";
	mkdirs(tmpdir);
-
	snprintf(tmp, MAXPATHLEN, "%s/%s.%s.XXXXXX", tmpdir, filename, extension);
+
	snprintf(tmp, sizeof(tmp), "%s/%s.%s.XXXXXX", tmpdir, filename, extension);

	mask = umask(022);
	fd = mkstemp(tmp);
@@ -174,7 +174,7 @@ load_fingerprint(const char *dir, const char *filename)
	char path[MAXPATHLEN];
	struct fingerprint *f = NULL;

-
	snprintf(path, MAXPATHLEN, "%s/%s", dir, filename);
+
	snprintf(path, sizeof(path), "%s/%s", dir, filename);

	p = ucl_parser_new(0);

@@ -281,13 +281,13 @@ repo_archive_extract_file(int fd, const char *file, const char *dest, struct pkg

		if (pkg_repo_signature_type(repo) == SIG_FINGERPRINT) {
			if (has_ext(archive_entry_pathname(ae), ".sig")) {
-
				snprintf(key, MAXPATHLEN, "%.*s",
+
				snprintf(key, sizeof(key), "%.*s",
				    (int) strlen(archive_entry_pathname(ae)) - 4,
				    archive_entry_pathname(ae));
				HASH_FIND_STR(sc, key, s);
				if (s == NULL) {
					s = calloc(1, sizeof(struct sig_cert));
-
					strlcpy(s->name, key, MAXPATHLEN);
+
					strlcpy(s->name, key, sizeof(s->name));
					HASH_ADD_STR(sc, name, s);
				}
				s->siglen = archive_entry_size(ae);
@@ -295,13 +295,13 @@ repo_archive_extract_file(int fd, const char *file, const char *dest, struct pkg
				archive_read_data(a, s->sig, s->siglen);
			}
			if (has_ext(archive_entry_pathname(ae), ".pub")) {
-
				snprintf(key, MAXPATHLEN, "%.*s",
+
				snprintf(key, sizeof(key), "%.*s",
				    (int) strlen(archive_entry_pathname(ae)) - 4,
				    archive_entry_pathname(ae));
				HASH_FIND_STR(sc, key, s);
				if (s == NULL) {
					s = calloc(1, sizeof(struct sig_cert));
-
					strlcpy(s->name, key, MAXPATHLEN);
+
					strlcpy(s->name, key, sizeof(s->name));
					HASH_ADD_STR(sc, name, s);
				}
				s->certlen = archive_entry_size(ae);
@@ -336,7 +336,7 @@ repo_archive_extract_file(int fd, const char *file, const char *dest, struct pkg
		}

		/* load fingerprints */
-
		snprintf(path, MAXPATHLEN, "%s/trusted", pkg_repo_fingerprints(repo));
+
		snprintf(path, sizeof(path), "%s/trusted", pkg_repo_fingerprints(repo));
		if ((load_fingerprints(path, &trusted)) != EPKG_OK) {
			pkg_emit_error("Error loading trusted certificates");
			rc = EPKG_FATAL;
@@ -349,7 +349,7 @@ repo_archive_extract_file(int fd, const char *file, const char *dest, struct pkg
			goto cleanup;
		}

-
		snprintf(path, MAXPATHLEN, "%s/revoked", pkg_repo_fingerprints(repo));
+
		snprintf(path, sizeof(path), "%s/revoked", pkg_repo_fingerprints(repo));
		if ((load_fingerprints(path, &revoked)) != EPKG_OK) {
			pkg_emit_error("Error loading revoked certificates");
			rc = EPKG_FATAL;
@@ -406,8 +406,10 @@ cleanup:
	if (rc != EPKG_OK && dest != NULL)
		unlink(dest);

-
	if (a != NULL)
+
	if (a != NULL) {
+
		archive_read_close(a);
		archive_read_free(a);
+
	}

	return rc;
}
@@ -430,7 +432,7 @@ repo_fetch_remote_extract_tmp(struct pkg_repo *repo, const char *filename,
	tmpdir = getenv("TMPDIR");
	if (tmpdir == NULL)
		tmpdir = "/tmp";
-
	snprintf(tmp, MAXPATHLEN, "%s/%s.XXXXXX", tmpdir, archive_file);
+
	snprintf(tmp, sizeof(tmp), "%s/%s.XXXXXX", tmpdir, archive_file);

	mask = umask(022);
	dest_fd = mkstemp(tmp);
@@ -574,10 +576,47 @@ pkg_update_increment_item_new(struct pkg_increment_task_item **head, const char
	HASH_ADD_KEYPTR(hh, *head, item->origin, strlen(item->origin), item);
}

+
static void __unused
+
pkg_parse_conflicts_file(FILE *f, sqlite3 *sqlite)
+
{
+
	size_t linecap = 0;
+
	ssize_t linelen;
+
	char *linebuf = NULL, *p, **deps;
+
	const char *origin, *pdep;
+
	int ndep, i;
+
	const char conflicts_clean_sql[] = ""
+
			"DELETE FROM pkg_conflicts;";
+

+
	pkg_debug(4, "pkg_parse_conflicts_file: running '%s'", conflicts_clean_sql);
+
	(void)sql_exec(sqlite, conflicts_clean_sql);
+

+
	while ((linelen = getline(&linebuf, &linecap, f)) > 0) {
+
		p = linebuf;
+
		origin = strsep(&p, ":");
+
		/* Check dependencies number */
+
		pdep = p;
+
		ndep = 1;
+
		while (*pdep != '\0') {
+
			if (*pdep == ',')
+
				ndep ++;
+
			pdep ++;
+
		}
+
		deps = malloc(sizeof(char *) * ndep);
+
		for (i = 0; i < ndep; i ++) {
+
			deps[i] = strsep(&p, ",\n");
+
		}
+
		pkgdb_repo_register_conflicts(origin, deps, ndep, sqlite);
+
		free(deps);
+
	}
+

+
	if (linebuf != NULL)
+
		free(linebuf);
+
}
+

static int
pkg_update_incremental(const char *name, struct pkg_repo *repo, time_t *mtime)
{
-
	FILE *fmanifest = NULL, *fdigests = NULL;
+
	FILE *fmanifest = NULL, *fdigests = NULL, *fconflicts = NULL;
	sqlite3 *sqlite = NULL;
	struct pkg *pkg = NULL;
	int rc = EPKG_FATAL;
@@ -634,6 +673,9 @@ pkg_update_incremental(const char *name, struct pkg_repo *repo, time_t *mtime)
		goto cleanup;
	packagesite_t = digest_t;
	*mtime = packagesite_t > digest_t ? packagesite_t : digest_t;
+
	fconflicts = repo_fetch_remote_extract_tmp(repo,
+
			repo_conflicts_archive, "txz", &local_t,
+
			&rc, repo_conflicts_file);
	fseek(fmanifest, 0, SEEK_END);
	len = ftell(fmanifest);

@@ -755,8 +797,12 @@ cleanup:
		fclose(fmanifest);
	if (fdigests)
		fclose(fdigests);
+
	if (fconflicts)
+
		fclose(fconflicts);
	if (map != MAP_FAILED)
		munmap(map, len);
+
	if (linebuf != NULL)
+
		free(linebuf);

	pkgdb_repo_close(sqlite, rc == EPKG_OK);

modified libpkg/utils.c
@@ -1,6 +1,7 @@
/*-
 * Copyright (c) 2011-2013 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
+
 * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -41,6 +42,7 @@
#include <utlist.h>
#include <ctype.h>
#include <fnmatch.h>
+
#include <paths.h>

#include "pkg.h"
#include "private/event.h"
@@ -97,10 +99,19 @@ sbuf_free(struct sbuf *buf)
		sbuf_delete(buf);
}

+
ssize_t
+
sbuf_size(struct sbuf *buf)
+
{
+
	if (buf != NULL)
+
		return sbuf_len(buf);
+

+
	return 0;
+
}
+

int
mkdirs(const char *_path)
{
-
	char path[MAXPATHLEN + 1];
+
	char path[MAXPATHLEN];
	char *p;

	strlcpy(path, _path, sizeof(path));
@@ -183,7 +194,7 @@ format_exec_cmd(char **dest, const char *in, const char *prefix,
    const char *plist_file, char *line)
{
	struct sbuf *buf = sbuf_new_auto();
-
	char path[MAXPATHLEN + 1];
+
	char path[MAXPATHLEN];
	char *cp;

	while (in[0] != '\0') {
@@ -624,3 +635,67 @@ set_blocking(int fd)
		fcntl(fd, F_SETFL, flags);
	}
}
+

+
/* Spawn a process from pfunc, returning it's pid. The fds array passed will
+
 * be filled with two descriptors: fds[0] will read from the child process,
+
 * and fds[1] will write to it.
+
 * Similarly, the child process will receive a reading/writing fd set (in
+
 * that same order) as arguments.
+
*/
+
extern char **environ;
+
pid_t
+
process_spawn_pipe(FILE *inout[2], const char *command)
+
{
+
	pid_t pid;
+
	int pipes[4];
+
	char *argv[4];
+

+
	/* Parent read/child write pipe */
+
	if (pipe(&pipes[0]) == -1)
+
		return (-1);
+

+
	/* Child read/parent write pipe */
+
	if (pipe(&pipes[2]) == -1) {
+
		close(pipes[0]);
+
		close(pipes[1]);
+
		return (-1);
+
	}
+

+
	argv[0] = __DECONST(char *, "sh");
+
	argv[1] = __DECONST(char *, "-c");
+
	argv[2] = __DECONST(char *, command);
+
	argv[3] = NULL;
+

+
	pid = fork();
+
	if (pid > 0) {
+
		/* Parent process */
+
		inout[0] = fdopen(pipes[0], "r");
+
		inout[1] = fdopen(pipes[3], "w");
+

+
		close(pipes[1]);
+
		close(pipes[2]);
+

+
		return (pid);
+

+
	} else if (pid == 0) {
+
		close(pipes[0]);
+
		close(pipes[3]);
+

+
		if (pipes[1] != STDOUT_FILENO) {
+
			dup2(pipes[1], STDOUT_FILENO);
+
			close(pipes[1]);
+
		}
+
		if (pipes[2] != STDIN_FILENO) {
+
			dup2(pipes[2], STDIN_FILENO);
+
			close(pipes[2]);
+
		}
+
		closefrom(STDERR_FILENO + 1);
+

+
		execve(_PATH_BSHELL, argv, environ);
+

+
		exit(127);
+
	}
+

+
	return (-1); /* ? */
+
}
+

modified pkg/Makefile
@@ -63,10 +63,9 @@ DEBUG_FLAGS+= -pg
CFLAGS+=	-I../libpkg \
		-I${.CURDIR}/../external/uthash \
		-I${.CURDIR}/../external/expat/lib
-
LDADD+=		-L../libpkg \
-
		-L../external/expat \
-
		-ledit \
+
LDADD+=		-L${.OBJDIR}/../libpkg \
		-lpkg \
+
		-ledit \
		-larchive \
		-lutil \
		-lpthread \
@@ -77,31 +76,22 @@ LDADD+= -L../libpkg \
		-lmd \
		-lz \
		-lbz2 \
-
		-llzma \
-
		-lexpat
-

-
LDADD_STATIC=	-L../external/sqlite \
-
		-L../external/libyaml \
-
		-L../external/libucl
+
		-llzma

-
.if !exists(/usr/lib/libelf.so)
-
LDADD_STATIC+=		-L../external/libelf
+
.if exists(/usr/lib/libelf.so)
+
LDADD_STATIC+=	-lelf
.endif

-
LDADD_STATIC+=	-lsqlite3 \
-
		-larchive \
+
LDADD_STATIC+=	-larchive \
		-lsbuf \
		-lfetch \
		-lpthread \
-
		-lelf \
		-lssl \
		-lcrypto \
		-lmd \
		-lz \
		-lbz2 \
-
		-llzma \
-
		-lyaml \
-
		-lucl
+
		-llzma

.if !exists(/usr/include/jail.h)
CFLAGS+=	-DNO_LIBJAIL
@@ -109,11 +99,6 @@ CFLAGS+= -DNO_LIBJAIL
LDADD+=		-ljail
.endif

-
.if exists(/usr/include/edit/readline/readline.h)
-
LDADD_STATIC+=	-ledit \
-
		-lncursesw
-
.endif
-

DPADD_STATIC=	${.OBJDIR}/../libpkg/libpkg.a

WARNS?=		6
modified pkg/add.c
@@ -64,7 +64,7 @@ exec_add(int argc, char **argv)
{
	struct pkgdb *db = NULL;
	struct sbuf *failedpkgs = NULL;
-
	char path[MAXPATHLEN + 1];
+
	char path[MAXPATHLEN];
	char *file;
	int retcode;
	int ch;
modified pkg/audit.c
@@ -128,12 +128,12 @@ static size_t audit_entry_first_byte_idx[256];
void
usage_audit(void)
{
-
	fprintf(stderr, "Usage: pkg audit [-Fqc] <pattern>\n\n");
+
	fprintf(stderr, "Usage: pkg audit [-Fq] <pattern>\n\n");
	fprintf(stderr, "For more information see 'pkg help audit'.\n");
}

static int
-
fetch_and_extract(const char *src, const char *dest, bool xml)
+
fetch_and_extract(const char *src, const char *dest)
{
	struct archive *a = NULL;
	struct archive_entry *ae = NULL;
@@ -149,11 +149,7 @@ fetch_and_extract(const char *src, const char *dest, bool xml)
	if (tmpdir == NULL)
		tmpdir = "/tmp";
	strlcpy(tmp, tmpdir, sizeof(tmp));
-
	if (xml)
-
		strlcat(tmp, "/vuln.xml.bz2.XXXX", sizeof(tmp));
-
	else
-
		strlcat(tmp, "/auditfile.tbz.XXXX", sizeof(tmp));
-

+
	strlcat(tmp, "/vuln.xml.bz2.XXXX", sizeof(tmp));
	if (stat(dest, &st) != -1) {
		t = st.st_mtime;
	}
@@ -161,11 +157,11 @@ fetch_and_extract(const char *src, const char *dest, bool xml)
	case EPKG_OK:
		break;
	case EPKG_UPTODATE:
-
		printf("%s file up-to-date.\n", xml ? "Vulnxml" : "Audit");
+
		printf("Vulnxml file up-to-date.\n");
		retcode = EPKG_OK;
		goto cleanup;
	default:
-
		warnx("Cannot fetch %s file!", xml ? "vulnxml" : "audit");
+
		warnx("Cannot fetch vulnxml file!");
		goto cleanup;
	}

@@ -176,10 +172,7 @@ fetch_and_extract(const char *src, const char *dest, bool xml)
	archive_read_support_filter_all(a);
#endif

-
	if (xml)
-
		archive_read_support_format_raw(a);
-
	else
-
		archive_read_support_format_tar(a);
+
	archive_read_support_format_raw(a);

	if (archive_read_open_filename(a, tmp, 4096) != ARCHIVE_OK) {
		warnx("archive_read_open_filename(%s): %s",
@@ -206,122 +199,16 @@ fetch_and_extract(const char *src, const char *dest, bool xml)

	cleanup:
	unlink(tmp);
-
	if (a != NULL)
-
#if ARCHIVE_VERSION_NUMBER < 3000002
-
		archive_read_finish(a);
-
#else
+
	if (a != NULL) {
+
		archive_read_close(a);
		archive_read_free(a);
-
#endif
+
	}
	if (fd >= 0)
		close(fd);

	return (retcode);
}

-
/* Fuuuu */
-
static void
-
parse_pattern(struct audit_entry *e, char *pattern, size_t len)
-
{
-
	size_t i;
-
	char *start = pattern;
-
	char *end;
-
	char **dest = &e->pkgname;
-
	char **next_dest = NULL;
-
	struct version_entry *v = &e->versions->v1;
-
	int skipnext;
-
	int type;
-
	for (i = 0; i < len; i++) {
-
		type = 0;
-
		skipnext = 0;
-
		if (pattern[i] == '=') {
-
			type = EQ;
-
		}
-
		if (pattern[i] == '<') {
-
			if (pattern[i+1] == '=') {
-
				skipnext = 1;
-
				type = LTE;
-
			} else {
-
				type = LT;
-
			}
-
		}
-
		if (pattern[i] == '>') {
-
			if (pattern[i+1] == '=') {
-
				skipnext = 1;
-
				type = GTE;
-
			} else {
-
				type = GT;
-
			}
-
		}
-

-
		if (type != 0) {
-
			v->type = type;
-
			next_dest = &v->version;
-
			v = &e->versions->v2;
-
		}
-

-
		if (next_dest != NULL || i == len - 1) {
-
			end = pattern + i;
-
			*dest = strndup(start, end - start);
-

-
			i += skipnext;
-
			start = pattern + i + 1;
-
			dest = next_dest;
-
			next_dest = NULL;
-
		}
-
	}
-
}
-

-
static int
-
parse_db_portaudit(const char *path, struct audit_entry **h)
-
{
-
	struct audit_entry *e;
-
	struct audit_versions *vers;
-
	FILE *fp;
-
	size_t linecap = 0;
-
	ssize_t linelen;
-
	char *line = NULL;
-
	char *column;
-
	uint8_t column_id;
-

-
	if ((fp = fopen(path, "r")) == NULL)
-
		return (EPKG_FATAL);
-

-
	while ((linelen = getline(&line, &linecap, fp)) > 0) {
-
		column_id = 0;
-

-
		if (line[0] == '#')
-
			continue;
-

-
		if ((e = calloc(1, sizeof(struct audit_entry))) == NULL)
-
			err(1, "calloc(audit_entry)");
-
		if ((vers = calloc(1, sizeof(struct audit_versions))) == NULL)
-
			err(1, "calloc(audit_versions)");
-

-
		LL_PREPEND(e->versions, vers);
-

-
		while ((column = strsep(&line, "|")) != NULL)
-
		{
-
			switch (column_id) {
-
			case 0:
-
				parse_pattern(e, column, linelen);
-
				break;
-
			case 1:
-
				e->url = strdup(column);
-
				break;
-
			case 2:
-
				e->desc = strdup(column);
-
				break;
-
			default:
-
				warn("extra column in audit file");
-
			}
-
			column_id++;
-
		}
-
		LL_PREPEND(*h, e);
-
	}
-

-
	return EPKG_OK;
-
}
-

enum vulnxml_parse_state {
	VULNXML_PARSE_INIT = 0,
	VULNXML_PARSE_VULN,
@@ -832,10 +719,9 @@ exec_audit(int argc, char **argv)
	const char *db_dir;
	char *name;
	char *version;
-
	char audit_file[MAXPATHLEN + 1];
+
	char audit_file[MAXPATHLEN];
	unsigned int vuln = 0;
	bool fetch = false;
-
	bool xml = true;
	int ch;
	int ret = EX_OK, res;
	const char *portaudit_site = NULL;
@@ -845,14 +731,11 @@ exec_audit(int argc, char **argv)
		return (EX_CONFIG);
	}

-
	while ((ch = getopt(argc, argv, "qcF")) != -1) {
+
	while ((ch = getopt(argc, argv, "qF")) != -1) {
		switch (ch) {
		case 'q':
			quiet = true;
			break;
-
		case 'c':
-
			xml = false;
-
			break;
		case 'F':
			fetch = true;
			break;
@@ -864,25 +747,14 @@ exec_audit(int argc, char **argv)
	argc -= optind;
	argv += optind;

-
	if (xml)
-
		snprintf(audit_file, sizeof(audit_file), "%s/vuln.xml", db_dir);
-
	else
-
		snprintf(audit_file, sizeof(audit_file), "%s/auditfile", db_dir);
+
	snprintf(audit_file, sizeof(audit_file), "%s/vuln.xml", db_dir);

	if (fetch == true) {
-
		if (xml) {
-
			if (pkg_config_string(PKG_CONFIG_VULNXML_SITE, &portaudit_site) != EPKG_OK) {
-
				warnx("VULNXML_SITE is missing");
-
				return (EX_CONFIG);
-
			}
+
		if (pkg_config_string(PKG_CONFIG_VULNXML_SITE, &portaudit_site) != EPKG_OK) {
+
			warnx("VULNXML_SITE is missing");
+
			return (EX_CONFIG);
		}
-
		else {
-
			if (pkg_config_string(PKG_CONFIG_PORTAUDIT_SITE, &portaudit_site) != EPKG_OK) {
-
				warnx("PORTAUDIT_SITE is missing");
-
				return (EX_CONFIG);
-
			}
-
		}
-
		if (fetch_and_extract(portaudit_site, audit_file, xml) != EPKG_OK) {
+
		if (fetch_and_extract(portaudit_site, audit_file) != EPKG_OK) {
			return (EX_IOERR);
		}
	}
@@ -904,17 +776,12 @@ exec_audit(int argc, char **argv)
		pkg_set(pkg,
		    PKG_NAME, name,
		    PKG_VERSION, version);
-
		if (xml)
-
			res = parse_db_vulnxml(audit_file, &h);
-
		else
-
			res = parse_db_portaudit(audit_file, &h);
+
		res = parse_db_vulnxml(audit_file, &h);
		if (res != EPKG_OK) {
			if (errno == ENOENT)
-
				warnx("unable to open %s file, try running 'pkg audit -F' first",
-
						xml ? "vulnxml" : "audit");
+
				warnx("unable to open vulnxml file, try running 'pkg audit -F' first");
			else
-
				warn("unable to open %s file %s",
-
						xml ? "vulnxml" : "audit", audit_file);
+
				warn("unable to open vulnxml file %s", audit_file);
			ret = EX_DATAERR;
			goto cleanup;
		}
@@ -949,17 +816,12 @@ exec_audit(int argc, char **argv)
		goto cleanup;
	}

-
	if (xml)
-
		res = parse_db_vulnxml(audit_file, &h);
-
	else
-
		res = parse_db_portaudit(audit_file, &h);
+
	res = parse_db_vulnxml(audit_file, &h);
	if (res != EPKG_OK) {
		if (errno == ENOENT)
-
			warnx("unable to open %s file, try running 'pkg audit -F' first",
-
					xml ? "vulnxml" : "audit");
+
			warnx("unable to open vulnxml file, try running 'pkg audit -F' first");
		else
-
			warn("unable to open %s file %s",
-
					xml ? "vulnxml" : "audit", audit_file);
+
			warn("unable to open vulnxml file %s", audit_file);
		ret = EX_DATAERR;
		goto cleanup;
	}
modified pkg/convert.c
@@ -90,29 +90,29 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
		pkg_to_old(pkg);
		pkg_old_emit_content(pkg, &content);

-
		snprintf(path, MAXPATHLEN, "%s/%s-%s", pkg_add_dbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s", pkg_add_dbdir, name, version);
		mkdir(path, 0755);

-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+CONTENTS", pkg_add_dbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s/+CONTENTS", pkg_add_dbdir, name, version);
		fp = fopen(path, "w");
		fputs(content, fp);
		fclose(fp);

		pkg_get(pkg, PKG_DESC, &buf);
-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+DESC", pkg_add_dbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s/+DESC", pkg_add_dbdir, name, version);
		fp = fopen(path, "w");
		fputs(buf, fp);
		fclose(fp);

		pkg_get(pkg, PKG_COMMENT, &buf);
-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+COMMENT", pkg_add_dbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s/+COMMENT", pkg_add_dbdir, name, version);
		fp = fopen(path, "w");
		fprintf(fp, "%s\n", buf);
		fclose(fp);

		pkg_get(pkg, PKG_MESSAGE, &buf);
		if (buf != NULL && buf[0] != '\0') {
-
			snprintf(path, MAXPATHLEN, "%s/%s-%s/+DISPLAY", pkg_add_dbdir, name, version);
+
			snprintf(path, sizeof(path), "%s/%s-%s/+DISPLAY", pkg_add_dbdir, name, version);
			fp = fopen(path, "w");
			fputs(buf, fp);
			fclose(fp);
@@ -120,7 +120,7 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)

		pkg_get(pkg, PKG_MTREE, &buf);
		if (buf != NULL && buf[0] != '\0') {
-
			snprintf(path, MAXPATHLEN, "%s/%s-%s/+MTREE_DIRS", pkg_add_dbdir, name, version);
+
			snprintf(path, sizeof(path), "%s/%s-%s/+MTREE_DIRS", pkg_add_dbdir, name, version);
			fp = fopen(path, "w");
			fputs(buf, fp);
			fclose(fp);
@@ -157,7 +157,7 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
		}
		if (sbuf_len(install_script) > 0) {
			sbuf_finish(install_script);
-
			snprintf(path, MAXPATHLEN, "%s/%s-%s/+INSTALL", pkg_add_dbdir, name, version);
+
			snprintf(path, sizeof(path), "%s/%s-%s/+INSTALL", pkg_add_dbdir, name, version);
			fp = fopen(path, "w");
			fputs(sbuf_data(install_script), fp);
			fclose(fp);
@@ -194,13 +194,13 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
		}
		if (sbuf_len(deinstall_script) > 0) {
			sbuf_finish(deinstall_script);
-
			snprintf(path, MAXPATHLEN, "%s/%s-%s/+DEINSTALL", pkg_add_dbdir, name, version);
+
			snprintf(path, sizeof(path), "%s/%s-%s/+DEINSTALL", pkg_add_dbdir, name, version);
			fp = fopen(path, "w");
			fputs(sbuf_data(deinstall_script), fp);
			fclose(fp);
		}

-
		snprintf(path, MAXPATHLEN, "%s/%s-%s/+REQUIRED_BY", pkg_add_dbdir, name, version);
+
		snprintf(path, sizeof(path), "%s/%s-%s/+REQUIRED_BY", pkg_add_dbdir, name, version);
		while (pkg_rdeps(pkg, &dep) == EPKG_OK) {
			if (rq == NULL)
				rq = fopen(path, "w");
@@ -249,7 +249,7 @@ convert_from_old(const char *pkg_add_dbdir, bool dry_run)
			} else
				pkg_reset(p, PKG_OLD_FILE);
			printf("Converting %s...\n", dp->d_name);
-
			snprintf(path, MAXPATHLEN, "%s/%s", pkg_add_dbdir, dp->d_name);
+
			snprintf(path, sizeof(path), "%s/%s", pkg_add_dbdir, dp->d_name);
			if (pkg_old_load_from_path(p, path) != EPKG_OK) {
				fprintf(stderr, "Skipping invalid package: %s\n", path);
				continue;
modified pkg/create.c
@@ -135,7 +135,7 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
		STAILQ_REMOVE_HEAD(&head, next);

		if (!overwrite) {
-
			pkg_snprintf(pkgpath, MAXPATHLEN, "%S/%n-%v.%S",
+
			pkg_snprintf(pkgpath, sizeof(pkgpath), "%S/%n-%v.%S",
			    outdir, e->pkg, e->pkg, format);
			if (access(pkgpath, F_OK) == 0) {
				pkg_printf("%n-%v already packaged, skipping...\n",
modified pkg/event.c
@@ -38,7 +38,7 @@
#include "pkgcli.h"

static off_t fetched = 0;
-
static char url[MAXPATHLEN+1];
+
static char url[MAXPATHLEN];
struct sbuf *messages = NULL;

static void
modified pkg/main.c
@@ -50,7 +50,15 @@

#include "pkgcli.h"

-
static void usage(const char *, const char *);
+
/* Used to define why do we show usage message to a user */
+
enum pkg_usage_reason {
+
	PKG_USAGE_ERROR,
+
	PKG_USAGE_UNKNOWN_COMMAND,
+
	PKG_USAGE_INVALID_ARGUMENTS,
+
	PKG_USAGE_HELP
+
};
+

+
static void usage(const char *, const char *, FILE *, enum pkg_usage_reason, ...);
static void usage_help(void);
static int exec_help(int, char **);
bool quiet = false;
@@ -124,58 +132,78 @@ show_command_names(void)
}

static void
-
usage(const char *conffile, const char *reposdir)
+
usage(const char *conffile, const char *reposdir, FILE *out, enum pkg_usage_reason reason, ...)
{
	struct plugcmd *c;
	bool plugins_enabled = false;
	unsigned int i;
+
	const char *arg;
+
	va_list vp;
+

+
	if (reason == PKG_USAGE_UNKNOWN_COMMAND) {
+
		va_start(vp, reason);
+
		arg = va_arg(vp, const char *);
+
		va_end(vp);
+
		fprintf(out, "pkg: unknown command: %s\n", arg);
+
		goto out;
+
	}
+
	else if (reason == PKG_USAGE_INVALID_ARGUMENTS) {
+
		va_start(vp, reason);
+
		arg = va_arg(vp, const char *);
+
		va_end(vp);
+
		fprintf(out, "pkg: %s\n", arg);
+
	}

#ifndef NO_LIBJAIL
-
 	fprintf(stderr, "Usage: pkg [-v] [-d] [-l] [-N] [-j <jail name or id>|-c <chroot path>] [-C <configuration file>] [-R <repo config dir>] <command> [<args>]\n\n");
+
 	fprintf(out, "Usage: pkg [-v] [-d] [-l] [-N] [-j <jail name or id>|-c <chroot path>] [-C <configuration file>] [-R <repo config dir>] <command> [<args>]\n\n");
#else
-
	fprintf(stderr, "Usage: pkg [-v] [-d] [-l] [-N] [-c <chroot path>] [-C <configuration file>] [-R <repo config dir>] <command> [<args>]\n\n");
+
	fprintf(out, "Usage: pkg [-v] [-d] [-l] [-N] [-c <chroot path>] [-C <configuration file>] [-R <repo config dir>] <command> [<args>]\n\n");
#endif
-
	fprintf(stderr, "Global options supported:\n");
-
	fprintf(stderr, "\t%-15s%s\n", "-d", "Increment debug level");
+
	if (reason == PKG_USAGE_HELP) {
+
		fprintf(out, "Global options supported:\n");
+
		fprintf(out, "\t%-15s%s\n", "-d", "Increment debug level");
#ifndef NO_LIBJAIL
-
	fprintf(stderr, "\t%-15s%s\n", "-j", "Execute pkg(8) inside a jail(8)");
+
		fprintf(out, "\t%-15s%s\n", "-j", "Execute pkg(8) inside a jail(8)");
#endif
-
	fprintf(stderr, "\t%-15s%s\n", "-c", "Execute pkg(8) inside a chroot(8)");
-
	fprintf(stderr, "\t%-15s%s\n", "-C", "Use the specified configuration file");
-
	fprintf(stderr, "\t%-15s%s\n", "-R", "Directory to search for individual repository configurations");
-
	fprintf(stderr, "\t%-15s%s\n", "-l", "List available commands and exit");
-
	fprintf(stderr, "\t%-15s%s\n", "-v", "Display pkg(8) version");
-
	fprintf(stderr, "\t%-15s%s\n\n", "-N", "Test if pkg(8) is activated and avoid auto-activation");
-
	fprintf(stderr, "Commands supported:\n");
-

-
	for (i = 0; i < cmd_len; i++)
-
		fprintf(stderr, "\t%-15s%s\n", cmd[i].name, cmd[i].desc);
-

-
	if (!pkg_initialized() && pkg_init(conffile, reposdir) != EPKG_OK)
-
		errx(EX_SOFTWARE, "Cannot parse configuration file!");
+
		fprintf(out, "\t%-15s%s\n", "-c", "Execute pkg(8) inside a chroot(8)");
+
		fprintf(out, "\t%-15s%s\n", "-C", "Use the specified configuration file");
+
		fprintf(out, "\t%-15s%s\n", "-R", "Directory to search for individual repository configurations");
+
		fprintf(out, "\t%-15s%s\n", "-l", "List available commands and exit");
+
		fprintf(out, "\t%-15s%s\n", "-v", "Display pkg(8) version");
+
		fprintf(out, "\t%-15s%s\n\n", "-N", "Test if pkg(8) is activated and avoid auto-activation");
+
		fprintf(out, "Commands supported:\n");

-
	pkg_config_bool(PKG_CONFIG_ENABLE_PLUGINS, &plugins_enabled);
+
		for (i = 0; i < cmd_len; i++)
+
			fprintf(out, "\t%-15s%s\n", cmd[i].name, cmd[i].desc);

-
	if (plugins_enabled) {
-
		if (pkg_plugins_init() != EPKG_OK)
-
			errx(EX_SOFTWARE, "Plugins cannot be loaded");
+
		if (!pkg_initialized() && pkg_init(conffile, reposdir) != EPKG_OK)
+
			errx(EX_SOFTWARE, "Cannot parse configuration file!");

-
		printf("\nCommands provided by plugins:\n");
+
		pkg_config_bool(PKG_CONFIG_ENABLE_PLUGINS, &plugins_enabled);

-
		STAILQ_FOREACH(c, &plugins, next)
-
			fprintf(stderr, "\t%-15s%s\n", c->name, c->desc);
-
	}
+
		if (plugins_enabled) {
+
			if (pkg_plugins_init() != EPKG_OK)
+
				errx(EX_SOFTWARE, "Plugins cannot be loaded");

-
	fprintf(stderr, "\nFor more information on the different commands"
-
			" see 'pkg help <command>'.\n");
+
			fprintf(out, "\nCommands provided by plugins:\n");

+
			STAILQ_FOREACH(c, &plugins, next)
+
			fprintf(out, "\t%-15s%s\n", c->name, c->desc);
+
		}
+
		fprintf(out, "\nFor more information on the different commands"
+
					" see 'pkg help <command>'.\n");
+
		exit(EXIT_SUCCESS);
+
	}
+

+
out:
+
	fprintf(out, "\nFor more information on available commands and options see 'pkg help'.\n");
	exit(EX_USAGE);
}

static void
usage_help(void)
{
-
	usage(NULL, NULL);
+
	usage(NULL, NULL, stdout, PKG_USAGE_HELP);
}

static int
@@ -554,7 +582,7 @@ main(int argc, char **argv)
	cmdargv = argv;

	if (argc < 2)
-
		usage(NULL, NULL);
+
		usage(NULL, NULL, stderr, PKG_USAGE_INVALID_ARGUMENTS, "not enough arguments");

#ifndef NO_LIBJAIL
	while ((ch = getopt(argc, argv, "dj:c:C:R:lNvq")) != -1) {
@@ -604,7 +632,7 @@ main(int argc, char **argv)
	}

	if (argc == 0 && version == 0 && !activation_test)
-
		usage(conffile, reposdir);
+
		usage(conffile, reposdir, stderr, PKG_USAGE_INVALID_ARGUMENTS, "no commands specified");

	umask(022);
	pkg_event_register(&event_callback, &debug);
@@ -614,8 +642,8 @@ main(int argc, char **argv)
	optind = 1;

	if (jail_str != NULL && chroot_path != NULL) {
-
		fprintf(stderr, "-j and -c cannot be used at the same time!\n");
-
		usage(conffile, reposdir);
+
		usage(conffile, reposdir, stderr, PKG_USAGE_INVALID_ARGUMENTS,
+
				"-j and -c cannot be used at the same time!\n");
	}

	if (chroot_path != NULL)
@@ -744,7 +772,7 @@ main(int argc, char **argv)
		}

		if (!plugin_found)
-
			usage(conffile, reposdir);
+
			usage(conffile, reposdir, stderr, PKG_USAGE_UNKNOWN_COMMAND, newargv[0]);

		return (ret);
	}
@@ -753,14 +781,7 @@ main(int argc, char **argv)
		assert(command->exec != NULL);
		ret = command->exec(newargc, newargv);
	} else {
-
		warnx("'%s' is not a valid command.\n", newargv[0]);
-

-
		fprintf(stderr, "See 'pkg help' for more information on the commands.\n\n");
-
		fprintf(stderr, "Command '%s' could be one of the following:\n", newargv[0]);
-

-
		for (i = 0; i < cmd_len; i++)
-
			if (strncmp(newargv[0], cmd[i].name, len) == 0)
-
				fprintf(stderr, "\t%s\n",cmd[i].name);
+
		usage(conffile, reposdir, stderr, PKG_USAGE_UNKNOWN_COMMAND, newargv[0]);
	}

	if (alias != NULL)
modified pkg/pkg-audit.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd October  10, 2013
+
.Dd December  24, 2013
.Dt PKG-AUDIT 8
.Os
.Sh NAME
@@ -23,7 +23,7 @@
.Nd Audits installed packages against known vulnerabilities.
.Sh SYNOPSIS
.Nm
-
.Op Fl Fqc
+
.Op Fl Fq
.Ar pkg-name
.Sh DESCRIPTION
.Nm
@@ -39,7 +39,7 @@ Note that a current ports tree (or any local copy of the ports tree) is not
required for operation.
.Pp
The URL that is used to fetch the database can be overridden via the VULNXML_SITE
-
config variable or by PORTAUDIT_SITE if you still use the compact audit file.
+
config variable.
See
.Xr pkg.conf 5
for more information.
@@ -60,9 +60,6 @@ Fetch the database before checking.
Be ``quiet''.
Prints only the requested information without
displaying many hints.
-
.It Fl c
-
Use the prepared audit file instead of vunlxml.
-
Database is fetched according to PORTAUDIT_SITE config variable.
.El
.Sh ENVIRONMENT
The following environment variables affect the execution of
@@ -72,7 +69,6 @@ See
for further description.
.Bl -tag -width ".Ev NO_DESCRIPTIONS"
.It Ev PKG_DBDIR
-
.It Ev PORTAUDIT_SITE
.It Ev VULNXML_SITE
.El
.Sh FILES
modified pkg/pkg-info.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd November 29, 2013
+
.Dd December 18, 2013
.Dt PKG-INFO 8
.Os
.Sh NAME
@@ -114,8 +114,9 @@ origin.
Added only for Ports compatibility.
.Pp
.It Fl E
-
Currently not implemented.
-
Added only for Ports compatibility.
+
is a synonym for
+
.Fl q .
+
This option is deprecated and exists only for Ports compatibility.
.It Fl o
Display
.Ar pkg-name
modified pkg/pkg-query.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd September 28, 2013
+
.Dd December 5, 2013
.Dt PKG-QUERY 8
.Os
.Sh NAME
@@ -98,6 +98,8 @@ is in bytes, and
is in human readable format.
.It Cm \&%a
Returns 1 if the matched package is an orphan package and can be pkg-autoremove(1)'d, 0 otherwise
+
.It Cm \&%q
+
Architecture of the matched package
.It Cm \&%k
Returns 1 if the matched package is locked against modification or deletion, 0 otherwise
.It Cm \&%M
@@ -254,6 +256,8 @@ WWW address of the package (type string)
Flatsize of the package (type integer)
.It Cm \&%a
Automatic status of the package (type integer)
+
.It Cm \&%q
+
Architecture of the package (type string)
.It Cm \&%k
Locking status of the package (type integer)
.It Cm \&%M
modified pkg/pkg-rquery.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd November 7, 2013
+
.Dd December 5, 2013
.Dt PKG-RQUERY 8
.Os
.Sh NAME
@@ -106,6 +106,8 @@ Comment of the matched package
Description of the matched package
.It Cm \&%w
Home page of the matched package
+
.It Cm \&%q
+
Architecture of the matched package
.It Cm \&%l
license logic of the matched package - nothing for single, & for AND, and | for OR
.It Cm \&%s Ns Op bh
@@ -224,6 +226,8 @@ WWW address of the package (type string)
Flatsize of the package (type integer)
.It Cm \&%a
Automatic status of the package (type integer)
+
.It Cm \&%q
+
Architecture of the package (type string)
.It Cm \&%M
Message of the package (type string)
.It Cm \&%# Ns Op drCOLBbA
modified pkg/pkg.8
@@ -370,7 +370,7 @@ command first appeared in
.An Eitan Adler <eadler@FreeBSD.org>
.An Romain Tarti\`ere <romain@FreeBSD.org>
.An Vsevolod Stakhov <vsevolod@FreeBSD.org>
-
.An Alexandre Perrin <alexandre.perrin@netoxygen.ch>
+
.An Alexandre Perrin <alex@kaworu.ch>
.\" ---------------------------------------------------------------------------
.Sh BUGS
See the issue tracker at
modified pkg/pkg.conf.5
@@ -15,7 +15,7 @@
.\"     @(#)pkg.1
.\" $FreeBSD$
.\"
-
.Dd December 2, 2013
+
.Dd December 15, 2013
.Dt PKG.CONF 5
.Os
.Sh NAME
@@ -207,6 +207,24 @@ data.
This option exists to facilitate developing
.Cm +MTREE
free package sets.
+
.It Cm ALIAS: key/value list
+
Define local aliases for various
+
.Xr pkg 8
+
standard command lines.
+
Whenever the
+
.Em key
+
text occurs as a separate
+
.Sq action
+
word in a command line of the form
+
.Nm pkg Em key ... ,
+
substitute the
+
.Em value
+
text verbatim.
+
The replacement can consist of any sequence of text, which should form
+
a syntactically correct
+
.Xr pkg 8
+
command line when substituted in and followed by any remaining tokens from
+
the original command line.
.El
.Sh MULTIPLE REPOSITORIES
To use multiple repositories, specify the primary repository as shown above.
modified pkg/query.c
@@ -67,6 +67,7 @@ static struct query_flags accepted_query_flags[] = {
	{ 'e', "",		0, PKG_LOAD_BASIC },
	{ 'w', "",		0, PKG_LOAD_BASIC },
	{ 'l', "",		0, PKG_LOAD_BASIC },
+
	{ 'q', "",		0, PKG_LOAD_BASIC },
	{ 'a', "",		0, PKG_LOAD_BASIC },
	{ 'k', "",		0, PKG_LOAD_BASIC },
	{ 'M', "",		0, PKG_LOAD_BASIC },
@@ -212,6 +213,9 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
					break;
				}
				break;
+
			case 'q':
+
				pkg_sbuf_printf(dest, "%q", pkg);
+
				break;
			case 'l':
				pkg_sbuf_printf(dest, "%l", pkg);
				break;
@@ -475,6 +479,10 @@ format_sql_condition(const char *str, struct sbuf *sqlcond, bool for_remote)
					sbuf_cat(sqlcond, "automatic");
					state = OPERATOR_INT;
					break;
+
				case 'q':
+
					sbuf_cat(sqlcond, "arch");
+
					state = OPERATOR_STRING;
+
					break;
				case 'k':
					if (for_remote)
						goto bad_option;
modified pkg/register.c
@@ -88,7 +88,7 @@ exec_register(int argc, char **argv)
	char		*arch = NULL;
	char		 myarch[BUFSIZ];
	char		*www  = NULL;
-
	char		 fpath[MAXPATHLEN + 1];
+
	char		 fpath[MAXPATHLEN];

	const char	*plist      = NULL;
	const char	*mdir       = NULL;
modified pkg/rquery.c
@@ -64,6 +64,7 @@ static struct query_flags accepted_rquery_flags[] = {
	{ 'c', "",		0, PKG_LOAD_BASIC },
	{ 'w', "",		0, PKG_LOAD_BASIC },
	{ 'l', "",		0, PKG_LOAD_BASIC },
+
	{ 'q', "",		0, PKG_LOAD_BASIC },
	{ 'M', "",		0, PKG_LOAD_BASIC }
};

modified pkg/shlib.c
@@ -141,7 +141,7 @@ int
exec_shlib(int argc, char **argv)
{
	struct pkgdb	*db = NULL;
-
	char		 libname[MAXPATHLEN + 1];
+
	char		 libname[MAXPATHLEN];
	int		 retcode = EPKG_OK;
	int		 ch;
	bool		 provides_only = false;
modified pkg/utils.c
@@ -521,141 +521,155 @@ print_info(struct pkg * const pkg, uint64_t options)
	}
}

-
void
-
print_jobs_summary(struct pkg_jobs *jobs, const char *msg, ...)
+
static void
+
print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,
+
		int64_t *newsize, int64_t *dlsize)
{
-
	struct pkg *pkg = NULL;
+
	const char *oldversion, *cachedir, *why;
	char path[MAXPATHLEN];
	struct stat st;
-
	const char *oldversion, *cachedir, *why;
-
	int64_t dlsize, oldsize, newsize;
	int64_t flatsize, oldflatsize, pkgsize;
	char size[7];
-
	va_list ap;
-
	pkg_jobs_t type;
-

-
	type = pkg_jobs_type(jobs);

-
	va_start(ap, msg);
-
	vprintf(msg, ap);
-
	va_end(ap);
-

-
	dlsize = oldsize = newsize = 0;
	flatsize = oldflatsize = pkgsize = 0;
	oldversion = NULL;
-
	
-
	pkg_config_string(PKG_CONFIG_CACHEDIR, &cachedir);
-

-
	while (pkg_jobs(jobs, &pkg) == EPKG_OK) {
-
		pkg_get(pkg, PKG_OLD_VERSION, &oldversion,
-
		    PKG_FLATSIZE, &flatsize, PKG_OLD_FLATSIZE, &oldflatsize,
-
		    PKG_PKGSIZE, &pkgsize, PKG_REASON, &why);
-

-
		if (pkg_is_locked(pkg)) {
-
			pkg_printf("\tPackage %n-%v is locked ", pkg, pkg);
-
			switch (type) {
-
			case PKG_JOBS_INSTALL:
-
			case PKG_JOBS_UPGRADE:
-
				/* If it's a new install, then it
-
				 * cannot have been locked yet. */
-
				if (oldversion != NULL) {
-
					switch(pkg_version_change(pkg)) {
-
					case PKG_UPGRADE:
-
						pkg_printf("and may not be upgraded to version %v\n", pkg);
-
						break;
-
					case PKG_REINSTALL:
-
						printf("and may not be reinstalled\n");
-
						break;
-
					case PKG_DOWNGRADE:
-
						pkg_printf("and may not be downgraded to version %v\n", pkg);
-
						break;
-
					}
-
					continue;
-
				} 
-
				break;
-
			case PKG_JOBS_DEINSTALL:
-
			case PKG_JOBS_AUTOREMOVE:
-
				printf("and may not be deinstalled\n");
-
				continue;
-
				break;
-
			case PKG_JOBS_FETCH:
-
				printf("but a new package can still be fetched\n");
-
				break;
-
			}

-
		}
+
	pkg_config_string(PKG_CONFIG_CACHEDIR, &cachedir);
+
	pkg_get(pkg, PKG_OLD_VERSION, &oldversion,
+
			PKG_FLATSIZE, &flatsize, PKG_OLD_FLATSIZE, &oldflatsize,
+
			PKG_PKGSIZE, &pkgsize, PKG_REASON, &why);

+
	if (pkg_is_locked(pkg)) {
+
		pkg_printf("\tPackage %n-%v is locked ", pkg, pkg);
		switch (type) {
		case PKG_JOBS_INSTALL:
		case PKG_JOBS_UPGRADE:
-
			pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, pkg);
-

-
			if (stat(path, &st) == -1 || pkgsize != st.st_size)
-
				/* file looks corrupted (wrong size),
-
				   assume a checksum mismatch will
-
				   occur later and the file will be
-
				   fetched from remote again */
-

-
				dlsize += pkgsize;
-

+
			/* If it's a new install, then it
+
			 * cannot have been locked yet. */
			if (oldversion != NULL) {
-
				switch (pkg_version_change(pkg)) {
-
				case PKG_DOWNGRADE:
-
					pkg_printf("\tDowngrading %n: %V -> %v", pkg, pkg, pkg);
-
					if (pkg_repos_total_count() > 1)
-
						pkg_printf(" [%N]", pkg);
-
					printf("\n");
+
				switch(pkg_version_change(pkg)) {
+
				case PKG_UPGRADE:
+
					pkg_printf("and may not be upgraded to version %v\n", pkg);
					break;
				case PKG_REINSTALL:
-
					pkg_printf("\tReinstalling %n-%v", pkg, pkg);
-
					if (pkg_repos_total_count() > 1)
-
						pkg_printf(" [%N]", pkg);
-
					if (why != NULL)
-
						printf(" (%s)", why);
-
					printf("\n");
+
					printf("and may not be reinstalled\n");
					break;
-
				case PKG_UPGRADE:
-
					pkg_printf("\tUpgrading %n: %V -> %v", pkg, pkg, pkg);
-
					if (pkg_repos_total_count() > 1)
-
						pkg_printf(" [%N]", pkg);
-
					printf("\n");
+
				case PKG_DOWNGRADE:
+
					pkg_printf("and may not be downgraded to version %v\n", pkg);
					break;
				}
-
				oldsize += oldflatsize;
-
				newsize += flatsize;
-
			} else {
-
				newsize += flatsize;
-

-
				pkg_printf("\tInstalling %n: %v", pkg, pkg);
-
				if (pkg_repos_total_count() > 1)
-
					pkg_printf(" [%N]", pkg);
-
				printf("\n");
+
				return;
			}
			break;
		case PKG_JOBS_DEINSTALL:
		case PKG_JOBS_AUTOREMOVE:
-
			oldsize += oldflatsize;
-
			newsize += flatsize;
-
			
-
			pkg_printf("\t%n-%v\n", pkg, pkg);
+
			printf("and may not be deinstalled\n");
+
			return;
			break;
		case PKG_JOBS_FETCH:
-
			dlsize += pkgsize;
-
			pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, pkg);
-
			if (stat(path, &st) != -1)
-
				oldsize = st.st_size;
-
			else
-
				oldsize = 0;
-
			dlsize -= oldsize;
+
			printf("but a new package can still be fetched\n");
+
			break;
+
		}

-
			humanize_number(size, sizeof(size), pkgsize, "B", HN_AUTOSCALE, 0);
+
	}

-
			pkg_printf("\t%n-%v ", pkg, pkg);
-
			printf("(%" PRId64 "%% of %s)\n", 100 - (100 * oldsize)/pkgsize, size);
-
			break;
+
	switch (type) {
+
	case PKG_JOBS_INSTALL:
+
	case PKG_JOBS_UPGRADE:
+
		pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, pkg);
+

+
		if (stat(path, &st) == -1 || pkgsize != st.st_size)
+
			/* file looks corrupted (wrong size),
+
					   assume a checksum mismatch will
+
					   occur later and the file will be
+
					   fetched from remote again */
+

+
			*dlsize += pkgsize;
+

+
		if (oldversion != NULL) {
+
			switch (pkg_version_change(pkg)) {
+
			case PKG_DOWNGRADE:
+
				pkg_printf("\tDowngrading %n: %V -> %v", pkg, pkg, pkg);
+
				if (pkg_repos_total_count() > 1)
+
					pkg_printf(" [%N]", pkg);
+
				printf("\n");
+
				break;
+
			case PKG_REINSTALL:
+
				pkg_printf("\tReinstalling %n-%v", pkg, pkg);
+
				if (pkg_repos_total_count() > 1)
+
					pkg_printf(" [%N]", pkg);
+
				if (why != NULL)
+
					printf(" (%s)", why);
+
				printf("\n");
+
				break;
+
			case PKG_UPGRADE:
+
				pkg_printf("\tUpgrading %n: %V -> %v", pkg, pkg, pkg);
+
				if (pkg_repos_total_count() > 1)
+
					pkg_printf(" [%N]", pkg);
+
				printf("\n");
+
				break;
+
			}
+
			*oldsize += oldflatsize;
+
			*newsize += flatsize;
+
		} else {
+
			*newsize += flatsize;
+

+
			pkg_printf("\tInstalling %n: %v", pkg, pkg);
+
			if (pkg_repos_total_count() > 1)
+
				pkg_printf(" [%N]", pkg);
+
			printf("\n");
		}
+
		break;
+
	case PKG_JOBS_DEINSTALL:
+
	case PKG_JOBS_AUTOREMOVE:
+
		*oldsize += oldflatsize;
+
		*newsize += flatsize;
+

+
		pkg_printf("\t%n-%v\n", pkg, pkg);
+
		break;
+
	case PKG_JOBS_FETCH:
+
		*dlsize += pkgsize;
+
		pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, pkg);
+
		if (stat(path, &st) != -1)
+
			*oldsize = st.st_size;
+
		else
+
			*oldsize = 0;
+
		*dlsize -= *oldsize;
+

+
		humanize_number(size, sizeof(size), pkgsize, "B", HN_AUTOSCALE, 0);
+

+
		pkg_printf("\t%n-%v ", pkg, pkg);
+
		printf("(%" PRId64 "%% of %s)\n", 100 - (100 * (*oldsize))/pkgsize, size);
+
		break;
+
	}
+
}
+

+
void
+
print_jobs_summary(struct pkg_jobs *jobs, const char *msg, ...)
+
{
+
	struct pkg *pkg = NULL;
+
	char size[7];
+
	va_list ap;
+
	pkg_jobs_t type;
+
	int64_t dlsize, oldsize, newsize;
+

+
	dlsize = oldsize = newsize = 0;
+
	type = pkg_jobs_type(jobs);
+

+
	va_start(ap, msg);
+
	vprintf(msg, ap);
+
	va_end(ap);
+

+
	while (pkg_jobs_add_iter(jobs, &pkg) == EPKG_OK) {
+
		print_jobs_summary_pkg(pkg, type, &oldsize, &newsize, &dlsize);
	}

+
	pkg = NULL;
+
	while (pkg_jobs_delete_iter(jobs, &pkg) == EPKG_OK) {
+
		print_jobs_summary_pkg(pkg, type, &oldsize, &newsize, &dlsize);
+
	}
+

+

	if (oldsize > newsize) {
		humanize_number(size, sizeof(size), oldsize - newsize, "B", HN_AUTOSCALE, 0);

modified pkg/version.c
@@ -144,7 +144,7 @@ exec_version(int argc, char **argv)
	unsigned int opt = 0;
	int ch;
	FILE *indexfile;
-
	char indexpath[MAXPATHLEN + 1];
+
	char indexpath[MAXPATHLEN];
	struct index_entry *indexhead = NULL;
	struct utsname u;
	int rel_major_ver;
modified pkg/which.c
@@ -49,7 +49,7 @@ exec_which(int argc, char **argv)
	struct pkgdb *db = NULL;
	struct pkgdb_it *it = NULL;
	struct pkg *pkg = NULL;
-
	char pathabs[MAXPATHLEN + 1];
+
	char pathabs[MAXPATHLEN];
	int ret = EPKG_OK, retcode = EX_SOFTWARE;
	int ch;
	bool orig = false;
modified scripts/periodic/410.pkg-audit.in
@@ -39,7 +39,7 @@ fi
# Compute PKG_DBDIR from the config file.
pkgcmd=__PREFIX__/sbin/pkg
PKG_DBDIR=`${pkgcmd} config PKG_DBDIR`
-
auditfile="${PKG_DBDIR}/auditfile"
+
auditfile="${PKG_DBDIR}/vuln.xml"

rc=0