Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Update libucl to latest version
Baptiste Daroussin committed 12 years ago
commit 5b430c73cab9eb7a0f2f9fca6a8dc3dfe154845c
parent bf36394
5 files changed +329 -60
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;
	}