Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge branch 'master' into indexfile
Matthew Seaman committed 12 years ago
commit e46df057a4c89eb6bc33fc616cbb7a4f59beda17
parent 019bcb3
45 files changed +1502 -445
modified external/libucl/include/ucl.h
@@ -375,9 +375,9 @@ ucl_object_t* ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt
		const char *key, size_t keylen, bool copy_key) UCL_WARN_UNUSED_RESULT;

/**
-
 * Append an element to the array object
+
 * Append an element to the front of array object
 * @param top destination object (will be created automatically if top is NULL)
-
 * @param eltelement to append (must NOT be NULL)
+
 * @param elt element to append (must NOT be NULL)
 * @return new value of top object
 */
static inline ucl_object_t * ucl_array_append (ucl_object_t *top,
@@ -392,24 +392,165 @@ ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
	}

	if (top == NULL) {
-
		top = ucl_object_new ();
-
		top->type = UCL_ARRAY;
+
		top = ucl_object_typed_new (UCL_ARRAY);
		top->value.av = elt;
		elt->next = NULL;
		elt->prev = elt;
+
		top->len = 1;
	}
	else {
		head = top->value.av;
-
		elt->prev = head->prev;
-
		head->prev->next = elt;
-
		head->prev = elt;
+
		if (head == NULL) {
+
			top->value.av = elt;
+
			elt->prev = elt;
+
		}
+
		else {
+
			elt->prev = head->prev;
+
			head->prev->next = elt;
+
			head->prev = elt;
+
		}
		elt->next = NULL;
+
		top->len ++;
	}

	return top;
}

/**
+
 * Append an element to the start of array object
+
 * @param top destination object (will be created automatically if top is NULL)
+
 * @param elt element to append (must NOT be NULL)
+
 * @return new value of top object
+
 */
+
static inline ucl_object_t * ucl_array_prepend (ucl_object_t *top,
+
		ucl_object_t *elt) UCL_WARN_UNUSED_RESULT;
+
static inline ucl_object_t *
+
ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
+
{
+
	ucl_object_t *head;
+

+
	if (elt == NULL) {
+
		return NULL;
+
	}
+

+
	if (top == NULL) {
+
		top = ucl_object_typed_new (UCL_ARRAY);
+
		top->value.av = elt;
+
		elt->next = NULL;
+
		elt->prev = elt;
+
		top->len = 1;
+
	}
+
	else {
+
		head = top->value.av;
+
		if (head == NULL) {
+
			top->value.av = elt;
+
			elt->prev = elt;
+
		}
+
		else {
+
			elt->prev = head->prev;
+
			head->prev = elt;
+
		}
+
		elt->next = head;
+
		top->value.av = elt;
+
		top->len ++;
+
	}
+

+
	return top;
+
}
+

+
/**
+
 * Removes an element `elt` from the array `top`. Caller must unref the returned object when it is not
+
 * needed.
+
 * @param top array ucl object
+
 * @param elt element to remove
+
 * @return removed element or NULL if `top` is NULL or not an array
+
 */
+
static inline ucl_object_t *
+
ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
+
{
+
	ucl_object_t *head;
+

+
	if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+
		return NULL;
+
	}
+
	head = top->value.av;
+

+
	if (elt->prev == elt) {
+
		top->value.av = NULL;
+
	}
+
	else if (elt == head) {
+
		elt->next->prev = elt->prev;
+
		top->value.av = elt->next;
+
	}
+
	else {
+
		elt->prev->next = elt->next;
+
		if (elt->next) {
+
			elt->next->prev = elt->prev;
+
		}
+
		else {
+
			head->prev = elt->prev;
+
		}
+
	}
+
	elt->next = NULL;
+
	elt->prev = elt;
+
	top->len --;
+

+
	return elt;
+
}
+

+
/**
+
 * Returns the first element of the array `top`
+
 * @param top array ucl object
+
 * @return element or NULL if `top` is NULL or not an array
+
 */
+
static inline ucl_object_t *
+
ucl_array_head (ucl_object_t *top)
+
{
+
	if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+
		return NULL;
+
	}
+
	return top->value.av;
+
}
+

+
/**
+
 * Returns the last element of the array `top`
+
 * @param top array ucl object
+
 * @return element or NULL if `top` is NULL or not an array
+
 */
+
static inline ucl_object_t *
+
ucl_array_tail (ucl_object_t *top)
+
{
+
	if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+
		return NULL;
+
	}
+
	return top->value.av->prev;
+
}
+

+
/**
+
 * Removes the last element from the array `top`. Caller must unref the returned object when it is not
+
 * needed.
+
 * @param top array ucl object
+
 * @return removed element or NULL if `top` is NULL or not an array
+
 */
+
static inline ucl_object_t *
+
ucl_array_pop_last (ucl_object_t *top)
+
{
+
	return ucl_array_delete (top, ucl_array_tail (top));
+
}
+

+
/**
+
 * Removes the first element from the array `top`. Caller must unref the returned object when it is not
+
 * needed.
+
 * @param top array ucl object
+
 * @return removed element or NULL if `top` is NULL or not an array
+
 */
+
static inline ucl_object_t *
+
ucl_array_pop_first (ucl_object_t *top)
+
{
+
	return ucl_array_delete (top, ucl_array_head (top));
+
}
+

+
/**
 * Append a element to another element forming an implicit array
 * @param head head to append (may be NULL)
 * @param elt new element
@@ -707,7 +848,7 @@ ucl_object_ref (ucl_object_t *obj) {
 */
static inline void
ucl_object_unref (ucl_object_t *obj) {
-
	if (--obj->ref <= 0) {
+
	if (obj != NULL && --obj->ref <= 0) {
		ucl_object_free (obj);
	}
}
modified external/libucl/src/ucl_emitter.c
@@ -754,7 +754,8 @@ ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type)
	return res;
}

-
bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
+
bool
+
ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
		struct ucl_emitter_functions *emitter)
{
	if (emit_type == UCL_EMIT_JSON) {
@@ -773,3 +774,56 @@ bool ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
	/* XXX: need some error checks here */
	return true;
}
+

+

+
unsigned char *
+
ucl_object_emit_single_json (ucl_object_t *obj)
+
{
+
	UT_string *buf = NULL;
+
	unsigned char *res = NULL;
+

+
	if (obj == NULL) {
+
		return NULL;
+
	}
+

+
	utstring_new (buf);
+

+
	if (buf != NULL) {
+
		switch (obj->type) {
+
		case UCL_OBJECT:
+
			ucl_utstring_append_len ("object", 6, buf);
+
			break;
+
		case UCL_ARRAY:
+
			ucl_utstring_append_len ("array", 5, buf);
+
			break;
+
		case UCL_INT:
+
			ucl_utstring_append_int (obj->value.iv, buf);
+
			break;
+
		case UCL_FLOAT:
+
		case UCL_TIME:
+
			ucl_utstring_append_double (obj->value.dv, buf);
+
			break;
+
		case UCL_NULL:
+
			ucl_utstring_append_len ("null", 4, buf);
+
			break;
+
		case UCL_BOOLEAN:
+
			if (obj->value.iv) {
+
				ucl_utstring_append_len ("true", 4, buf);
+
			}
+
			else {
+
				ucl_utstring_append_len ("false", 5, buf);
+
			}
+
			break;
+
		case UCL_STRING:
+
			ucl_utstring_append_len (obj->value.sv, obj->len, buf);
+
			break;
+
		case UCL_USERDATA:
+
			ucl_utstring_append_len ("userdata", 8, buf);
+
			break;
+
		}
+
		res = utstring_body (buf);
+
		free (buf);
+
	}
+

+
	return res;
+
}
modified external/libucl/src/ucl_internal.h
@@ -159,6 +159,8 @@ size_t ucl_unescape_json_string (char *str, size_t len);
 */
bool ucl_include_handler (const unsigned char *data, size_t len, void* ud);

+
bool ucl_try_include_handler (const unsigned char *data, size_t len, void* ud);
+

/**
 * Handle includes macro
 * @param data include data
@@ -280,5 +282,11 @@ ucl_hash_insert_object (ucl_hash_t *hashlin, ucl_object_t *obj)
	return hashlin;
}

+
/**
+
 * Emit a single object to string
+
 * @param obj
+
 * @return
+
 */
+
unsigned char * ucl_object_emit_single_json (ucl_object_t *obj);

#endif /* UCL_INTERNAL_H_ */
modified external/libucl/src/ucl_parser.c
@@ -86,13 +86,18 @@ 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)
{
-
	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);
+
	if (chunk->pos < chunk->end) {
+
		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);
+
		}
	}
	else {
-
		ucl_create_err (err, "error on line %d at column %d: '%s', character: '0x%02x'",
-
				chunk->line, chunk->column, str, (int)*chunk->pos);
+
		ucl_create_err (err, "error at the end of chunk: %s", str);
	}
}

@@ -150,7 +155,7 @@ start:
				ucl_chunk_skipc (chunk, p);
			}
			if (comments_nested != 0) {
-
				ucl_set_err (chunk, UCL_ENESTED, "comments nesting is invalid", &parser->err);
+
				ucl_set_err (chunk, UCL_ENESTED, "unfinished multiline comment", &parser->err);
				return false;
			}
		}
