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