@@ -553,18 +558,30 @@ ucl_maybe_parse_number (ucl_object_t *obj,
{
	const char *p = start, *c = start;
	char *endptr;
-
	bool got_dot = false, got_exp = false, need_double = false, is_date = false, valid_start = false;
+
	bool got_dot = false, got_exp = false, need_double = false,
+
			is_date = false, valid_start = false, is_hex = false,
+
			is_neg = false;
	double dv = 0;
	int64_t lv = 0;

	if (*p == '-') {
+
		is_neg = true;
+
		c ++;
		p ++;
	}
	while (p < end) {
-
		if (isdigit (*p)) {
+
		if (is_hex && isxdigit (*p)) {
+
			p ++;
+
		}
+
		else if (isdigit (*p)) {
			valid_start = true;
			p ++;
		}
+
		else if (!is_hex && (*p == 'x' || *p == 'X')) {
+
			is_hex = true;
+
			allow_double = false;
+
			c = p + 1;
+
		}
		else if (allow_double) {
			if (p == c) {
				/* Empty digits sequence, not a number */
@@ -627,7 +644,12 @@ ucl_maybe_parse_number (ucl_object_t *obj,
		dv = strtod (c, &endptr);
	}
	else {
-
		lv = strtoimax (c, &endptr, 10);
+
		if (is_hex) {
+
			lv = strtoimax (c, &endptr, 16);
+
		}
+
		else {
+
			lv = strtoimax (c, &endptr, 10);
+
		}
	}
	if (errno == ERANGE) {
		*pos = start;
@@ -758,11 +780,11 @@ ucl_maybe_parse_number (ucl_object_t *obj,
		else {
			obj->type = UCL_TIME;
		}
-
		obj->value.dv = dv;
+
		obj->value.dv = is_neg ? (-dv) : dv;
	}
	else {
		obj->type = UCL_INT;
-
		obj->value.iv = lv;
+
		obj->value.iv = is_neg ? (-lv) : lv;
	}
	*pos = p;
	return 0;
@@ -848,10 +870,6 @@ ucl_lex_json_string (struct ucl_parser *parser,
					ucl_chunk_skipc (chunk, p);
				}
			}
-
			else {
-
				ucl_set_err (chunk, UCL_ESYNTAX, "invalid escape character", &parser->err);
-
				return false;
-
			}
			*need_unescape = true;
			*ucl_escape = true;
			continue;
@@ -883,7 +901,7 @@ static bool
ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_key, bool *end_of_object)
{
	const unsigned char *p, *c = NULL, *end, *t;
-
	const char *key;
+
	const char *key = NULL;
	bool got_quote = false, got_eq = false, got_semicolon = false,
			need_unescape = false, ucl_escape = false, var_expand = false,
			got_content = false, got_sep = false;
@@ -1059,10 +1077,12 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
	keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
			&key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false);
	if (keylen == -1) {
+
		ucl_object_free(nobj);
		return false;
	}
	else if (keylen == 0) {
		ucl_set_err (chunk, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
+
		ucl_object_free(nobj);
		return false;
	}

@@ -1074,6 +1094,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
		container = ucl_hash_insert_object (container, nobj);
		nobj->prev = nobj;
		nobj->next = NULL;
+
		parser->stack->obj->len ++;
	}
	else {
		DL_APPEND (tobj, nobj);
@@ -1097,7 +1118,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
 */
static bool
ucl_parse_string_value (struct ucl_parser *parser,
-
		struct ucl_chunk *chunk, bool *var_expand)
+
		struct ucl_chunk *chunk, bool *var_expand, bool *need_unescape)
{
	const unsigned char *p;
	enum {
@@ -1117,7 +1138,7 @@ ucl_parse_string_value (struct ucl_parser *parser,
		}
		else if (*p == '}') {
			braces[UCL_BRACE_FIGURE][1] ++;
-
			if (braces[UCL_BRACE_FIGURE][1] == braces[UCL_BRACE_FIGURE][0]) {
+
			if (braces[UCL_BRACE_FIGURE][1] <= braces[UCL_BRACE_FIGURE][0]) {
				/* This is not a termination symbol, continue */
				ucl_chunk_skipc (chunk, p);
				continue;
@@ -1129,7 +1150,7 @@ ucl_parse_string_value (struct ucl_parser *parser,
		}
		else if (*p == ']') {
			braces[UCL_BRACE_SQUARE][1] ++;
-
			if (braces[UCL_BRACE_SQUARE][1] == braces[UCL_BRACE_SQUARE][0]) {
+
			if (braces[UCL_BRACE_SQUARE][1] <= braces[UCL_BRACE_SQUARE][0]) {
				/* This is not a termination symbol, continue */
				ucl_chunk_skipc (chunk, p);
				continue;
@@ -1138,6 +1159,14 @@ ucl_parse_string_value (struct ucl_parser *parser,
		else if (*p == '$') {
			*var_expand = true;
		}
+
		else if (*p == '\\') {
+
			*need_unescape = true;
+
			ucl_chunk_skipc (chunk, p);
+
			if (p < chunk->end) {
+
				ucl_chunk_skipc (chunk, p);
+
			}
+
			continue;
+
		}

		if (ucl_lex_is_atom_end (*p) || (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1]))) {
			break;
@@ -1230,6 +1259,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
				DL_APPEND (t, obj);
				parser->cur_obj = obj;
				parser->stack->obj->value.av = t;
+
				parser->stack->obj->len ++;
			}
			else {
				/* Object has been already allocated */
@@ -1328,7 +1358,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
				/* Fallback to normal string */
			}

-
			if (!ucl_parse_string_value (parser, chunk, &var_expand)) {
+
			if (!ucl_parse_string_value (parser, chunk, &var_expand, &need_unescape)) {
				return false;
			}
			/* Cut trailing spaces */
@@ -1349,7 +1379,8 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
			else if (!ucl_maybe_parse_boolean (obj, c, str_len)) {
				obj->type = UCL_STRING;
				if ((str_len = ucl_copy_or_store_ptr (parser, c, &obj->trash_stack[UCL_TRASH_VALUE],
-
						&obj->value.sv, str_len, false, false, var_expand)) == -1) {
+
						&obj->value.sv, str_len, need_unescape,
+
						false, var_expand)) == -1) {
					return false;
				}
				obj->len = str_len;
@@ -1377,7 +1408,6 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
	const unsigned char *p;
	bool got_sep = false;
	struct ucl_stack *st;
-
	int last_level;

	p = chunk->pos;

@@ -1406,14 +1436,15 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)

					/* Pop all nested objects from a stack */
					st = parser->stack;
-
					last_level = st->level;
					parser->stack = st->next;
					UCL_FREE (sizeof (struct ucl_stack), st);

-
					while (parser->stack != NULL && last_level > 0 && parser->stack->level == last_level) {
+
					while (parser->stack != NULL) {
						st = parser->stack;
+
						if (st->next == NULL || st->next->level == st->level) {
+
							break;
+
						}
						parser->stack = st->next;
-
						last_level = st->level;
						UCL_FREE (sizeof (struct ucl_stack), st);
					}
				}
@@ -1596,6 +1627,14 @@ ucl_state_machine (struct ucl_parser *parser)
				parser->state = UCL_STATE_AFTER_VALUE;
				continue;
			}
+
			if (parser->stack == NULL) {
+
				/* No objects are on stack, but we want to parse a key */
+
				ucl_set_err (chunk, UCL_ESYNTAX, "top object is finished but the parser "
+
						"expects a key", &parser->err);
+
				parser->prev_state = parser->state;
+
				parser->state = UCL_STATE_ERROR;
+
				return false;
+
			}
			if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) {
				parser->prev_state = parser->state;
				parser->state = UCL_STATE_ERROR;
@@ -1657,9 +1696,11 @@ ucl_state_machine (struct ucl_parser *parser)
			}
			else if (p - c > 0) {
				/* We got macro name */
-
				HASH_FIND (hh, parser->macroes, c, (p - c), macro);
+
				macro_len = (size_t)(p - c);
+
				HASH_FIND (hh, parser->macroes, c, macro_len, macro);
				if (macro == NULL) {
-
					ucl_create_err (&parser->err, "error on line %d at column %d: unknown macro: '%.*s', character: '%c'",
+
					ucl_create_err (&parser->err, "error on line %d at column %d: "
+
							"unknown macro: '%.*s', character: '%c'",
								chunk->line, chunk->column, (int)(p - c), c, *chunk->pos);
					parser->state = UCL_STATE_ERROR;
					return false;
@@ -1706,7 +1747,7 @@ ucl_state_machine (struct ucl_parser *parser)
			break;
		default:
			/* TODO: add all states */
-
			ucl_set_err (chunk, UCL_EMACRO, "internal error: parser is in an unknown state", &parser->err);
+
			ucl_set_err (chunk, UCL_EINTERNAL, "internal error: parser is in an unknown state", &parser->err);
			parser->state = UCL_STATE_ERROR;
			return false;
		}
@@ -1724,6 +1765,7 @@ ucl_parser_new (int flags)
	memset (new, 0, sizeof (struct ucl_parser));

	ucl_parser_register_macro (new, "include", ucl_include_handler, new);
+
	ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new);
	ucl_parser_register_macro (new, "includes", ucl_includes_handler, new);

	new->flags = flags;
modified external/libucl/src/ucl_util.c
@@ -161,7 +161,7 @@ ucl_unescape_json_string (char *str, size_t len)
				}
				break;
			default:
-
				*t++ = '?';
+
				*t++ = *h;
				break;
			}
			h ++;
@@ -208,8 +208,7 @@ ucl_copy_value_trash (ucl_object_t *obj)
		}
		else {
			/* Just emit value in json notation */
-
			obj->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit (obj,
-
					UCL_EMIT_JSON_COMPACT);
+
			obj->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj);
			obj->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]);
		}
		obj->flags |= UCL_OBJECT_ALLOCATED_VALUE;
@@ -220,7 +219,7 @@ ucl_copy_value_trash (ucl_object_t *obj)
ucl_object_t*
ucl_parser_get_object (struct ucl_parser *parser)
{
-
	if (parser->state != UCL_STATE_ERROR) {
+
	if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
		return ucl_object_ref (parser->top_obj);
	}

@@ -340,7 +339,8 @@ ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud)
 * @return
 */
static bool
-
ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, UT_string **err)
+
ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen,
+
		UT_string **err, bool must_exist)
{

#ifdef HAVE_FETCH_H
@@ -355,8 +355,10 @@ ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, UT
		return false;
	}
	if ((in = fetchXGet (fetch_url, &us, "")) == NULL) {
-
		ucl_create_err (err, "cannot fetch URL %s: %s",
+
		if (!must_exist) {
+
			ucl_create_err (err, "cannot fetch URL %s: %s",
				url, strerror (errno));
+
		}
		fetchFreeURL (fetch_url);
		return false;
	}
@@ -403,8 +405,10 @@ ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, UT
	curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata);

	if ((r = curl_easy_perform (curl)) != CURLE_OK) {
-
		ucl_create_err (err, "error fetching URL %s: %s",
+
		if (!must_exist) {
+
			ucl_create_err (err, "error fetching URL %s: %s",
				url, curl_easy_strerror (r));
+
		}
		curl_easy_cleanup (curl);
		if (cbdata.buf) {
			free (cbdata.buf);
@@ -430,14 +434,17 @@ ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, UT
 * @return
 */
static bool
-
ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen, UT_string **err)
+
ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen,
+
		UT_string **err, bool must_exist)
{
	int fd;
	struct stat st;

	if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) {
-
		ucl_create_err (err, "cannot stat file %s: %s",
-
				filename, strerror (errno));
+
		if (must_exist) {
+
			ucl_create_err (err, "cannot stat file %s: %s",
+
					filename, strerror (errno));
+
		}
		return false;
	}
	if (st.st_size == 0) {
@@ -523,7 +530,7 @@ ucl_sig_check (const unsigned char *data, size_t datalen,
 */
static bool
ucl_include_url (const unsigned char *data, size_t len,
-
		struct ucl_parser *parser, bool check_signature)
+
		struct ucl_parser *parser, bool check_signature, bool must_exist)
{

	bool res;
@@ -535,8 +542,8 @@ ucl_include_url (const unsigned char *data, size_t len,

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

-
	if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err)) {
-
		return false;
+
	if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) {
+
		return (!must_exist || false);
	}

	if (check_signature) {
@@ -545,7 +552,7 @@ ucl_include_url (const unsigned char *data, size_t len,
		size_t siglen = 0;
		/* We need to check signature first */
		snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data);
-
		if (!ucl_fetch_file (urlbuf, &sigbuf, &siglen, &parser->err)) {
+
		if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) {
			return false;
		}
		if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
@@ -592,7 +599,7 @@ ucl_include_url (const unsigned char *data, size_t len,
 */
static bool
ucl_include_file (const unsigned char *data, size_t len,
-
		struct ucl_parser *parser, bool check_signature)
+
		struct ucl_parser *parser, bool check_signature, bool must_exist)
{
	bool res;
	struct ucl_chunk *chunk;
@@ -603,14 +610,17 @@ ucl_include_file (const unsigned char *data, size_t len,

	snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
	if (realpath (filebuf, realbuf) == NULL) {
+
		if (!must_exist) {
+
			return true;
+
		}
		ucl_create_err (&parser->err, "cannot open file %s: %s",
									filebuf,
									strerror (errno));
		return false;
	}

-
	if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err)) {
-
		return false;
+
	if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
+
		return (!must_exist || false);
	}

	if (check_signature) {
@@ -619,7 +629,7 @@ ucl_include_file (const unsigned char *data, size_t len,
		size_t siglen = 0;
		/* We need to check signature first */
		snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
-
		if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err)) {
+
		if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) {
			return false;
		}
		if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
@@ -676,10 +686,10 @@ ucl_include_handler (const unsigned char *data, size_t len, void* ud)

	if (*data == '/' || *data == '.') {
		/* Try to load a file */
-
		return ucl_include_file (data, len, parser, false);
+
		return ucl_include_file (data, len, parser, false, true);
	}

-
	return ucl_include_url (data, len, parser, false);
+
	return ucl_include_url (data, len, parser, false, true);
}

/**
@@ -697,10 +707,24 @@ ucl_includes_handler (const unsigned char *data, size_t len, void* ud)

	if (*data == '/' || *data == '.') {
		/* Try to load a file */
-
		return ucl_include_file (data, len, parser, true);
+
		return ucl_include_file (data, len, parser, true, true);
+
	}
+

+
	return ucl_include_url (data, len, parser, true, true);
+
}
+

+

+
bool
+
ucl_try_include_handler (const unsigned char *data, size_t len, void* ud)
+
{
+
	struct ucl_parser *parser = ud;
+

+
	if (*data == '/' || *data == '.') {
+
		/* Try to load a file */
+
		return ucl_include_file (data, len, parser, false, false);
	}

-
	return ucl_include_url (data, len, parser, true);
+
	return ucl_include_url (data, len, parser, false, false);
}

bool
@@ -748,7 +772,7 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
		return false;
	}

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

modified libpkg/Makefile
@@ -299,10 +299,6 @@ LDADD+= -larchive \
		-lutil \
		-lpthread

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

.if defined(DEBUG_FLAGS)
.if ${DEBUG_FLAGS} == 1
DEBUG_FLAGS=	-ggdb -O0
modified libpkg/pkg.h.in
@@ -499,6 +499,17 @@ typedef enum {
	PKG_UPGRADE,
} pkg_change_t;

+
/**
+
 * Locking types for database:
+
 * `PKGDB_LOCK_READONLY`: lock for read only queries (can be nested)
+
 * `PKGDB_LOCK_ADVISORY`: write to DB inside a transaction (allows `PKGDB_LOCK_READONLY`)
+
 * `PKGDB_LOCK_EXCLUSIVE`: possibly destructive operations (does not allow other locks)
+
 */
+
typedef enum {
+
	PKGDB_LOCK_READONLY,
+
	PKGDB_LOCK_ADVISORY,
+
	PKGDB_LOCK_EXCLUSIVE
+
} pkgdb_lock_t;

#define PKG_OPEN_MANIFEST_ONLY 0x1
#define PKG_OPEN_MANIFEST_COMPACT (0x1 << 1)
@@ -1017,6 +1028,14 @@ int pkgdb_access(unsigned mode, unsigned database);
int pkgdb_open(struct pkgdb **db, pkgdb_t type);

/**
+
 * Locking functions
+
 */
+
int pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type, double delay, unsigned int retries);
+
int pkgdb_upgrade_lock(struct pkgdb *db, pkgdb_lock_t old_type, pkgdb_lock_t new_type,
+
		double delay, unsigned int retries);
+
int pkgdb_release_lock(struct pkgdb *db, pkgdb_lock_t type);
+

+
/**
 * Close and free the struct pkgdb.
 */
void pkgdb_close(struct pkgdb *db);
@@ -1182,13 +1201,18 @@ pkg_jobs_t pkg_jobs_type(struct pkg_jobs *j);
int pkg_jobs_count(struct pkg_jobs *jobs);

/**
+
 * Returns the number of total elements in pkg universe
+
 */
+
int pkg_jobs_total(struct pkg_jobs *jobs);
+

+
/**
 * Iterates over the packages in the jobs queue.
 * @param iter Must be set to NULL for the first call.
 * @return A next pkg or NULL.
 */
-
struct pkg * pkg_jobs_add_iter(struct pkg_jobs *jobs, void **iter);
-
struct pkg * pkg_jobs_delete_iter(struct pkg_jobs *jobs, void **iter);
-
struct pkg * pkg_jobs_upgrade_iter(struct pkg_jobs *jobs, void **iter);
+
bool pkg_jobs_add_iter(struct pkg_jobs *jobs, void **iter, struct pkg **n, struct pkg **o);
+
bool pkg_jobs_delete_iter(struct pkg_jobs *jobs, void **iter, struct pkg **n, struct pkg **o);
+
bool pkg_jobs_upgrade_iter(struct pkg_jobs *jobs, void **iter, struct pkg **n, struct pkg **o);

/**
 * Apply the jobs in the queue (fetch and install).
modified libpkg/pkg_cudf.c
@@ -91,14 +91,40 @@ cudf_print_element(FILE *f, const char *line, bool has_next, int *column)
	return (ret);
}

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

+
	ret += cudf_print_package_name(f, origin);
+
	ret += fprintf(f, "=%d", ver);
+

+
	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)
+
cudf_emit_pkg(struct pkg *pkg, int version, FILE *f,
+
		struct pkg_job_universe_item *conflicts_chain)
{
	const char *origin;
	struct pkg_dep *dep, *dtmp;
	struct pkg_provide *prov, *ptmp;
	struct pkg_conflict *conflict, *ctmp;
-
	int column = 0;
+
	struct pkg_job_universe_item *u;
+
	int column = 0, ver;

	pkg_get(pkg, PKG_ORIGIN, &origin);
	if (fprintf(f, "package: ") < 0)
@@ -134,7 +160,7 @@ cudf_emit_pkg(struct pkg *pkg, int version, FILE *f)
	}

	column = 0;
-
	if (HASH_COUNT(pkg->conflicts) > 0) {
+
	if (HASH_COUNT(pkg->conflicts) > 0 || conflicts_chain->next != NULL) {
		if (fprintf(f, "conflicts: ") < 0)
			return (EPKG_FATAL);
		HASH_ITER(hh, pkg->conflicts, conflict, ctmp) {
@@ -143,6 +169,16 @@ cudf_emit_pkg(struct pkg *pkg, int version, FILE *f)
				return (EPKG_FATAL);
			}
		}
+
		ver = 1;
+
		LL_FOREACH(conflicts_chain, u) {
+
			if (u->pkg != pkg) {
+
				if (cudf_print_conflict(f, origin, ver,
+
						(u->next != NULL && u->next->pkg != pkg), &column) < 0) {
+
					return (EPKG_FATAL);
+
				}
+
			}
+
			ver ++;
+
		}
	}

	if (fprintf(f, "installed: %s\n\n", pkg->type == PKG_INSTALLED ?
@@ -221,11 +257,26 @@ pkg_jobs_cudf_emit_file(struct pkg_jobs *j, pkg_jobs_t t, FILE *f)
		return (EPKG_FATAL);

	HASH_ITER(hh, j->universe, it, itmp) {
-
		LL_SORT(it, pkg_cudf_version_cmp);
+
		/* XXX
+
		 * Here are dragons:
+
		 * after sorting it we actually modify the head of the list, but there is
+
		 * no simple way to update a pointer in uthash, therefore universe hash
+
		 * contains not a head of list but a random elt of the conflicts chain:
+
		 * before:
+
		 * head -> elt1 -> elt2 -> elt3
+
		 * after:
+
		 * elt1 -> elt3 -> head -> elt2
+
		 *
+
		 * But hash would still point to head whilst the real head is elt1.
+
		 * So after sorting we need to rotate conflicts chain back to find the new
+
		 * head.
+
		 */
+
		DL_SORT(it, pkg_cudf_version_cmp);
+

		version = 1;
		LL_FOREACH(it, icur) {
			pkg = icur->pkg;
-
			if (cudf_emit_pkg(pkg, version++, f) != EPKG_OK)
+
			if (cudf_emit_pkg(pkg, version++, f, it) != EPKG_OK)
				return (EPKG_FATAL);
		}
	}
@@ -278,7 +329,9 @@ cudf_strdup(const char *in)
}

static void
-
pkg_jobs_cudf_insert_res_job (struct pkg_solved **target, struct pkg_job_universe_item *it)
+
pkg_jobs_cudf_insert_res_job (struct pkg_solved **target,
+
		struct pkg_job_universe_item *it_new,
+
		struct pkg_job_universe_item *it_old)
{
	struct pkg_solved *res;

@@ -287,8 +340,12 @@ pkg_jobs_cudf_insert_res_job (struct pkg_solved **target, struct pkg_job_univers
		pkg_emit_errno("calloc", "pkg_solved");
		return;
	}
-
	res->priority = it->priority;
-
	res->pkg[0] = it->pkg;
+
	res->priority = it_new->priority;
+
	res->pkg[0] = it_new->pkg;
+
	if (it_old != NULL) {
+
		res->pkg[1] = it_old->pkg;
+
		res->priority = MAX(it_old->priority, res->priority);
+
	}
	DL_APPEND(*target, res);
}

@@ -302,13 +359,14 @@ struct pkg_cudf_entry {
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;
+
	struct pkg_job_universe_item *it, *cur, *selected = NULL, *old = NULL, *head;
+
	const char *origin, *oldversion;
+
	int ver, n;

	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);
+
		pkg_emit_error("package %s is found in CUDF output but not in the universe",
+
				entry->origin);
		return (EPKG_FATAL);
	}

@@ -319,26 +377,59 @@ pkg_jobs_cudf_add_package(struct pkg_jobs *j, struct pkg_cudf_entry *entry)
	 */
	ver = strtoul(entry->version, NULL, 10);

-
	LL_FOREACH(it, cur) {
-
		if (--ver == 0) {
+
	/* Find the old head, see the comment in `pkg_jobs_cudf_emit_file` */
+
	cur = it;
+
	do {
+
		head = cur;
+
		cur = cur->prev;
+
	} while (cur->next != NULL);
+

+
	n = 1;
+
	LL_FOREACH(head, cur) {
+
		if (n == ver) {
			selected = cur;
			break;
		}
+
		n ++;
	}

	if (selected == NULL) {
-
		pkg_emit_error("package %s is found in CUDF output but the universe has no such version", entry->origin);
+
		pkg_emit_error("package %s-%d is found in CUDF output but the "
+
				"universe has no such version (only %d versions found)",
+
				entry->origin, ver, n);
		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) {
-
		pkg_jobs_cudf_insert_res_job (&j->jobs_add, selected);
-
		j->count ++;
+
	if (n == 1) {
+
		if (entry->installed && selected->pkg->type != PKG_INSTALLED) {
+
			pkg_debug(3, "pkg_cudf: schedule installation of %s(%d)",
+
					entry->origin, ver);
+
			pkg_jobs_cudf_insert_res_job (&j->jobs_add, selected, NULL);
+
			j->count ++;
+
		}
+
		else if (!entry->installed && selected->pkg->type == PKG_INSTALLED) {
+
			pkg_debug(3, "pkg_cudf: schedule removing of %s(%d)",
+
					entry->origin, ver);
+
			pkg_jobs_cudf_insert_res_job (&j->jobs_delete, selected, NULL);
+
			j->count ++;
+
		}
	}
-
	else if (!entry->installed && selected->pkg->type == PKG_INSTALLED) {
-
		pkg_jobs_cudf_insert_res_job (&j->jobs_delete, selected);
+
	else {
+
		/* Define upgrade */
+
		LL_FOREACH(head, cur) {
+
			if (cur != selected) {
+
				old = cur;
+
				break;
+
			}
+
		}
+
		pkg_debug(3, "pkg_cudf: schedule upgrade of %s(to %d)",
+
				entry->origin, ver);
+
		assert(old != NULL);
+
		/* XXX: this is a hack due to iterators stupidity */
+
		pkg_get(old->pkg, PKG_VERSION, &oldversion);
+
		pkg_set(selected->pkg, PKG_OLD_VERSION, oldversion);
+
		pkg_jobs_cudf_insert_res_job (&j->jobs_upgrade, selected, old);
		j->count ++;
	}

modified libpkg/pkg_jobs.c
@@ -101,10 +101,6 @@ pkg_jobs_free(struct pkg_jobs *j)
	if (j == NULL)
		return;

-
	if ((j->flags & PKG_FLAG_DRY_RUN) == 0 &&
-
		j->type != PKG_JOBS_FETCH)
-
		pkgdb_release_lock(j->db);
-

	HASH_ITER(hh, j->request_add, req, tmp) {
		HASH_DEL(j->request_add, req);
		free(req);
@@ -159,27 +155,28 @@ pkg_jobs_add(struct pkg_jobs *j, match_t match, char **argv, int argc)
}

#define MAKE_JOBS_ITER_FUNC(type)											\
-
    struct pkg *															\
-
    pkg_jobs_##type##_iter(struct pkg_jobs *jobs, void **iter)				\
+
    bool																	\
+
    pkg_jobs_##type##_iter(struct pkg_jobs *jobs, void **iter, 			\
+
						struct pkg **new, struct pkg **old)				\
    {																		\
    	struct pkg_solved *s;												\
-
    	struct pkg *res;													\
    	assert(iter != NULL);												\
    	if (jobs->jobs_##type == NULL) {									\
-
    		return (NULL);													\
+
    		return (false);													\
    	}																	\
    	if (*iter == NULL) {												\
    		s = jobs->jobs_##type;											\
    	}																	\
    	else if (*iter == jobs->jobs_##type) {								\
-
    		return (NULL);													\
+
    		return (false);													\
    	}																	\
    	else {																\
    		s = *iter;														\
    	}																	\
-
    	res = s->pkg[0];													\
+
    	*new = s->pkg[0];													\
+
    	*old = s->pkg[1];													\
    	*iter = s->next ? s->next : jobs->jobs_##type;						\
-
    	return (res);														\
+
    	return (true);														\
    }

MAKE_JOBS_ITER_FUNC(add)
@@ -248,7 +245,7 @@ pkg_jobs_handle_pkg_universe(struct pkg_jobs *j, struct pkg *pkg, int priority)
	seen->pkg = pkg;
	HASH_ADD_KEYPTR(hh, j->seen, seen->digest, strlen(seen->digest), seen);

-
	HASH_FIND_STR(j->universe, __DECONST(char *, origin), item);
+
	HASH_FIND_STR(j->universe, origin, item);
	if (item == NULL) {
		/* Insert new origin */
		item = calloc(1, sizeof (struct pkg_job_universe_item));
@@ -259,7 +256,10 @@ pkg_jobs_handle_pkg_universe(struct pkg_jobs *j, struct pkg *pkg, int priority)
		}
		item->pkg = pkg;
		item->priority = priority;
-
		HASH_ADD_KEYPTR(hh, j->universe, __DECONST(char *, origin), strlen(origin), item);
+
		item->prev = item;
+
		HASH_ADD_KEYPTR(hh, j->universe, origin, strlen(origin), item);
+
		j->total++;
+
		return (EPKG_OK);
	}
	else {
		/* Search for the same package added */
@@ -290,8 +290,10 @@ pkg_jobs_handle_pkg_universe(struct pkg_jobs *j, struct pkg *pkg, int priority)
			priority, name, version);
	item->pkg = pkg;
	item->priority = priority;
-
	if (tmp != NULL)
-
		tmp->next = item;
+

+
	DL_APPEND(tmp, item);
+

+
	j->total++;

	return (EPKG_OK);
}
@@ -318,7 +320,7 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg, int priority, bool re
	/* 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);
+
		HASH_FIND_STR(j->universe, pkg_dep_get(d, PKG_DEP_ORIGIN), unit);
		if (unit != NULL)
			continue;

@@ -356,7 +358,7 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg, int priority, bool re
	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);
+
		HASH_FIND_STR(j->universe, pkg_dep_get(d, PKG_DEP_ORIGIN), unit);
		if (unit != NULL)
			continue;

@@ -380,7 +382,7 @@ pkg_jobs_add_universe(struct pkg_jobs *j, struct pkg *pkg, int priority, bool re
	/* 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);
+
		HASH_FIND_STR(j->universe, pkg_conflict_origin(c), unit);
		if (unit != NULL)
			continue;

@@ -475,6 +477,8 @@ jobs_solve_upgrade(struct pkg_jobs *j)
	struct pkg *pkg = NULL;
	struct pkgdb_it *it;
	char *origin;
+
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
+
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;

	if ((j->flags & PKG_FLAG_PKG_VERSION_TEST) == PKG_FLAG_PKG_VERSION_TEST)
		if (new_pkg_version(j)) {
@@ -485,7 +489,7 @@ jobs_solve_upgrade(struct pkg_jobs *j)
	if ((it = pkgdb_query(j->db, NULL, MATCH_ALL)) == NULL)
		return (EPKG_FATAL);

-
	while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
+
	while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
		/* TODO: use repository priority here */
		pkg_jobs_add_universe(j, pkg, 0, true);
		if(pkg_is_locked(pkg)) {
@@ -501,6 +505,11 @@ jobs_solve_upgrade(struct pkg_jobs *j)
	}
	pkgdb_it_free(it);

+
	if (pkg_conflicts_request_resolve(j) != EPKG_OK) {
+
		pkg_emit_error("Cannot resolve conflicts in a request");
+
		return (EPKG_FATAL);
+
	}
+

order:

	j->solved = true;
@@ -557,7 +566,7 @@ find_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root, i
	const char *buf1, *buf2;
	bool force = false, seen = false;
	int rc = EPKG_FATAL;
-
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|
+
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;

	if (root && (j->flags & PKG_FLAG_FORCE) == PKG_FLAG_FORCE)
@@ -570,15 +579,6 @@ find_remote_pkg(struct pkg_jobs *j, const char *pattern, match_t m, bool root, i
	if (j->type == PKG_JOBS_UPGRADE && (j->flags & PKG_FLAG_FORCE) == PKG_FLAG_FORCE)
		force = true;

-
	if (j->type == PKG_JOBS_FETCH) {
-
		if ((j->flags & PKG_FLAG_WITH_DEPS) == PKG_FLAG_WITH_DEPS)
-
			flags |= PKG_LOAD_DEPS;
-
		if ((j->flags & PKG_FLAG_UPGRADES_FOR_INSTALLED) == PKG_FLAG_UPGRADES_FOR_INSTALLED)
-
			flags |= PKG_LOAD_DEPS;
-
	} else {
-
		flags |= PKG_LOAD_DEPS;
-
	}
-

	if ((it = pkgdb_rquery(j->db, pattern, m, j->reponame)) == NULL)
		return (rc);

@@ -977,20 +977,12 @@ jobs_sort_priority_inc(struct pkg_solved *r1, struct pkg_solved *r2)
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 (!dry_run && pkgdb_obtain_lock(j->db) != EPKG_OK)
-
		return (EPKG_FATAL);
-

-

	switch (j->type) {
	case PKG_JOBS_AUTOREMOVE:
		ret =jobs_solve_autoremove(j);
@@ -1083,6 +1075,14 @@ pkg_jobs_count(struct pkg_jobs *j)
	return (j->count);
}

+
int
+
pkg_jobs_total(struct pkg_jobs *j)
+
{
+
	assert(j != NULL);
+

+
	return (j->total);
+
}
+

pkg_jobs_t
pkg_jobs_type(struct pkg_jobs *j)
{
@@ -1092,23 +1092,27 @@ pkg_jobs_type(struct pkg_jobs *j)
}

static int
-
pkg_jobs_handle_install(struct pkg_solved *ps, struct pkg_jobs *j, bool handle_rc,
+
pkg_jobs_handle_install(struct pkg *new, struct pkg *old, struct pkg_jobs *j, bool handle_rc,
		const char *cachedir, struct pkg_manifest_key *keys)
{
-
	struct pkg *p = ps->pkg[0];
	struct pkg *newpkg = NULL;
-
	const char *pkgorigin, *oldversion;
+
	const char *pkgorigin, *oldversion = NULL;
	struct pkg_note *an;
	char path[MAXPATHLEN];
	bool automatic;
	int flags = 0;
	int retcode = EPKG_FATAL;

-
	pkg_get(p, PKG_ORIGIN, &pkgorigin,
-
			PKG_OLD_VERSION, &oldversion, PKG_AUTOMATIC, &automatic);
-
	an = pkg_annotation_lookup(p, "repository");
+
	pkg_get(new, PKG_ORIGIN, &pkgorigin,
+
				PKG_AUTOMATIC, &automatic);
+
	if (old != NULL)
+
		pkg_get(old, PKG_VERSION, &oldversion);
+
	else
+
		pkg_get(new, PKG_OLD_VERSION, &oldversion);
+

+
	an = pkg_annotation_lookup(new, "repository");

-
	pkg_snprintf(path, sizeof(path), "%S/%R", cachedir, p);
+
	pkg_snprintf(path, sizeof(path), "%S/%R", cachedir, new);

	if (pkg_open(&newpkg, path, keys, 0) != EPKG_OK) {
		pkgdb_transaction_rollback(j->db->sqlite, "upgrade");
@@ -1116,7 +1120,8 @@ pkg_jobs_handle_install(struct pkg_solved *ps, struct pkg_jobs *j, bool handle_r
	}

	if (oldversion != NULL) {
-
		pkg_emit_upgrade_begin(p);
+
		pkg_set(newpkg, PKG_OLD_VERSION, oldversion);
+
		pkg_emit_upgrade_begin(old);
	} else {
		pkg_emit_install_begin(newpkg);
	}
@@ -1135,7 +1140,7 @@ pkg_jobs_handle_install(struct pkg_solved *ps, struct pkg_jobs *j, bool handle_r
	}

	if (an != NULL) {
-
		pkgdb_add_annotation(j->db, p, "repository", pkg_annotation_value(an));
+
		pkgdb_add_annotation(j->db, new, "repository", pkg_annotation_value(an));
	}

	if (oldversion != NULL)
@@ -1171,6 +1176,12 @@ pkg_jobs_install(struct pkg_jobs *j)
	
	pkg_config_bool(PKG_CONFIG_HANDLE_RC_SCRIPTS, &handle_rc);

+
	/* XXX: get rid of hardcoded values */
+
	retcode = pkgdb_upgrade_lock(j->db, PKGDB_LOCK_ADVISORY,
+
			PKGDB_LOCK_EXCLUSIVE, 0.5, 20);
+
	if (retcode != EPKG_OK)
+
		goto cleanup;
+

	p = NULL;
	pkg_manifest_keys_new(&keys);
	/* Install */
@@ -1184,13 +1195,21 @@ pkg_jobs_install(struct pkg_jobs *j)
			goto cleanup;
	}
	DL_FOREACH(j->jobs_add, ps) {
-
		retcode = pkg_jobs_handle_install(ps, j, handle_rc, cachedir, keys);
+
		retcode = pkg_jobs_handle_install(ps->pkg[0], NULL,
+
				j, handle_rc, cachedir, keys);
+
		if (retcode != EPKG_OK)
+
			goto cleanup;
+
	}
+
	DL_FOREACH(j->jobs_upgrade, ps) {
+
		retcode = pkg_jobs_handle_install(ps->pkg[0], ps->pkg[1],
+
				j, handle_rc, cachedir, keys);
		if (retcode != EPKG_OK)
			goto cleanup;
	}

cleanup:
	pkgdb_transaction_commit(j->db->sqlite, "upgrade");
+
	pkgdb_release_lock(j->db, PKGDB_LOCK_EXCLUSIVE);
	pkg_manifest_keys_free(keys);

	return (retcode);
@@ -1214,6 +1233,10 @@ pkg_jobs_deinstall(struct pkg_jobs *j)
	if ((j->flags & PKG_FLAG_NOSCRIPT) == PKG_FLAG_NOSCRIPT)
		flags |= PKG_DELETE_NOSCRIPT;

+
	/* XXX: get rid of hardcoded values */
+
	retcode = pkgdb_upgrade_lock(j->db, PKGDB_LOCK_ADVISORY,
+
			PKGDB_LOCK_EXCLUSIVE, 0.5, 20);
+

	DL_FOREACH(j->jobs_delete, ps) {
		p = ps->pkg[0];
		pkg_get(p, PKG_NAME, &name);
@@ -1225,10 +1248,11 @@ pkg_jobs_deinstall(struct pkg_jobs *j)
		retcode = pkg_delete(p, j->db, flags);

		if (retcode != EPKG_OK)
-
			return (retcode);
+
			goto cleanup;
	}
-

-
	return (EPKG_OK);
+
cleanup:
+
	pkgdb_release_lock(j->db, PKGDB_LOCK_EXCLUSIVE);
+
	return (retcode);
}

int
@@ -1263,6 +1287,7 @@ pkg_jobs_apply(struct pkg_jobs *j)
				LL_FREE(j->jobs_upgrade, pkg_solved, free);
				j->jobs_add = j->jobs_delete = j->jobs_upgrade = NULL;
				j->count = 0;
+
				j->total = 0;

				rc = pkg_jobs_solve(j);
				if (rc == EPKG_OK) {
modified libpkg/pkg_manifest.c
@@ -752,7 +752,7 @@ pkg_parse_manifest_file(struct pkg *pkg, const char *file, struct pkg_manifest_k

	errno = 0;
	p = ucl_parser_new(0);
-
	if (ucl_parser_add_file(p, file)) {
+
	if (!ucl_parser_add_file(p, file)) {
		if (errno == ENOENT) {
			ucl_parser_free(p);
			return (EPKG_FATAL);
@@ -867,11 +867,11 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
	    PKG_VERSION, &version, PKG_REPOPATH, &repopath,
	    PKG_CKSUM, &pkgsum, PKG_PKGSIZE, &pkgsize);

-
	pkg_debug(1, "Emitting basic metadata");
+
	pkg_debug(4, "Emitting basic metadata");
	top = ucl_object_insert_key(top, ucl_object_fromstring(name), "name", 4, false);
	obj = ucl_object_insert_key(top, ucl_object_fromstring(pkgorigin), "origin", 6, false);
	obj = ucl_object_insert_key(top, ucl_object_fromstring(version), "version", 7, false);
-
	obj = ucl_object_insert_key(top, ucl_object_fromstring(comment), "comment", 7, false);
+
	obj = ucl_object_insert_key(top, ucl_object_fromstring_common(comment, 0, UCL_STRING_TRIM), "comment", 7, false);
	obj = ucl_object_insert_key(top, ucl_object_fromstring(pkgarch), "arch", 4, false);
	obj = ucl_object_insert_key(top, ucl_object_fromstring(pkgmaintainer), "maintainer", 10, false);
	obj = ucl_object_insert_key(top, ucl_object_fromstring(prefix), "prefix", 6, false);
@@ -891,7 +891,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
		break;
	}

-
	pkg_debug(1, "Emitting licenses");
+
	pkg_debug(4, "Emitting licenses");
	seq = NULL;
	while (pkg_licenses(pkg, &license) == EPKG_OK)
		seq = ucl_array_append(seq, ucl_object_fromstring(pkg_license_name(license)));
@@ -906,7 +906,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
	    ucl_object_fromstring_common(sbuf_data(tmpsbuf), sbuf_len(tmpsbuf), UCL_STRING_TRIM),
	    "desc", 4, false);

-
	pkg_debug(1, "Emitting deps");
+
	pkg_debug(4, "Emitting deps");
	map = NULL;
	while (pkg_deps(pkg, &dep) == EPKG_OK) {
		submap = NULL;
@@ -916,37 +916,37 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
	}
	obj = ucl_object_insert_key(top, map, "deps", 4, false);

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

-
	pkg_debug(1, "Emitting users");
+
	pkg_debug(4, "Emitting users");
	seq = NULL;
	while (pkg_users(pkg, &user) == EPKG_OK)
		seq = ucl_array_append(seq, ucl_object_fromstring(pkg_user_name(user)));
	obj = ucl_object_insert_key(top, seq, "users", 5, false);

-
	pkg_debug(1, "Emitting groups");
+
	pkg_debug(4, "Emitting groups");
	seq = NULL;
	while (pkg_groups(pkg, &group) == EPKG_OK) 
		seq = ucl_array_append(seq, ucl_object_fromstring(pkg_group_name(group)));
	obj = ucl_object_insert_key(top, seq, "groups", 6, false);

-
	pkg_debug(1, "Emitting required");
+
	pkg_debug(4, "Emitting required");
	seq = NULL;
	while (pkg_shlibs_required(pkg, &shlib) == EPKG_OK)
		seq = ucl_array_append(seq, ucl_object_fromstring(pkg_shlib_name(shlib)));
	obj = ucl_object_insert_key(top, seq, "shlibs_required", 15, false);

-
	pkg_debug(1, "Emitting shlibs_provided");
+
	pkg_debug(4, "Emitting shlibs_provided");
	seq = NULL;
	while (pkg_shlibs_provided(pkg, &shlib) == EPKG_OK)
		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");
+
	pkg_debug(4, "Emitting conflicts");
	map = NULL;
	while (pkg_conflicts(pkg, &conflict) == EPKG_OK)
		map = ucl_object_insert_key(map,
@@ -954,7 +954,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
		    pkg_conflict_origin(conflict), 0, false);
	obj = ucl_object_insert_key(top, map, "conflicts", 9, false);

-
	pkg_debug(1, "Emitting provides");
+
	pkg_debug(4, "Emitting provides");
	map = NULL;
	while (pkg_provides(pkg, &provide) == EPKG_OK)
		map = ucl_object_insert_key(map,
@@ -962,7 +962,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
		    pkg_provide_name(provide), 0, false);
	obj = ucl_object_insert_key(top, map, "provides", 8, false);

-
	pkg_debug(1, "Emitting options");
+
	pkg_debug(4, "Emitting options");
	map = NULL;
	while (pkg_options(pkg, &option) == EPKG_OK) {
		pkg_debug(2, "Emiting option: %s", pkg_option_value(option));
@@ -980,7 +980,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)

	if ((flags & PKG_MANIFEST_EMIT_COMPACT) == 0) {
		if ((flags & PKG_MANIFEST_EMIT_NOFILES) == 0) {
-
			pkg_debug(1, "Emitting files");
+
			pkg_debug(4, "Emitting files");
			map = NULL;
			while (pkg_files(pkg, &file) == EPKG_OK) {
				const char *pkg_sum = pkg_file_cksum(file);
@@ -995,7 +995,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
			}
			obj = ucl_object_insert_key(top, map, "files", 5, false);

-
			pkg_debug(1, "Emitting directories");
+
			pkg_debug(4, "Emitting directories");
			map = NULL;
			while (pkg_dirs(pkg, &dir) == EPKG_OK) {
				urlencode(pkg_dir_path(dir), &tmpsbuf);
@@ -1009,7 +1009,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
			obj = ucl_object_insert_key(top, map, "directories", 11, false);
		}

-
		pkg_debug(1, "Emitting scripts");
+
		pkg_debug(4, "Emitting scripts");
		map = NULL;
		for (i = 0; i < PKG_NUM_SCRIPTS; i++) {
			if (pkg_script_get(pkg, i) == NULL)
@@ -1053,7 +1053,7 @@ emit_manifest(struct pkg *pkg, struct sbuf **out, short flags)
		obj = ucl_object_insert_key(top, map, "scripts", 7, false);
	}

-
	pkg_debug(1, "Emitting message");
+
	pkg_debug(4, "Emitting message");
	if (message != NULL && *message != '\0') {
		urlencode(message, &tmpsbuf);
		obj = ucl_object_insert_key(top,
modified libpkg/pkg_ports.c
@@ -924,7 +924,7 @@ ports_parse_plist(struct pkg *pkg, const char *plist, const char *stage)
	buf = NULL;

	if ((plist_f = fopen(plist, "r")) == NULL) {
-
		pkg_emit_errno("Unable to open plist file: %s", plist);
+
		pkg_emit_error("Unable to open plist file: %s", plist);
		return (EPKG_FATAL);
	}

modified libpkg/pkg_solve.c
@@ -42,6 +42,8 @@
#include "private/pkg.h"
#include "private/pkgdb.h"

+
struct pkg_solve_item;
+

struct pkg_solve_variable {
	struct pkg *pkg;
	bool to_install;
@@ -49,12 +51,19 @@ struct pkg_solve_variable {
	const char *digest;
	const char *origin;
	bool resolved;
+
	struct _pkg_solve_var_rule {
+
		struct pkg_solve_item *rule;
+
		struct _pkg_solve_var_rule *next;
+
	} *rules;
+
	int nrules;
	UT_hash_handle hd;
	UT_hash_handle ho;
-
	struct pkg_solve_variable *next;
+
	struct pkg_solve_variable *next, *prev;
};

struct pkg_solve_item {
+
	int nitems;
+
	int nresolved;
	struct pkg_solve_variable *var;
	bool inverse;
	struct pkg_solve_item *next;
@@ -86,73 +95,83 @@ struct pkg_solve_problem {
 * @return true or false
 */
static bool
-
pkg_solve_check_rules(struct pkg_solve_rule *rules)
+
pkg_solve_check_rules(struct pkg_solve_problem *problem)
{
-
	struct pkg_solve_rule *cur;
-
	struct pkg_solve_item *it;
-
	bool ret;
-

-
	LL_FOREACH(rules, cur) {
+
	struct pkg_solve_variable *var, *tvar;

-
		ret = false;
-
		LL_FOREACH(cur->items, it) {
-
			if (it->var->resolved) {
-
				if (PKG_SOLVE_CHECK_ITEM(it))
-
					ret = true;
-
			}
+
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
+
		if (!var->resolved) {
+
			pkg_debug(2, "solver: var %s-%s is not still resolved",
+
					var->origin, var->digest);
+
			return false;
		}
-
		if (!ret)
-
			return (false);
-

	}

	return (true);
}

/**
+
 * Updates rules related to a single variable
+
 * @param var
+
 */
+
static void
+
pkg_solve_update_var_resolved (struct pkg_solve_variable *var)
+
{
+
	struct _pkg_solve_var_rule *rul;
+

+
	LL_FOREACH(var->rules, rul) {
+
		rul->rule->nresolved ++;
+
	}
+
}
+

+
/**
 * 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)
+
pkg_solve_propagate_units(struct pkg_solve_problem *problem)
{
-
	struct pkg_solve_rule *cur;
	struct pkg_solve_item *it, *unresolved = NULL;
-
	int resolved = 0, total = 0, solved_vars;
+
	int solved_vars;
+
	struct _pkg_solve_var_rule *rul;
+
	struct pkg_solve_variable *var, *tvar;
	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(%d) to %d",
-
							unresolved->var->origin, unresolved->var->priority, unresolved->var->to_install);
-
				}
-
				/* Now check for a conflict */
-
				ret = false;
-
				LL_FOREACH(cur->items, it) {
-
					if (PKG_SOLVE_CHECK_ITEM(it))
-
						ret = true;
+
		HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
+
check_again:
+
			LL_FOREACH(var->rules, rul) {
+
				unresolved = rul->rule;
+
				if (unresolved->nresolved == unresolved->nitems - 1) {
+
					/* Check for unit */
+
					ret = false;
+
					LL_FOREACH(unresolved, it) {
+
						if (it->var->resolved) {
+
							if (PKG_SOLVE_CHECK_ITEM(it))
+
								ret = true;
+
						}
+
					}
+
					if (!ret) {
+
						/* This is a unit */
+
						LL_FOREACH(unresolved, it) {
+
							if (!it->var->resolved) {
+
								it->var->to_install = (!it->inverse);
+
								it->var->resolved = true;
+
								pkg_solve_update_var_resolved(it->var);
+
								pkg_debug(2, "propagate %s-%s(%d) to %s",
+
										it->var->origin, it->var->digest,
+
										it->var->priority,
+
										it->var->to_install ? "install" : "delete");
+
								break;
+
							}
+
						}
+
						solved_vars ++;
+
						/* We want to try propagating all clauses for a single variable */
+
						goto check_again;
+
					}
				}
-
				/* A conflict found */
-
				if (!ret)
-
					return (false);
			}
		}
	} while (solved_vars > 0);
@@ -165,51 +184,56 @@ pkg_solve_propagate_units(struct pkg_solve_rule *rules)
 * Propagate pure clauses
 */
static bool
-
pkg_solve_propagate_pure(struct pkg_solve_rule *rules)
+
pkg_solve_propagate_pure(struct pkg_solve_problem *problem)
{
-
	struct pkg_solve_rule *cur;
+
	struct pkg_solve_variable *var, *tvar;
+
	struct _pkg_solve_var_rule *rul;
	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;
+
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
+
		LL_FOREACH(var->rules, rul) {
+
			it = rul->rule;
+
			if (it->nitems == 1 && it->nresolved == 0) {
+
				it->var->to_install = (!it->inverse);
+
				it->var->resolved = true;
+
				pkg_debug(2, "requested %s-%s(%d) to %s",
+
						it->var->origin, it->var->digest,
+
						it->var->priority, it->var->to_install ? "install" : "delete");
+
				pkg_solve_update_var_resolved(it->var);
+
			}
		}
	}

-
	return (false);
+
	return (true);
}

static bool
-
pkg_solve_check_conflicts(struct pkg_solve_rule *rules)
+
pkg_solve_check_conflicts(struct pkg_solve_variable *var, bool guess)
{
-
	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);
+
	struct pkg_solve_item *it;
+
	struct _pkg_solve_var_rule *rul;
+
	bool test = false;
+

+
	LL_FOREACH(var->rules, rul) {
+
		test = true;
+
		LL_FOREACH(rul->rule, it) {
+
			if (it->var->resolved) {
+
				test |= it->var->to_install ^ it->inverse;
			}
-
			else if (!it->var->resolved && next->var->resolved) {
-
				if (!PKG_SOLVE_CHECK_ITEM(next))
-
					return (false);
+
			else if (it->var == var) {
+
				test |= guess;
			}
		}
+
		if (!test) {
+
			/* We have a conflict */
+
			break;
+
		}
	}

-
	return (true);
+
	return (test);
}

+

/**
 * Use the default propagation policy:
 * - do not deinstall packages that are not in conflict
@@ -217,34 +241,32 @@ pkg_solve_check_conflicts(struct pkg_solve_rule *rules)
 *
 * This must be called after the explicit propagation
 */
-
static void
-
pkg_solve_propagate_default(struct pkg_solve_rule *rules)
+
static int
+
pkg_solve_propagate_default(struct pkg_solve_problem *problem)
{
-
	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(%d) to %d",
-
								it->var->origin, it->var->priority, 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(%d) to %d",
-
								it->var->origin, it->var->priority, it->var->to_install);
-
						it->var->resolved = true;
-
					}
+
	struct pkg_solve_variable *var, *tvar;
+
	bool guess;
+

+
	HASH_ITER(hd, problem->variables_by_digest, var, tvar) {
+
		if (!var->resolved) {
+
			/* Guess true for installed packages and false otherwise */
+
			guess = (var->pkg->type == PKG_INSTALLED);
+
			if (!pkg_solve_check_conflicts (var, guess)) {
+
				guess = !guess;
+
				if (!pkg_solve_check_conflicts (var, guess)) {
+
					return (EPKG_FATAL);
				}
			}
+
			var->to_install = guess;
+
			var->resolved = true;
+
			pkg_debug(2, "decided %s-%s(%d) to %s",
+
					var->origin, var->digest,
+
					var->priority, var->to_install ? "install" : "delete");
+
			pkg_solve_update_var_resolved(var);
		}
	}
+

+
	return (EPKG_OK);
}

/**
@@ -259,20 +281,23 @@ pkg_solve_sat_problem(struct pkg_solve_problem *problem)
{

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

-
	while (!pkg_solve_check_rules(problem->rules)) {
+
	while (!pkg_solve_check_rules(problem)) {
		/* 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)) {
+
		if (!pkg_solve_propagate_units(problem)) {
			pkg_emit_error("unimplemented: cannot solve SAT problem as units propagation has fallen");
			return (false);
		}
-
		pkg_solve_propagate_default(problem->rules);
+
		if (pkg_solve_propagate_default(problem) == EPKG_FATAL) {
+
			pkg_emit_error("unimplemented: cannot solve SAT problem as direct conflict has been found");
+
			return (false);
+
		}
	}

	return (true);
@@ -295,6 +320,7 @@ pkg_solve_item_new(struct pkg_solve_variable *var)
	}

	result->var = var;
+
	result->inverse = false;

	return (result);
}
@@ -333,6 +359,7 @@ pkg_solve_variable_new(struct pkg *pkg, int priority)
	/* XXX: Is it safe to save a ptr here ? */
	result->digest = digest;
	result->origin = origin;
+
	result->prev = result;

	return (result);
}
@@ -353,12 +380,16 @@ pkg_solve_problem_free(struct pkg_solve_problem *problem)
{
	struct pkg_solve_rule *r, *rtmp;
	struct pkg_solve_variable *v, *vtmp;
+
	struct _pkg_solve_var_rule *vrule, *vrtmp;

	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);
+
		LL_FOREACH_SAFE(v->rules, vrule, vrtmp) {
+
			free(vrule);
+
		}
		free(v);
	}
}
@@ -368,9 +399,10 @@ 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;
+
	struct pkg_solve_variable *nvar, *tvar = NULL, *found;
+
	const char *digest;

-
	HASH_FIND_STR(j->universe, __DECONST(char *, origin), unit);
+
	HASH_FIND_STR(j->universe, 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);
@@ -380,17 +412,26 @@ pkg_solve_add_universe_variable(struct pkg_jobs *j,
	nvar = pkg_solve_variable_new(unit->pkg, unit->priority);
	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);
+
	HASH_ADD_KEYPTR(ho, problem->variables_by_origin, nvar->origin, strlen(nvar->origin), nvar);
+
	pkg_debug(4, "solver: add variable from universe with origin %s", nvar->origin);
+

	unit = unit->next;
-
	tvar = nvar;
	while (unit != NULL) {
-
		/* Add all alternatives as independent variables */
-
		tvar->next = pkg_solve_variable_new(unit->pkg, unit->priority);
-
		tvar = tvar->next;
-
		if (tvar == NULL)
-
			return (EPKG_FATAL);
-
		HASH_ADD_KEYPTR(hd, problem->variables_by_digest, tvar->digest, strlen(tvar->digest), tvar);
+
		pkg_get(unit->pkg, PKG_DIGEST, &digest);
+
		HASH_FIND(hd, problem->variables_by_digest, digest, strlen(digest), found);
+
		if (found == NULL) {
+
			/* Add all alternatives as independent variables */
+
			tvar = pkg_solve_variable_new(unit->pkg, unit->priority);
+
			if (tvar == NULL)
+
				return (EPKG_FATAL);
+
			DL_APPEND(nvar, tvar);
+
			HASH_ADD_KEYPTR(hd, problem->variables_by_digest, tvar->digest,
+
					strlen(tvar->digest), tvar);
+
			pkg_debug (4, "solver: add another variable with origin %s and digest %s",
+
					tvar->origin, tvar->digest);
+
		}
		unit = unit->next;
	}

@@ -400,6 +441,36 @@ pkg_solve_add_universe_variable(struct pkg_jobs *j,
}

static int
+
pkg_solve_add_var_rules (struct pkg_solve_variable *var,
+
		struct pkg_solve_item *rule, int nrules, bool iterate_vars)
+
{
+
	struct _pkg_solve_var_rule *head = NULL;
+
	struct pkg_solve_variable *tvar;
+

+
	LL_FOREACH(var, tvar) {
+
		pkg_debug(4, "solver: add %d-ary clause to variable %s-%s: %d",
+
				nrules, tvar->origin, tvar->digest, rule->inverse);
+
		tvar->nrules += nrules;
+
		head = calloc(1, sizeof (struct _pkg_solve_var_rule));
+
		if (head == NULL) {
+
			pkg_emit_errno("calloc", "_pkg_solve_var_rule");
+
			return (EPKG_FATAL);
+
		}
+
		head->rule = rule;
+
		LL_PREPEND(tvar->rules, head);
+
		if (!iterate_vars)
+
			break;
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
#define RULE_ITEM_PREPEND(rule, item) do {									\
+
	(item)->nitems = (rule)->items ? (rule)->items->nitems + 1 : 1;			\
+
	LL_PREPEND((rule)->items, (item));										\
+
} while (0)
+

+
static int
pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
		struct pkg_solve_variable *pvar, bool conflicting)
{
@@ -409,6 +480,7 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
	struct pkg_solve_rule *rule;
	struct pkg_solve_item *it = NULL;
	struct pkg_solve_variable *var, *tvar, *cur_var;
+
	int cnt;

	const char *origin;

@@ -421,8 +493,7 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
			var = NULL;

			origin = pkg_dep_get(dep, PKG_DEP_ORIGIN);
-
			HASH_FIND(ho, problem->variables_by_origin,
-
					__DECONST(char *, origin), strlen(origin), var);
+
			HASH_FIND(ho, problem->variables_by_origin, origin, strlen(origin), var);
			if (var == NULL) {
				if (pkg_solve_add_universe_variable(j, problem, origin, &var) != EPKG_OK)
					goto err;
@@ -437,16 +508,20 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
				goto err;

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

				it->inverse = false;
-
				LL_PREPEND(rule->items, it);
+
				RULE_ITEM_PREPEND(rule, it);
+
				cnt ++;
			}
+
			pkg_solve_add_var_rules (var, rule->items, cnt, true);
+
			pkg_solve_add_var_rules (cur_var, rule->items, cnt, false);

			LL_PREPEND(problem->rules, rule);
			problem->rules_count ++;
@@ -459,8 +534,7 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
			var = NULL;

			origin = pkg_conflict_origin(conflict);
-
			HASH_FIND(ho, problem->variables_by_origin,
-
					__DECONST(char *, origin), strlen(origin), var);
+
			HASH_FIND(ho, problem->variables_by_origin, origin, strlen(origin), var);
			if (var == NULL) {
				if (pkg_solve_add_universe_variable(j, problem, origin, &var) != EPKG_OK)
					goto err;
@@ -477,17 +551,19 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
					goto err;

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

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

				LL_PREPEND(problem->rules, rule);
				problem->rules_count ++;
+
				pkg_solve_add_var_rules (tvar, rule->items, 2, false);
+
				pkg_solve_add_var_rules (cur_var, rule->items, 2, false);
			}
		}

@@ -510,17 +586,20 @@ pkg_solve_add_pkg_rule(struct pkg_jobs *j, struct pkg_solve_problem *problem,
						goto err;

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

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

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

+
					pkg_solve_add_var_rules (tvar, rule->items, 2, true);
+
					pkg_solve_add_var_rules (cur_var, rule->items, 2, false);
				}
			}
		}
@@ -568,8 +647,16 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
		if (var == NULL)
			goto err;

+
		pkg_debug(4, "solver: add variable from install request with origin %s-%s",
+
						var->origin, var->digest);
		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);
+
		HASH_FIND(ho, problem->variables_by_origin, var->origin, strlen(var->origin), tvar);
+
		if (tvar == NULL) {
+
			HASH_ADD_KEYPTR(ho, problem->variables_by_origin, var->origin, strlen(var->origin), var);
+
		}
+
		else {
+
			DL_APPEND(tvar, var);
+
		}
		it = pkg_solve_item_new(var);
		if (it == NULL)
			goto err;
@@ -579,7 +666,8 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
			goto err;

		/* Requests are unary rules */
-
		LL_PREPEND(rule->items, it);
+
		RULE_ITEM_PREPEND(rule, it);
+
		pkg_solve_add_var_rules (var, it, 1, false);
		LL_PREPEND(problem->rules, rule);
		problem->rules_count ++;
	}
@@ -595,8 +683,16 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
		if (var == NULL)
			goto err;

+
		pkg_debug(4, "solver: add variable from delete request with origin %s-%s",
+
				var->origin, var->digest);
		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);
+
		HASH_FIND(ho, problem->variables_by_origin, var->origin, strlen(var->origin), tvar);
+
		if (tvar == NULL) {
+
			HASH_ADD_KEYPTR(ho, problem->variables_by_origin, var->origin, strlen(var->origin), var);
+
		}
+
		else {
+
			DL_APPEND(tvar, var);
+
		}
		it = pkg_solve_item_new(var);
		if (it == NULL)
			goto err;
@@ -607,8 +703,9 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
			goto err;

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

@@ -633,12 +730,13 @@ pkg_solve_jobs_to_sat(struct pkg_jobs *j)
				/* Check origin */
				HASH_FIND(ho, problem->variables_by_origin, origin, strlen(origin), tvar);
				if (tvar == NULL) {
+
					pkg_debug(4, "solver: add variable from universe with origin %s", var->origin);
					HASH_ADD_KEYPTR(ho, problem->variables_by_origin,
							var->origin, strlen(var->origin), var);
				}
				else {
					/* Insert a variable to a chain */
-
					tvar->next = var;
+
					DL_APPEND(tvar, var);
				}
			}
		}
@@ -704,7 +802,7 @@ static void
pkg_solve_insert_res_job (struct pkg_solve_variable *var,
		struct pkg_solve_problem *problem, struct pkg_jobs *j)
{
-
	struct pkg_solved *res, *res_a, *res_d;
+
	struct pkg_solved *res;
	struct pkg_solve_variable *cur_var, *del_var = NULL, *add_var = NULL;
	int seen_add = 0, seen_del = 0;

@@ -713,13 +811,14 @@ pkg_solve_insert_res_job (struct pkg_solve_variable *var,
			add_var = cur_var;
			seen_add ++;
		}
-
		else if (!var->to_install && var->pkg->type == PKG_INSTALLED) {
+
		else if (!cur_var->to_install && cur_var->pkg->type == PKG_INSTALLED) {
			del_var = cur_var;
			seen_del ++;
		}
	}
	if (seen_add > 1 || seen_del > 1) {
-
		pkg_emit_error("internal solver error: more than two packages to install from the same origin");
+
		pkg_emit_error("internal solver error: more than two packages to install(%d) "
+
				"or delete(%d) from the same origin: %s", seen_add, seen_del, var->origin);
		return;
	}
	else if (seen_add != 0 || seen_del != 0) {
@@ -732,17 +831,24 @@ pkg_solve_insert_res_job (struct pkg_solve_variable *var,
			res->priority = del_var->priority;
			res->pkg[0] = del_var->pkg;
			DL_APPEND(j->jobs_delete, res);
+
			pkg_debug(3, "pkg_solve: schedule deletion of %s(%s)",
+
					del_var->origin, del_var->digest);
		}
		else if (seen_del == 0 && seen_add != 0) {
			res->priority = add_var->priority;
			res->pkg[0] = add_var->pkg;
			DL_APPEND(j->jobs_add, res);
+
			pkg_debug(3, "pkg_solve: schedule installation of %s(%s)",
+
					add_var->origin, add_var->digest);
		}
		else {
			res->priority = MAX(del_var->priority, add_var->priority);
			res->pkg[0] = add_var->pkg;
			res->pkg[1] = del_var->pkg;
			DL_APPEND(j->jobs_upgrade, res);
+
			pkg_debug(3, "pkg_solve: schedule upgrade of %s from %s to %s",
+
					del_var->origin, del_var->digest, add_var->digest);
+
#if 0
			/* Need some more tasks */
			res_a = calloc(1, sizeof(struct pkg_solved));
			res_d = calloc(1, sizeof(struct pkg_solved));
@@ -750,12 +856,14 @@ pkg_solve_insert_res_job (struct pkg_solve_variable *var,
				pkg_emit_errno("calloc", "pkg_solved");
				return;
			}
-
			res_a->priority = add_var->priority;
+
			/* Priority for individual tasks are the same as for the upgrade task */
+
			res_a->priority = res->priority;
			res_a->pkg[0] = add_var->pkg;
-
			DL_APPEND(j->jobs_add, res);
-
			res_d->priority = del_var->priority;
+
			DL_APPEND(j->jobs_add, res_a);
+
			res_d->priority = res->priority;
			res_d->pkg[0] = del_var->pkg;
-
			DL_APPEND(j->jobs_delete, res);
+
			DL_APPEND(j->jobs_delete, res_d);
+
#endif
		}
		j->count ++;
	}
@@ -769,13 +877,12 @@ 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_origin, var, vtmp) {
+
	HASH_ITER(ho, problem->variables_by_origin, var, vtmp) {
		if (!var->resolved)
			return (EPKG_FATAL);

-
		pkg_get(var->pkg, PKG_ORIGIN, &origin);
+
		pkg_debug(4, "solver: check variable with origin %s", var->origin);
		pkg_solve_insert_res_job(var, problem, j);
	}

modified libpkg/pkgdb.c
@@ -44,6 +44,7 @@
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
+
#include <signal.h>

#include <sqlite3.h>

@@ -2205,7 +2206,7 @@ pkgdb_load_options(struct pkgdb *db, struct pkg *pkg)
			break;
		}

-
		pkg_debug(1, "Pkgdb> adding option");
+
		pkg_debug(4, "Pkgdb> adding option");
		ret = load_tag_val(db->sqlite, pkg, sql, PKG_LOAD_OPTIONS,
				   pkg_addtagval, PKG_OPTIONS);
		if (ret != EPKG_OK)
@@ -4160,37 +4161,271 @@ pkgshell_open(const char **reponame)
	*reponame = strdup(localpath);
}

+
static int
+
pkgdb_write_lock_pid(struct pkgdb *db)
+
{
+
	const char lock_pid_sql[] = ""
+
			"INSERT INTO pkg_lock_pid VALUES (?1);";
+
	sqlite3_stmt	*stmt = NULL;
+
	int ret;
+

+
	ret = sqlite3_prepare_v2(db->sqlite, lock_pid_sql, -1, &stmt, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (EPKG_FATAL);
+
	}
+
	sqlite3_bind_int64(stmt, 1, (int64_t)getpid());
+

+
	if (sqlite3_step(stmt) != SQLITE_DONE) {
+
		ERROR_SQLITE(db->sqlite);
+
		sqlite3_finalize(stmt);
+
		return (EPKG_FATAL);
+
	}
+
	sqlite3_finalize(stmt);
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkgdb_remove_lock_pid(struct pkgdb *db, int64_t pid)
+
{
+
	const char lock_pid_sql[] = ""
+
			"DELETE FROM pkg_lock_pid WHERE pid = ?1;";
+
	sqlite3_stmt	*stmt = NULL;
+
	int ret;
+

+
	ret = sqlite3_prepare_v2(db->sqlite, lock_pid_sql, -1, &stmt, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (EPKG_FATAL);
+
	}
+
	sqlite3_bind_int64(stmt, 1, pid);
+

+
	if (sqlite3_step(stmt) != SQLITE_DONE) {
+
		ERROR_SQLITE(db->sqlite);
+
		sqlite3_finalize(stmt);
+
		return (EPKG_FATAL);
+
	}
+
	sqlite3_finalize(stmt);
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkgdb_check_lock_pid(struct pkgdb *db)
+
{
+
	sqlite3_stmt	*stmt = NULL;
+
	int ret, found = 0;
+
	int64_t pid, lpid;
+

+
	ret = sqlite3_prepare_v2(db->sqlite, "SELECT pid FROM pkg_lock_pid;", -1,
+
			&stmt, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (EPKG_FATAL);
+
	}
+

+
	lpid = getpid();
+

+
	while (sqlite3_step(stmt) != SQLITE_DONE) {
+
		pid = sqlite3_column_int64(stmt, 0);
+
		if (pid != lpid && kill((pid_t)pid, 0) == -1) {
+
			pkg_debug(1, "found stale pid %lld in lock database", pid);
+
			if (pkgdb_remove_lock_pid(db, pid) != EPKG_OK){
+
				sqlite3_finalize(stmt);
+
				return (EPKG_FATAL);
+
			}
+
		}
+
		else {
+
			found ++;
+
		}
+
	}
+

+
	if (found == 0)
+
		return (EPKG_END);
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
pkgdb_reset_lock(struct pkgdb *db)
+
{
+
	const char init_sql[] = ""
+
		"UPDATE pkg_lock SET exclusive=0, advisory=0, read=0;";
+
	int ret;
+

+
	ret = sqlite3_exec(db->sqlite, init_sql, NULL, NULL, NULL);
+

+
	if (ret == SQLITE_OK)
+
		return (EPKG_OK);
+

+
	return (EPKG_FATAL);
+
}
+

+
static int
+
pkgdb_try_lock(struct pkgdb *db, const char *lock_sql,
+
		double delay, unsigned int retries, pkgdb_lock_t type)
+
{
+
	unsigned int tries = 0;
+
	struct timespec ts;
+
	int ret = EPKG_END;
+

+
	while (tries <= retries) {
+
		ret = sqlite3_exec(db->sqlite, lock_sql, NULL, NULL, NULL);
+
		if (ret != SQLITE_OK) {
+
			if (ret == SQLITE_READONLY && type == PKGDB_LOCK_READONLY) {
+
				pkg_debug(1, "want read lock but cannot write to database, "
+
						"slightly ignore this error for now");
+
				return (EPKG_OK);
+
			}
+
			return (EPKG_FATAL);
+
		}
+

+
		ret = EPKG_END;
+
		if (sqlite3_changes(db->sqlite) == 0) {
+
			if (pkgdb_check_lock_pid(db) == EPKG_END) {
+
				/* No live processes found, so we can safely reset lock */
+
				pkg_debug(1, "no concurrent processes found, cleanup the lock");
+
				pkgdb_reset_lock(db);
+
				continue;
+
			}
+
			else if (delay > 0) {
+
				ts.tv_sec = (int)delay;
+
				ts.tv_nsec = (delay - (int)delay) * 1000000000.;
+
				pkg_debug(1, "waiting for database lock for %d times, "
+
						"next try in %.2f seconds", tries, delay);
+
				(void)nanosleep(&ts, NULL);
+
			}
+
			else {
+
				break;
+
			}
+
		}
+
		else {
+
			ret = pkgdb_write_lock_pid(db);
+
			break;
+
		}
+
		tries ++;
+
	}
+

+
	return (ret);
+
}
+

int
-
pkgdb_obtain_lock(struct pkgdb *db)
+
pkgdb_obtain_lock(struct pkgdb *db, pkgdb_lock_t type,
+
		double delay, unsigned int retries)
{
	int ret;
+
	const char table_sql[] = ""
+
			"CREATE TABLE pkg_lock "
+
			"(exclusive INTEGER(1), advisory INTEGER(1), read INTEGER(8));";
+
	const char pid_sql[] = ""
+
			"CREATE TABLE IF NOT EXISTS pkg_lock_pid "
+
			"(pid INTEGER PRIMARY KEY);";
+
	const char init_sql[] = ""
+
			"INSERT INTO pkg_lock VALUES(0,0,0);";
+
	const char readonly_lock_sql[] = ""
+
			"UPDATE pkg_lock SET read=read+1 WHERE exclusive=0;";
+
	const char advisory_lock_sql[] = ""
+
			"UPDATE pkg_lock SET advisory=1 WHERE exclusive=0 AND advisory=0;";
+
	const char exclusive_lock_sql[] = ""
+
			"UPDATE pkg_lock SET exclusive=1 WHERE exclusive=0 AND advisory=0 AND read=0;";
+
	const char *lock_sql = NULL;

	assert(db != NULL);
-
	assert(db->lock_count >= 0);
-
	if (!db->lock_count) {
-
		ret = sql_exec(db->sqlite,
-
		    "PRAGMA main.locking_mode=EXCLUSIVE;BEGIN IMMEDIATE;COMMIT;");
-
		/* Set lock only if we actually were able to switch locking mode */
-
		if (ret == EPKG_OK)
-
			++db->lock_count;
-
		return (ret);
+

+
	ret = sqlite3_exec(db->sqlite, table_sql, NULL, NULL, NULL);
+
	if (ret == SQLITE_OK) {
+
		/* Need to initialize */
+
		ret = sqlite3_exec(db->sqlite, init_sql, NULL, NULL, NULL);
+
		if (ret != SQLITE_OK) {
+
			ERROR_SQLITE(db->sqlite);
+
			return (EPKG_FATAL);
+
		}
	}
-
	else
-
		return (EPKG_OK);
+

+
	ret = sqlite3_exec(db->sqlite, pid_sql, NULL, NULL, NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (EPKG_FATAL);
+
	}
+

+
	switch (type) {
+
	case PKGDB_LOCK_READONLY:
+
		lock_sql = readonly_lock_sql;
+
		pkg_debug(1, "want to get a read only lock on a database");
+
		break;
+
	case PKGDB_LOCK_ADVISORY:
+
		lock_sql = advisory_lock_sql;
+
		pkg_debug(1, "want to get an advisory lock on a database");
+
		break;
+
	case PKGDB_LOCK_EXCLUSIVE:
+
		pkg_debug(1, "want to get an exclusive lock on a database");
+
		lock_sql = exclusive_lock_sql;
+
		break;
+
	}
+

+
	ret = pkgdb_try_lock(db, lock_sql, delay, retries, type);
+

+
	return (ret);
}

int
-
pkgdb_release_lock(struct pkgdb *db)
+
pkgdb_upgrade_lock(struct pkgdb *db, pkgdb_lock_t old_type, pkgdb_lock_t new_type,
+
		double delay, unsigned int retries)
{
+
	const char advisory_exclusive_lock_sql[] = ""
+
		"UPDATE pkg_lock SET exclusive=1,advisory=1 WHERE exclusive=0 AND advisory=1 AND read=0;";
+
	int ret = EPKG_FATAL;
+

	assert(db != NULL);
-
	assert(db->lock_count >= 0);
-
	if (db->lock_count > 0)
-
		db->lock_count--;
-
	if (db->lock_count == 0)
-
		return sql_exec(db->sqlite,
-
		    "PRAGMA main.locking_mode=NORMAL;BEGIN IMMEDIATE;COMMIT;");
-
	else
+

+
	if (old_type == PKGDB_LOCK_ADVISORY && new_type == PKGDB_LOCK_EXCLUSIVE) {
+
		pkg_debug(1, "want to upgrade advisory to exclusive lock");
+
		ret = pkgdb_try_lock(db, advisory_exclusive_lock_sql, delay, retries,
+
				new_type);
+
	}
+

+
	return (ret);
+
}
+

+
int
+
pkgdb_release_lock(struct pkgdb *db, pkgdb_lock_t type)
+
{
+
	const char readonly_unlock_sql[] = ""
+
			"UPDATE pkg_lock SET read=read-1 WHERE read>0;";
+
	const char advisory_unlock_sql[] = ""
+
			"UPDATE pkg_lock SET advisory=0 WHERE advisory=1;";
+
	const char exclusive_unlock_sql[] = ""
+
			"UPDATE pkg_lock SET exclusive=0 WHERE exclusive=1;";
+
	const char *unlock_sql = NULL;
+
	int ret = EPKG_FATAL;
+

+
	if (db == NULL)
		return (EPKG_OK);
+

+
	switch (type) {
+
	case PKGDB_LOCK_READONLY:
+
		unlock_sql = readonly_unlock_sql;
+
		pkg_debug(1, "release a read only lock on a database");
+
		break;
+
	case PKGDB_LOCK_ADVISORY:
+
		unlock_sql = advisory_unlock_sql;
+
		pkg_debug(1, "release an advisory lock on a database");
+
		break;
+
	case PKGDB_LOCK_EXCLUSIVE:
+
		pkg_debug(1, "release an exclusive lock on a database");
+
		unlock_sql = exclusive_unlock_sql;
+
		break;
+
	}
+

+
	ret = sqlite3_exec(db->sqlite, unlock_sql, NULL, NULL, NULL);
+
	if (ret != SQLITE_OK)
+
		return (EPKG_FATAL);
+

+
	if (sqlite3_changes(db->sqlite) == 0)
+
		return (EPKG_END);
+

+
	return pkgdb_remove_lock_pid(db, (int64_t)getpid());
}

int64_t
modified libpkg/private/pkg.h
@@ -219,7 +219,7 @@ struct pkg_job_universe_item {
	struct pkg *pkg;
	UT_hash_handle hh;
	int priority;
-
	struct pkg_job_universe_item *next;
+
	struct pkg_job_universe_item *next, *prev;
};

struct pkg_jobs {
@@ -235,6 +235,7 @@ struct pkg_jobs {
	pkg_flags	 flags;
	int		 solved;
	int count;
+
	int total;
	const char *	 reponame;
	struct job_pattern *patterns;
};
modified libpkg/private/pkgdb.h
@@ -65,9 +65,6 @@ int pkgdb_transaction_rollback(sqlite3 *sqlite, const char *savepoint);

struct pkgdb_it *pkgdb_it_new(struct pkgdb *db, sqlite3_stmt *s, int type, short flags);

-
int pkgdb_obtain_lock(struct pkgdb *db);
-
int pkgdb_release_lock(struct pkgdb *db);
-

void pkgshell_open(const char **r);

/**
modified pkg/Makefile
@@ -65,7 +65,6 @@ CFLAGS+= -I../libpkg \
		-I${.CURDIR}/../external/expat/lib
LDADD+=		-L${.OBJDIR}/../libpkg \
		-lpkg \
-
		-ledit \
		-larchive \
		-lutil \
		-lpthread \
modified pkg/add.c
@@ -79,7 +79,7 @@ exec_add(int argc, char **argv)
			f |= PKG_ADD_NOSCRIPT;
			break;
		case 'A':
-
			f |= PKG_FLAG_AUTOMATIC;
+
			f |= PKG_ADD_AUTOMATIC;
			break;
		case 'f':
			f |= PKG_FLAG_FORCE;
@@ -113,6 +113,12 @@ exec_add(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	failedpkgs = sbuf_new_auto();
	pkg_manifest_keys_new(&keys);
	for (i = 0; i < argc; i++) {
@@ -157,7 +163,7 @@ exec_add(int argc, char **argv)

	}
	pkg_manifest_keys_free(keys);
-

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);
	
	if(failedpkgcount > 0) {
modified pkg/annotate.c
@@ -283,8 +283,13 @@ exec_annotate(int argc, char **argv)

	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
	if (retcode != EPKG_OK) {
-
		exitcode = EX_IOERR;
-
		goto cleanup;
+
		return (EX_IOERR);
+
	}
+

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
	}

	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
@@ -324,8 +329,9 @@ cleanup:
		pkg_free(pkg);
	if (it != NULL)
		pkgdb_it_free(it);
-
	if (db != NULL)
-
		pkgdb_close(db);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
	pkgdb_close(db);
	if (input != NULL)
		sbuf_delete(input);

modified pkg/audit.c
@@ -809,6 +809,12 @@ exec_audit(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL)
	{
		warnx("Error accessing the package database");
@@ -839,6 +845,8 @@ exec_audit(int argc, char **argv)

cleanup:
	pkgdb_it_free(it);
+
	if (db != NULL)
+
		pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
	pkg_free(pkg);
	free_audit_list(h);
modified pkg/autoremove.c
@@ -101,6 +101,11 @@ exec_autoremove(__unused int argc, __unused char **argv)
		return (EX_IOERR);
	}

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
	/* Always force packages to be removed */
	if (pkg_jobs_new(&jobs, PKG_JOBS_AUTOREMOVE, db) != EPKG_OK) {
		pkgdb_close(db);
@@ -139,6 +144,7 @@ exec_autoremove(__unused int argc, __unused char **argv)

cleanup:
	pkg_jobs_free(jobs);
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkgdb_close(db);

	return (retcode);
modified pkg/check.c
@@ -339,11 +339,17 @@ exec_check(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	i = 0;
	do {
		if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
-
			pkgdb_close(db);
-
			return (EX_IOERR);
+
			rc = EX_IOERR;
+
			goto cleanup;
		}

		while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
@@ -364,18 +370,32 @@ exec_check(int argc, char **argv)
				}
			}
			if (recompute) {
-
				if (verbose)
-
					pkg_printf("Recomputing size and checksums: %n\n", pkg);
-
				if (pkg_recompute(db, pkg) != EPKG_OK) {
-
					rc = EX_DATAERR;
+
				if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
+
						PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
					if (verbose)
+
						pkg_printf("Recomputing size and checksums: %n\n", pkg);
+
					if (pkg_recompute(db, pkg) != EPKG_OK) {
+
						rc = EX_DATAERR;
+
					}
+
					pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
				}
+
				else {
+
					rc = EX_TEMPFAIL;
				}
			}
			if (reanalyse_shlibs) {
-
				if (verbose)
-
					pkg_printf("Reanalyzing files for shlibs: %n\n", pkg);
-
				if (pkgdb_reanalyse_shlibs(db, pkg) != EPKG_OK) {
-
					pkg_printf("Failed to reanalyse for shlibs: %n\n", pkg);
-
					rc = EX_UNAVAILABLE;
+
				if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
+
						PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
					if (verbose)
+
						pkg_printf("Reanalyzing files for shlibs: %n\n", pkg);
+
					if (pkgdb_reanalyse_shlibs(db, pkg) != EPKG_OK) {
+
						pkg_printf("Failed to reanalyse for shlibs: %n\n", pkg);
+
						rc = EX_UNAVAILABLE;
+
					}
+
					pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
				}
+
				else {
+
					rc = EX_TEMPFAIL;
				}
			}
		}
@@ -383,21 +403,32 @@ exec_check(int argc, char **argv)
		if (dcheck && nbpkgs > 0 && !noinstall) {
			printf("\n>>> Missing package dependencies were detected.\n");
			printf(">>> Found %d issue(s) in the package database.\n\n", nbpkgs);
-

-
			ret = fix_deps(db, &dh, nbpkgs, yes);
-
			if (ret == EPKG_OK)
-
				check_summary(db, &dh);
-
			else if (ret == EPKG_ENODB) {
-
				db = NULL;
-
				return (EX_IOERR);
+
			if (pkgdb_upgrade_lock(db, PKGDB_LOCK_ADVISORY,
+
					PKGDB_LOCK_EXCLUSIVE, 0.5, 20) == EPKG_OK) {
+
				ret = fix_deps(db, &dh, nbpkgs, yes);
+
				if (ret == EPKG_OK)
+
					check_summary(db, &dh);
+
				else if (ret == EPKG_ENODB) {
+
					db = NULL;
+
					rc = EX_IOERR;
+
				}
+
				pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
				if (rc == EX_IOERR)
+
					goto cleanup;
+
			}
+
			else {
+
				rc = EX_TEMPFAIL;
+
				goto cleanup;
			}
		}
		pkgdb_it_free(it);
		i++;
	} while (i < argc);

+
cleanup:
	deps_free(&dh);
	pkg_free(pkg);
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkgdb_close(db);

	return (rc);
modified pkg/clean.c
@@ -280,8 +280,13 @@ exec_clean(int argc, char **argv)

	retcode = EX_SOFTWARE;

-
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) {
-
		goto cleanup;
+
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
+
		return (EX_IOERR);
+

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
	}

	if ((fts = fts_open(paths, FTS_PHYSICAL, NULL)) == NULL) {
@@ -416,8 +421,9 @@ cleanup:
	pkg_free(p);
	if (fts != NULL)
		fts_close(fts);
-
	if (db != NULL)
-
		pkgdb_close(db);
+

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

	return (retcode);
}
modified pkg/convert.c
@@ -69,9 +69,13 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
	if (mkdir(pkg_add_dbdir, 0755) != 0 && errno != EEXIST)
		err(EX_CANTCREAT, "%s", pkg_add_dbdir);

-
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
-
		pkgdb_close(db);
+
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);
+

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
	}

	if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
@@ -218,6 +222,7 @@ convert_to_old(const char *pkg_add_dbdir, bool dry_run)
cleanup:
	pkg_free(pkg);
	pkgdb_it_free(it);
+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);

	return (ret);
@@ -231,6 +236,7 @@ convert_from_old(const char *pkg_add_dbdir, bool dry_run)
	struct pkg *p = NULL;
	char path[MAXPATHLEN];
	struct pkgdb *db = NULL;
+
	struct stat sb;

	if ((d = opendir(pkg_add_dbdir)) == NULL)
		err(EX_NOINPUT, "%s", pkg_add_dbdir);
@@ -239,7 +245,8 @@ convert_from_old(const char *pkg_add_dbdir, bool dry_run)
		return (EX_IOERR);
	}
	while ((dp = readdir(d)) != NULL) {
-
		if (dp->d_type == DT_DIR) {
+
		if (fstatat(dirfd(d), dp->d_name, &sb, 0) == 0 &&
+
		    S_ISDIR(sb.st_mode)) {
			if (strcmp(dp->d_name, ".") == 0 ||
			    strcmp(dp->d_name, "..") == 0)
				continue;
modified pkg/create.c
@@ -86,6 +86,12 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
		pkgdb_close(db);
		return (EX_IOERR);
	}
+
	/* XXX: get rid of hardcoded timeouts */
+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0.5, 20) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}

	switch (fmt) {
	case TXZ:
@@ -154,6 +160,7 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt,
	}

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

	return (retcode);
modified pkg/delete.c
@@ -130,6 +130,13 @@ exec_delete(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

+

	if (pkg_jobs_new(&jobs, PKG_JOBS_DEINSTALL, db) != EPKG_OK) {
		pkgdb_close(db);
		return (EX_IOERR);
@@ -167,7 +174,9 @@ exec_delete(int argc, char **argv)

	if (!quiet || dry_run) {
		print_jobs_summary(jobs,
-
		    "Deinstallation has been requested for the following %d packages:\n\n", nbactions);
+
		    "Deinstallation has been requested for the following %d packages "
+
		    "(of %d packages in the universe):\n\n", nbactions,
+
		    pkg_jobs_total(jobs));
		if (dry_run) {
			retcode = EX_OK;
			goto cleanup;
@@ -184,6 +193,7 @@ exec_delete(int argc, char **argv)
	retcode = EX_OK;

cleanup:
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkg_jobs_free(jobs);
	pkgdb_close(db);

modified pkg/fetch.c
@@ -151,6 +151,13 @@ exec_fetch(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

+

	if (pkg_jobs_new(&jobs, PKG_JOBS_FETCH, db) != EPKG_OK)
		goto cleanup;

@@ -183,6 +190,7 @@ exec_fetch(int argc, char **argv)

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

	return (retcode);
modified pkg/info.c
@@ -234,6 +234,12 @@ exec_info(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	i = 0;
	do {
		gotone = false;
@@ -327,7 +333,7 @@ exec_info(int argc, char **argv)
		}

		if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
-
			return (EX_IOERR);
+
			goto cleanup;
		}

		/* this is place for compatibility hacks */
@@ -423,7 +429,11 @@ exec_info(int argc, char **argv)
		i++;
	} while (i < argc);

-
	pkg_free(pkg);
+
cleanup:
+
	if (pkg != NULL)
+
		pkg_free(pkg);
+

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

	return (retcode);
modified pkg/install.c
@@ -158,6 +158,12 @@ exec_install(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if (pkg_jobs_new(&jobs, PKG_JOBS_INSTALL, db) != EPKG_OK)
		goto cleanup;

@@ -177,8 +183,8 @@ exec_install(int argc, char **argv)
		yes = yes_arg;
		if (!quiet || dry_run) {
			print_jobs_summary(jobs,
-
			    "The following %d packages will be installed:\n\n",
-
			    nbactions);
+
			    "The following %d packages will be installed (of %d in the universe):\n\n",
+
			    nbactions, pkg_jobs_total(jobs));

			if (!yes && !dry_run)
				yes = query_yesno(
@@ -206,6 +212,7 @@ exec_install(int argc, char **argv)
	retcode = EX_OK;

cleanup:
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkg_jobs_free(jobs);
	pkgdb_close(db);

modified pkg/lock.c
@@ -178,6 +178,12 @@ exec_lock_unlock(int argc, char **argv, enum action action)
	if (retcode != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
		exitcode = EX_IOERR;
		goto cleanup;
@@ -202,8 +208,9 @@ cleanup:
		pkg_free(pkg);
	if (it != NULL)
		pkgdb_it_free(it);
-
	if (db != NULL)
-
		pkgdb_close(db);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
+
	pkgdb_close(db);

	return (exitcode);
}
modified pkg/main.c
@@ -35,7 +35,6 @@

#include <assert.h>
#include <err.h>
-
#include <histedit.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
@@ -155,9 +154,9 @@ usage(const char *conffile, const char *reposdir, FILE *out, enum pkg_usage_reas
	}

#ifndef NO_LIBJAIL
-
 	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");
+
 	fprintf(out, "Usage: pkg [-v] [-d] [-l] [-N] [-j <jail name or id>|-c <chroot path>] [-C <configuration file>] [-R <repo config dir>] [-o var=value] <command> [<args>]\n\n");
#else
-
	fprintf(out, "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>] [-o var=value] <command> [<args>]\n\n");
#endif
	if (reason == PKG_USAGE_HELP) {
		fprintf(out, "Global options supported:\n");
@@ -171,6 +170,7 @@ usage(const char *conffile, const char *reposdir, FILE *out, enum pkg_usage_reas
		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, "\t%-15s%s\n\n", "-o", "Override configuration option from the command line");
		fprintf(out, "Commands supported:\n");

		for (i = 0; i < cmd_len; i++)
@@ -545,6 +545,28 @@ do_activation_test(int argc)
	return;
}

+
static void
+
export_arg_option (char *arg)
+
{
+
	char *eqp;
+
	const char *opt;
+

+
	if ((eqp = strchr(arg, '=')) != NULL) {
+
		*eqp = '\0';
+

+
		if ((opt = getenv (arg)) != NULL) {
+
			warnx("option %s is defined in the environment to '%s' but command line "
+
					"option redefines it", arg, opt);
+
			setenv(arg, eqp + 1, 1);
+
		}
+
		else {
+
			setenv(arg, eqp + 1, 0);
+
		}
+

+
		*eqp = '=';
+
	}
+
}
+

int
main(int argc, char **argv)
{
@@ -570,24 +592,24 @@ main(int argc, char **argv)
	const char *reposdir = NULL;
	struct pkg_config_kv *alias = NULL;
	const char *alias_value;
-
	char **newargv;
-
	int newargc;
-
	Tokenizer *t = NULL;
-
	struct sbuf *newcmd;
+
	char **newargv, *arg, *args;
+
	int newargc, newargvl;
+
	struct sbuf *newcmd = NULL;
	int j;

	/* Set stdout unbuffered */
	setvbuf(stdout, NULL, _IONBF, 0);

	cmdargv = argv;
+
	newargvl = 0;

	if (argc < 2)
		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) {
+
	while ((ch = getopt(argc, argv, "dj:c:C:R:lNvqo:")) != -1) {
#else
-
	while ((ch = getopt(argc, argv, "d:c:C:R:lNvq")) != -1) {
+
	while ((ch = getopt(argc, argv, "d:c:C:R:lNvqo:")) != -1) {
#endif
		switch (ch) {
		case 'd':
@@ -616,6 +638,9 @@ main(int argc, char **argv)
		case 'v':
			version++;
			break;
+
		case 'o':
+
			export_arg_option (optarg);
+
			break;
		default:
			break;
		}
@@ -752,12 +777,20 @@ main(int argc, char **argv)
				else
					sbuf_printf(newcmd, " %s ", argv[j]);
			}
+
			newargv = NULL;
+
			newargc = 0;
			sbuf_done(newcmd);
-
			t = tok_init(NULL);
-
			/* XXX: __DECONST() workaround gcc's -Werror=cast-qual. */
-
			if (tok_str(t, sbuf_data(newcmd), &newargc, __DECONST(const char ***, &newargv)) != 0)
-
				errx(EX_CONFIG, "Invalid alias: %s", alias_value);
-
			sbuf_delete(newcmd);
+
			args = sbuf_data(newcmd);
+
			while ((arg = strsep(&args, "\t \n")) != NULL) {
+
				if (*arg == '\0')
+
					continue;
+
				if (newargc > newargvl -2) {
+
					newargvl += 1024;
+
					newargv = reallocf(newargv, newargvl * sizeof(char *));
+
				}
+
				newargv[newargc++] = arg;
+
			}
+
			newargv[newargc+1] = NULL;
			break;
		}
	}
@@ -808,8 +841,10 @@ main(int argc, char **argv)
		usage(conffile, reposdir, stderr, PKG_USAGE_UNKNOWN_COMMAND, newargv[0]);
	}

-
	if (alias != NULL)
-
		tok_end(t);
+
	if (newcmd != NULL) {
+
		free(newargv);
+
		sbuf_delete(newcmd);
+
	}

	if (ret == EX_OK && newpkgversion) {
		if (jail_str == NULL && chroot_path == NULL)
modified pkg/pkg-repo.8
@@ -104,7 +104,9 @@ function: sha256
fingerprint: sha256_representation_of_the_public_key
.Ed
.Pp
-
See EXAMPLES section and
+
See the
+
.Sx EXAMPLES
+
section and
.Xr pkg.conf 5
for more information.
.Pp
modified pkg/pkg.8
@@ -15,7 +15,7 @@
.\"     @(#)pkg.8
.\" $FreeBSD$
.\"
-
.Dd February 1, 2014
+
.Dd February 26, 2014
.Dt PKG 8
.Os
.\" ---------------------------------------------------------------------------
@@ -67,6 +67,11 @@ option takes precedence over
but
.Fl l
will override any other command line arguments.
+
.It Fl o Ao option=value Ac
+
Set configuration option for
+
.Nm
+
from the command line. Options that are set from the environment are
+
redefined. It is permitted to specify this option multiple times. 
.It Fl N
Activation status check mode.
Prevent
modified pkg/query.c
@@ -882,12 +882,14 @@ exec_query(int argc, char **argv)
	/* Default to all packages if no pkg provided */
	if (argc == 1 && pkgname == NULL && condition == NULL && match == MATCH_EXACT) {
		match = MATCH_ALL;
-
	} else if ((argc == 1) ^ (match == MATCH_ALL) && pkgname == NULL && condition == NULL) {
+
	} else if (((argc == 1) ^ (match == MATCH_ALL)) && pkgname == NULL
+
			&& condition == NULL) {
		usage_query();
		return (EX_USAGE);
	}

-
	if (analyse_query_string(argv[0], accepted_query_flags, q_flags_len, &query_flags, &multiline) != EPKG_OK)
+
	if (analyse_query_string(argv[0], accepted_query_flags, q_flags_len,
+
			&query_flags, &multiline) != EPKG_OK)
		return (EX_USAGE);

	if (pkgname != NULL) {
@@ -926,6 +928,12 @@ exec_query(int argc, char **argv)
	if (ret != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if (match == MATCH_ALL || match == MATCH_CONDITION) {
		const char *condition_sql = NULL;
		if (match == MATCH_CONDITION && sqlcond)
@@ -945,8 +953,10 @@ exec_query(int argc, char **argv)
		for (i = 1; i < argc; i++) {
			pkgname = argv[i];

-
			if ((it = pkgdb_query(db, pkgname, match)) == NULL)
-
				return (EX_IOERR);
+
			if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
+
				retcode = EX_IOERR;
+
				goto cleanup;
+
			}

			while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
				nprinted++;
@@ -967,7 +977,11 @@ exec_query(int argc, char **argv)
		}
	}

-
	pkg_free(pkg);
+
cleanup:
+
	if (pkg != NULL)
+
		pkg_free(pkg);
+

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

	return (retcode);
modified pkg/register.c
@@ -162,7 +162,7 @@ exec_register(int argc, char **argv)
	}

	/*
-
         * Ideally, the +MANIFEST should be all that is necessary,
+
	 * Ideally, the +MANIFEST should be all that is necessary,
	 * since it can contain all of the meta-data supplied by the
	 * other files mentioned below.  These are here for backwards
	 * compatibility with the way the ports tree works with
@@ -273,12 +273,19 @@ exec_register(int argc, char **argv)
	}


-
	if (!old && pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
-
		return (EX_IOERR);
+
	if (!old) {
+
		if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
+
			return (EX_IOERR);
+

+
		if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
			pkgdb_close(db);
+
			warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
			return (EX_TEMPFAIL);
+
		}
	}

	/*
-
         * testing_mode allows updating the local package database
+
	 * testing_mode allows updating the local package database
	 * without any check that the files etc. listed in the meta
	 * data actually exist on the system.  Inappropriate use of
	 * testing_mode can really screw things up.
@@ -316,8 +323,10 @@ exec_register(int argc, char **argv)
	if (!legacy && pkg_has_message(pkg))
		pkg_printf("%M\n", pkg);

-
	if (!old)
+
	if (!old) {
+
		pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
		pkgdb_close(db);
+
	}

	pkg_free(pkg);

modified pkg/set.c
@@ -147,12 +147,19 @@ exec_set(int argc, char **argv)

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);
+

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

 
	if (oldorigin != NULL) {
		match = MATCH_ALL;
		if ((it = pkgdb_query(db, oldorigin, MATCH_EXACT)) == NULL) {
-
			pkgdb_close(db);
-
			return (EX_IOERR);
+
			retcode = EX_IOERR;
+
			goto cleanup;
		}

		if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) {
@@ -173,8 +180,10 @@ exec_set(int argc, char **argv)
				    "[y/N]: ", oldorigin, neworigin);
		}
		if (pkg != NULL && yes) {
-
			if (pkgdb_set(db, pkg, PKG_SET_ORIGIN, neworigin) != EPKG_OK)
-
				return (EX_IOERR);
+
			if (pkgdb_set(db, pkg, PKG_SET_ORIGIN, neworigin) != EPKG_OK) {
+
				retcode = EX_IOERR;
+
				goto cleanup;
+
			}
		}
		pkgdb_it_free(it);
	}
@@ -183,9 +192,8 @@ exec_set(int argc, char **argv)
		bool save_yes = yes;

		if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
-
			free(oldorigin);
-
			pkgdb_close(db);
-
			return (EX_IOERR);
+
			retcode = EX_IOERR;
+
			goto cleanup;
		}

		while (pkgdb_it_next(it, &pkg, loads) == EPKG_OK) {
@@ -210,8 +218,10 @@ exec_set(int argc, char **argv)
					 * Do not query user when he has already
					 * been queried.
					 */
-
					if (pkgdb_set(db, pkg, PKG_SET_DEPORIGIN, oldorigin, neworigin) != EPKG_OK)
-
						return (EX_IOERR);
+
					if (pkgdb_set(db, pkg, PKG_SET_DEPORIGIN, oldorigin, neworigin) != EPKG_OK) {
+
						retcode = EX_IOERR;
+
						goto cleanup;
+
					}
				}
			}
		}
@@ -219,9 +229,15 @@ exec_set(int argc, char **argv)
		i++;
	} while (i < argc);

-
	free(oldorigin);
-
	pkg_free(pkg);
+
cleanup:
+
	if (oldorigin)
+
		free(oldorigin);
+

+
	if (pkg != NULL)
+
		pkg_free(pkg);
+

+
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);

-
	return (EX_OK);
+
	return (retcode);
}
modified pkg/shell.c
@@ -42,6 +42,7 @@ usage_shell(void)
int
exec_shell(int argc, char **argv)
{
+
	/* XXX: need exclusive lock here */
	pkgdb_cmd(argc, argv);
	return (EX_OK);
}
modified pkg/shlib.c
@@ -174,6 +174,14 @@ exec_shlib(int argc, char **argv)
	}

	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
+
	if (retcode != EPKG_OK)
+
		return (EX_IOERR);
+

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}

	if (retcode == EPKG_OK && !requires_only)
		retcode = pkgs_providing_lib(db, libname);
@@ -184,6 +192,7 @@ exec_shlib(int argc, char **argv)
	if (retcode != EPKG_OK)
		retcode = (EX_IOERR);
		
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
	return (retcode);
}
modified pkg/stats.c
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <inttypes.h>
#include <libutil.h>
+
#include <err.h>

#include <pkg.h>

@@ -81,6 +82,12 @@ exec_stats(__unused int argc, __unused char **argv)
		return (EX_IOERR);
	}

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if (opt & STATS_LOCAL) {
		printf("Local package database:\n");
		printf("\tInstalled packages: %" PRId64 "\n", pkgdb_stats(db, PKG_STATS_LOCAL_COUNT));
@@ -111,6 +118,7 @@ exec_stats(__unused int argc, __unused char **argv)
		}
	}

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

	return (EX_OK);
modified pkg/updating.c
@@ -69,6 +69,7 @@ exec_updating(int argc, char **argv)
	struct pkgdb_it *it = NULL;
	const char *origin;
	FILE *fd;
+
	int retcode = EXIT_SUCCESS;

	while ((ch = getopt(argc, argv, "d:f:")) != -1) {
		switch (ch) {
@@ -94,10 +95,18 @@ exec_updating(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	SLIST_INIT(&origins);
	if (argc == 0) {
-
		if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL)
+
		if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
+
			retcode = EX_UNAVAILABLE;
			goto cleanup;
+
		}

		while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
			port = malloc(sizeof(struct installed_ports));
@@ -117,15 +126,17 @@ exec_updating(int argc, char **argv)
	if (updatingfile == NULL) {
		const char *portsdir;
		if (pkg_config_string(PKG_CONFIG_PORTSDIR, &portsdir) != EPKG_OK) {
-
			warnx("Cannot get portsdir config entry!");
-
			return (1);
+
			retcode = EX_CONFIG;
+
			goto cleanup;
		}
		asprintf(&updatingfile, "%s/UPDATING", portsdir);
	}

	fd = fopen(updatingfile, "r");
-
	if (fd == NULL)
-
		errx(EX_UNAVAILABLE, "Unable to open: %s", updatingfile);
+
	if (fd == NULL) {
+
		warnx("Unable to open: %s", updatingfile);
+
		goto cleanup;
+
	}

	while ((linelen = getline(&line, &linecap, fd)) > 0) {
		if (strspn(line, "0123456789:") == 9) {
@@ -156,10 +167,12 @@ exec_updating(int argc, char **argv)
		}
	}
	fclose(fd);
+

cleanup:
	pkgdb_it_free(it);
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
	pkg_free(pkg);

-
	return (EXIT_SUCCESS);
+
	return (retcode);
}
modified pkg/upgrade.c
@@ -132,6 +132,12 @@ exec_upgrade(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

	if (pkg_jobs_new(&jobs, PKG_JOBS_UPGRADE, db) != EPKG_OK)
		goto cleanup;

@@ -153,7 +159,7 @@ exec_upgrade(int argc, char **argv)
	if (!quiet || dry_run) {
		print_jobs_summary(jobs,
		    "Upgrades have been requested for the following %d "
-
		    "packages:\n\n", nbactions);
+
		    "packages (%d packages in the universe):\n\n", nbactions, pkg_jobs_total(jobs));

		if (!yes && !dry_run)
			yes = query_yesno("\nProceed with upgrading "
@@ -172,8 +178,9 @@ exec_upgrade(int argc, char **argv)

	retcode = EXIT_SUCCESS;

-
	cleanup:
+
cleanup:
	pkg_jobs_free(jobs);
+
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
	pkgdb_close(db);

	if (!yes && newpkgversion)
modified pkg/utils.c
@@ -528,7 +528,8 @@ print_info(struct pkg * const pkg, uint64_t options)
}

static void
-
print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,
+
print_jobs_summary_pkg(struct pkg *new_pkg, struct pkg *old_pkg,
+
		pkg_jobs_t type, int64_t *oldsize,
		int64_t *newsize, int64_t *dlsize)
{
	const char *oldversion, *cachedir, *why;
@@ -541,27 +542,27 @@ print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,
	oldversion = NULL;

	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);
+
	pkg_get(new_pkg, PKG_FLATSIZE, &flatsize, PKG_PKGSIZE, &pkgsize, PKG_REASON, &why);
+
	if (old_pkg != NULL)
+
		pkg_get(old_pkg, PKG_VERSION, &oldversion, PKG_FLATSIZE, &oldflatsize);

-
	if (pkg_is_locked(pkg)) {
-
		pkg_printf("\tPackage %n-%v is locked ", pkg, pkg);
+
	if (old_pkg != NULL && pkg_is_locked(old_pkg)) {
+
		pkg_printf("\tPackage %n-%v is locked ", old_pkg, old_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)) {
+
				switch(pkg_version_change(new_pkg)) {
				case PKG_UPGRADE:
-
					pkg_printf("and may not be upgraded to version %v\n", pkg);
+
					pkg_printf("and may not be upgraded to version %v\n", new_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);
+
					pkg_printf("and may not be downgraded to version %v\n", new_pkg);
					break;
				}
				return;
@@ -582,7 +583,7 @@ print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,
	switch (type) {
	case PKG_JOBS_INSTALL:
	case PKG_JOBS_UPGRADE:
-
		pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, pkg);
+
		pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, new_pkg);

		if (stat(path, &st) == -1 || pkgsize != st.st_size)
			/* file looks corrupted (wrong size),
@@ -592,26 +593,26 @@ print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,

			*dlsize += pkgsize;

-
		if (oldversion != NULL) {
-
			switch (pkg_version_change(pkg)) {
+
		if (old_pkg != NULL) {
+
			switch (pkg_version_change(new_pkg)) {
			case PKG_DOWNGRADE:
-
				pkg_printf("\tDowngrading %n: %V -> %v", pkg, pkg, pkg);
+
				pkg_printf("\tDowngrading %n: %v -> %v", new_pkg, old_pkg, new_pkg);
				if (pkg_repos_total_count() > 1)
-
					pkg_printf(" [%N]", pkg);
+
					pkg_printf(" [%N]", new_pkg);
				printf("\n");
				break;
			case PKG_REINSTALL:
-
				pkg_printf("\tReinstalling %n-%v", pkg, pkg);
+
				pkg_printf("\tReinstalling %n-%v", new_pkg, new_pkg);
				if (pkg_repos_total_count() > 1)
-
					pkg_printf(" [%N]", pkg);
+
					pkg_printf(" [%N]", new_pkg);
				if (why != NULL)
					printf(" (%s)", why);
				printf("\n");
				break;
			case PKG_UPGRADE:
-
				pkg_printf("\tUpgrading %n: %V -> %v", pkg, pkg, pkg);
+
				pkg_printf("\tUpgrading %n: %v -> %v", new_pkg, old_pkg, new_pkg);
				if (pkg_repos_total_count() > 1)
-
					pkg_printf(" [%N]", pkg);
+
					pkg_printf(" [%N]", new_pkg);
				printf("\n");
				break;
			}
@@ -620,9 +621,9 @@ print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,
		} else {
			*newsize += flatsize;

-
			pkg_printf("\tInstalling %n: %v", pkg, pkg);
+
			pkg_printf("\tInstalling %n: %v", new_pkg, new_pkg);
			if (pkg_repos_total_count() > 1)
-
				pkg_printf(" [%N]", pkg);
+
				pkg_printf(" [%N]", new_pkg);
			printf("\n");
		}
		break;
@@ -631,11 +632,11 @@ print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,
		*oldsize += oldflatsize;
		*newsize += flatsize;

-
		pkg_printf("\tRemoving %n-%v\n", pkg, pkg);
+
		pkg_printf("\tRemoving %n-%v\n", new_pkg, new_pkg);
		break;
	case PKG_JOBS_FETCH:
		*dlsize += pkgsize;
-
		pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, pkg);
+
		pkg_snprintf(path, MAXPATHLEN, "%S/%R", cachedir, new_pkg);
		if (stat(path, &st) != -1)
			*oldsize = st.st_size;
		else
@@ -644,7 +645,7 @@ print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,

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

-
		pkg_printf("\t%n-%v ", pkg, pkg);
+
		pkg_printf("\t%n-%v ", new_pkg, new_pkg);
		printf("(%" PRId64 "%% of %s)\n", 100 - (100 * (*oldsize))/pkgsize, size);
		break;
	}
@@ -653,7 +654,7 @@ print_jobs_summary_pkg(struct pkg *pkg, pkg_jobs_t type, int64_t *oldsize,
void
print_jobs_summary(struct pkg_jobs *jobs, const char *msg, ...)
{
-
	struct pkg *pkg;
+
	struct pkg *new_pkg, *old_pkg;
	void *iter = NULL;
	char size[7];
	va_list ap;
@@ -667,17 +668,17 @@ print_jobs_summary(struct pkg_jobs *jobs, const char *msg, ...)
	vprintf(msg, ap);
	va_end(ap);

-
	while ((pkg = pkg_jobs_add_iter(jobs, &iter))) {
-
		print_jobs_summary_pkg(pkg, type, &oldsize, &newsize, &dlsize);
+
	while (pkg_jobs_add_iter(jobs, &iter, &new_pkg, &old_pkg)) {
+
		print_jobs_summary_pkg(new_pkg, old_pkg, type, &oldsize, &newsize, &dlsize);
	}

	iter = NULL;
-
	while ((pkg = pkg_jobs_delete_iter(jobs, &iter))) {
-
		print_jobs_summary_pkg(pkg, inv_type, &oldsize, &newsize, &dlsize);
+
	while (pkg_jobs_delete_iter(jobs, &iter, &new_pkg, &old_pkg)) {
+
		print_jobs_summary_pkg(new_pkg, old_pkg, inv_type, &oldsize, &newsize, &dlsize);
	}
	iter = NULL;
-
	while ((pkg = pkg_jobs_upgrade_iter(jobs, &iter))) {
-
		print_jobs_summary_pkg(pkg, inv_type, &oldsize, &newsize, &dlsize);
+
	while (pkg_jobs_upgrade_iter(jobs, &iter, &new_pkg, &old_pkg)) {
+
		print_jobs_summary_pkg(new_pkg, old_pkg, type, &oldsize, &newsize, &dlsize);
	}

	if (oldsize > newsize) {
modified pkg/version.c
@@ -172,6 +172,7 @@ exec_version(int argc, char **argv)
	char *pattern=NULL;
	struct stat sb;
	char portsdirmakefile[MAXPATHLEN];
+
	int retcode = EXIT_SUCCESS;

	pkg_config_bool(PKG_CONFIG_REPO_AUTOUPDATE, &auto_update);

@@ -325,7 +326,6 @@ exec_version(int argc, char **argv)
		   user is forced to have a repo.sqlite */
		if (opt & VERSION_SOURCE_REMOTE) {
			if (auto_update) {
-
				int retcode;

				retcode = pkgcli_update(false);
				if (retcode != EPKG_OK)
@@ -337,6 +337,12 @@ exec_version(int argc, char **argv)
			if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
				return (EX_IOERR);

+
		if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
			pkgdb_close(db);
+
			warnx("Cannot get a read lock on a database, it is locked by another process");
+
			return (EX_TEMPFAIL);
+
		}
+

		if ((it = pkgdb_query(db, pattern, match)) == NULL)
			goto cleanup;

@@ -345,8 +351,11 @@ exec_version(int argc, char **argv)
			rel_major_ver = (int) strtol(u.release, NULL, 10);
			snprintf(indexpath, sizeof(indexpath), "%s/INDEX-%d", portsdir, rel_major_ver);
			indexfile = fopen(indexpath, "r");
-
			if (!indexfile)
-
				err(EX_SOFTWARE, "Unable to open %s!", indexpath);
+
			if (!indexfile) {
+
				warnx("Unable to open %s!", indexpath);
+
				retcode = EX_IOERR;
+
				goto cleanup;
+
			}

			while ((linelen = getline(&line, &linecap, indexfile)) > 0) {
				/* line is pkgname|portdir|... */
@@ -432,7 +441,8 @@ cleanup:
	pkg_free(pkg);
	pkg_free(pkg_remote);
	pkgdb_it_free(it);
+
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);

-
	return (EX_OK);
+
	return (retcode);
}
modified pkg/which.c
@@ -33,6 +33,7 @@
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
+
#include <err.h>

#include "pkgcli.h"

@@ -80,11 +81,6 @@ exec_which(int argc, char **argv)
		return (EX_USAGE);
	}

-
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
-
		pkgdb_close(db);
-
		return (EX_IOERR);
-
	}
-

	if (!glob)
		absolutepath(argv[0], pathabs, sizeof(pathabs));
	else {
@@ -92,8 +88,20 @@ exec_which(int argc, char **argv)
			return (EX_USAGE);
	}

-
	if ((it = pkgdb_query_which(db, pathabs, glob)) == NULL)
+
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
		return (EX_IOERR);
+
	}
+

+
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY, 0, 0) != EPKG_OK) {
+
		pkgdb_close(db);
+
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		return (EX_TEMPFAIL);
+
	}
+

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

	while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
		retcode = EX_OK;
@@ -113,6 +121,9 @@ exec_which(int argc, char **argv)
	pkg_free(pkg);
	pkgdb_it_free(it);

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

	return (retcode);
}
added scripts/pkg_aspcud.sh
@@ -0,0 +1,51 @@
+
#!/bin/sh
+
# Copyright (c) 2014 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.
+
#
+
#
+
# This is a simple support script to enable aspcud external solver.
+
# This solver can be installed from package aspcud or from the port
+
# in math/aspcud.
+
# 
+
# To use this utility as the external solver you can specify CUDF_SOLVER option:
+
# pkg -o CUDF_SOLVER=/path/to/pkg_aspcud.sh <upgrade|install|remove>
+

+

+
[ -z "$CLASP_OPTS" ] && CLASP_OPTS="--opt-heu=1 --sat-prepro --restarts=L,128 --heuristic=VSIDS --opt-hierarch=1 --local-restarts --del-max=200000,250 --save-progress=0 --quiet=1,1"
+
[ -z "$CLASP" ] && CLASP="clasp"
+

+
[ -z "$GRINGO_OPTS" ] && GRINGO_OPTS="/usr/local/share/aspcud/misc2012.lp"
+
[ -z "$GRINGO" ] && GRINGO="gringo"
+

+
TRENDY="-count(removed),-notuptodate(solution),-unsat_recommends(solution),-count(new)"
+

+
[ -z "$CUDF2LP_OPTS" ] && CUDF2LP_OPTS="-c $TRENDY"
+
[ -z "$CUDF2LP" ] && CUDF2LP="cudf2lp"
+

+
( $CUDF2LP $CUDF2LP_OPTS ) | ( $GRINGO $GRINGO_OPTS - ) | ( $CLASP $CLASP_OPTS ) \
+
| grep -A 1 "Answer" | sed -e '1d' | tr " " "\n" \
+
| grep 'in(' | sed -e 's/in("\([^,]*\)",\([0-9]*\))/package: \1\
+
version: \2\
+
installed: true\
+
/'
modified scripts/sbin/pkg2ng.in
@@ -32,7 +32,7 @@ shift $(( ${OPTIND} -1 ))
if [ -f ${PORTSDIR}/Mk/bsd.pkgng.mk ]; then
	FORCE_POST=$(make _POSTMKINCLUDED=1 UID=$(id -u) -f ${PORTSDIR}/Mk/bsd.pkgng.mk -V _FORCE_POST_PATTERNS)
else
-
	FORCE_POST="rmdir kldxref mkfontscale mkfontdir fc-cache fonts.dir fonts.scale gtk-update-icon-cache gio-querymodules gtk-query-immodules ldconfig load-octave-pkg update-desktop-database update-mime-database gdk-pixbuf-query-loaders catalog.ports glib-compile-schemas"
+
	FORCE_POST="rmdir kldxref mkfontscale mkfontdir fc-cache fonts.dir fonts.scale gtk-update-icon-cache gio-querymodules gtk-query-immodules ldconfig load-octave-pkg update-desktop-database update-mime-database gdk-pixbuf-query-loaders catalog.ports glib-compile-schemas ccache-update-links"
fi

FORCE_PORT=$FORCE_POST __PREFIX__/sbin/pkg convert
@@ -44,7 +44,9 @@ rm -f ${PKG_DBDIR}/pkgdb.db > /dev/null 2>&1

# Build the shlibs_{provided,required} tables in the pkg database

+
echo -n "Analysing shared libraries, this will take a while... "
__PREFIX__/sbin/pkg check -Ba
+
echo "done"

# Fix up /etc/periodic.conf if requested.  Pull in the current
# periodic settings, and update /etc/periodic.conf to disable any