Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Update to the recent libucl.
Vsevolod Stakhov committed 11 years ago
commit 0040fbf73122f2d8d9ca9e887a78af1112ef31d6
parent 84ce9f4
7 files changed +427 -654
modified external/Makefile.am
@@ -140,6 +140,8 @@ ucl_common_cflags= -I$(top_srcdir)/external//libucl/uthash \
			-I$(top_srcdir)/external//libucl/src \
			-Wno-unused-parameter -Wno-pointer-sign
libucl_la_SOURCES=	libucl/src/ucl_emitter.c \
+
			libucl/src/ucl_emitter_utils.c \
+
			libucl/src/ucl_emitter_streamline.c \
			libucl/src/ucl_hash.c \
			libucl/src/ucl_parser.c \
			libucl/src/ucl_schema.c \
modified external/libucl/include/ucl.h
@@ -786,6 +786,7 @@ UCL_EXTERN bool ucl_parser_set_filevars (struct ucl_parser *parser, const char *
 * @{
 */

+
struct ucl_emitter_context;
/**
 * Structure using for emitter callbacks
 */
@@ -798,10 +799,49 @@ struct ucl_emitter_functions {
	int (*ucl_emitter_append_int) (int64_t elt, void *ud);
	/** Append floating point element */
	int (*ucl_emitter_append_double) (double elt, void *ud);
+
	/** Free userdata */
+
	void (*ucl_emitter_free_func)(void *ud);
	/** Opaque userdata pointer */
	void *ud;
};

+
struct ucl_emitter_operations {
+
	/** Write a primitive element */
+
	void (*ucl_emitter_write_elt) (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool first, bool print_key);
+
	/** Start ucl object */
+
	void (*ucl_emitter_start_object) (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool print_key);
+
	/** End ucl object */
+
	void (*ucl_emitter_end_object) (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj);
+
	/** Start ucl array */
+
	void (*ucl_emitter_start_array) (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool print_key);
+
	void (*ucl_emitter_end_array) (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj);
+
};
+

+
/**
+
 * Structure that defines emitter functions
+
 */
+
struct ucl_emitter_context {
+
	/** Name of emitter (e.g. json, compact_json) */
+
	const char *name;
+
	/** Unique id (e.g. UCL_EMIT_JSON for standard emitters */
+
	int id;
+
	/** A set of output functions */
+
	const struct ucl_emitter_functions *func;
+
	/** A set of output operations */
+
	const struct ucl_emitter_operations *ops;
+
	/** Current amount of indent tabs */
+
	unsigned int ident;
+
	/** Top level object */
+
	const ucl_object_t *top;
+
	/** The rest of context */
+
	unsigned char data[1];
+
};
+

/**
 * Emit object to a string
 * @param obj object
@@ -817,11 +857,81 @@ UCL_EXTERN unsigned char *ucl_object_emit (const ucl_object_t *obj,
 * @param obj object
 * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is
 * #UCL_EMIT_CONFIG then emit config like object
+
 * @param emitter a set of emitter functions
 * @return dump of an object (must be freed after using) or NULL in case of error
 */
UCL_EXTERN bool ucl_object_emit_full (const ucl_object_t *obj,
		enum ucl_emitter emit_type,
		struct ucl_emitter_functions *emitter);
+

+
/**
+
 * Start streamlined UCL object emitter
+
 * @param obj top UCL object
+
 * @param emit_type emit type
+
 * @param emitter a set of emitter functions
+
 * @return new streamlined context that should be freed by
+
 * `ucl_object_emit_streamline_finish`
+
 */
+
UCL_EXTERN struct ucl_emitter_context* ucl_object_emit_streamline_new (
+
		const ucl_object_t *obj, enum ucl_emitter emit_type,
+
		struct ucl_emitter_functions *emitter);
+

+
/**
+
 * Start object or array container for the streamlined output
+
 * @param ctx streamlined context
+
 * @param obj container object
+
 */
+
UCL_EXTERN void ucl_object_emit_streamline_start_container (
+
		struct ucl_emitter_context *ctx, const ucl_object_t *obj);
+
/**
+
 * Add a complete UCL object to streamlined output
+
 * @param ctx streamlined context
+
 * @param obj object to output
+
 */
+
UCL_EXTERN void ucl_object_emit_streamline_add_object (
+
		struct ucl_emitter_context *ctx, const ucl_object_t *obj);
+
/**
+
 * End previously added container
+
 * @param ctx streamlined context
+
 */
+
UCL_EXTERN void ucl_object_emit_streamline_end_container (
+
		struct ucl_emitter_context *ctx);
+
/**
+
 * Terminate streamlined container finishing all containers in it
+
 * @param ctx streamlined context
+
 */
+
UCL_EXTERN void ucl_object_emit_streamline_finish (
+
		struct ucl_emitter_context *ctx);
+

+
/**
+
 * Returns functions to emit object to memory
+
 * @param pmem target pointer (should be freed by caller)
+
 * @return emitter functions structure
+
 */
+
UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_memory_funcs (
+
		void **pmem);
+

+
/**
+
 * Returns functions to emit object to FILE *
+
 * @param fp FILE * object
+
 * @return emitter functions structure
+
 */
+
UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_file_funcs (
+
		FILE *fp);
+
/**
+
 * Returns functions to emit object to a file descriptor
+
 * @param fd file descriptor
+
 * @return emitter functions structure
+
 */
+
UCL_EXTERN struct ucl_emitter_functions* ucl_object_emit_fd_funcs (
+
		int fd);
+

+
/**
+
 * Free emitter functions
+
 * @param f pointer to functions
+
 */
+
UCL_EXTERN void ucl_object_emit_funcs_free (struct ucl_emitter_functions *f);
+

/** @} */

/**
modified external/libucl/src/Makefile.am
@@ -4,6 +4,8 @@ libucl_common_cflags= -I$(top_srcdir)/src \
			-Wall -W -Wno-unused-parameter -Wno-pointer-sign
lib_LTLIBRARIES=	libucl.la
libucl_la_SOURCES=	ucl_emitter.c \
+
					ucl_emitter_streamline.c \
+
					ucl_emitter_utils.c \
					ucl_hash.c \
					ucl_parser.c \
					ucl_schema.c \
modified external/libucl/src/ucl_emitter.c
@@ -36,38 +36,55 @@
#endif

/**
-
 * @file rcl_emitter.c
+
 * @file ucl_emitter.c
 * Serialise UCL object to various of output formats
 */

+
static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool first, bool print_key, bool compact);
+

+
#define UCL_EMIT_TYPE_OPS(type)		\
+
	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj, bool first, bool print_key);	\
+
	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj, bool print_key);	\
+
	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj, bool print_key);	\
+
	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj);	\
+
	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj)
+

+
/*
+
 * JSON format operations
+
 */
+
UCL_EMIT_TYPE_OPS(json);
+
UCL_EMIT_TYPE_OPS(json_compact);
+
UCL_EMIT_TYPE_OPS(config);
+
UCL_EMIT_TYPE_OPS(yaml);
+

+
#define UCL_EMIT_TYPE_CONTENT(type) {	\
+
	.ucl_emitter_write_elt = ucl_emit_ ## type ## _elt,	\
+
	.ucl_emitter_start_object = ucl_emit_ ## type ##_start_obj,	\
+
	.ucl_emitter_start_array = ucl_emit_ ## type ##_start_array,	\
+
	.ucl_emitter_end_object = ucl_emit_ ## type ##_end_object,	\
+
	.ucl_emitter_end_array = ucl_emit_ ## type ##_end_array	\
+
}
+

+

+
const struct ucl_emitter_operations ucl_standartd_emitter_ops[] = {
+
	[UCL_EMIT_JSON] = UCL_EMIT_TYPE_CONTENT(json),
+
	[UCL_EMIT_JSON_COMPACT] = UCL_EMIT_TYPE_CONTENT(json_compact),
+
	[UCL_EMIT_CONFIG] = UCL_EMIT_TYPE_CONTENT(config),
+
	[UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml)
+
};
+

+
/*
+
 * Utility to check whether we need a top object
+
 */
+
#define UCL_EMIT_IDENT_TOP_OBJ(ctx, obj) ((ctx)->top != (obj) || \
+
		((ctx)->id == UCL_EMIT_JSON_COMPACT || (ctx)->id == UCL_EMIT_JSON))

-
static void ucl_obj_write_json (const ucl_object_t *obj,
-
		struct ucl_emitter_functions *func,
-
		unsigned int tabs,
-
		bool start_tabs,
-
		bool compact);
-
static void ucl_elt_write_json (const ucl_object_t *obj,
-
		struct ucl_emitter_functions *func,
-
		unsigned int tabs,
-
		bool start_tabs,
-
		bool compact);
-
static void ucl_elt_write_config (const ucl_object_t *obj,
-
		struct ucl_emitter_functions *func,
-
		unsigned int tabs,
-
		bool start_tabs,
-
		bool is_top,
-
		bool expand_array);
-
static void ucl_elt_write_yaml (const ucl_object_t *obj,
-
		struct ucl_emitter_functions *func,
-
		unsigned int tabs,
-
		bool start_tabs,
-
		bool compact,
-
		bool expand_array);
-
static void ucl_elt_array_write_yaml (const ucl_object_t *obj,
-
		struct ucl_emitter_functions *func,
-
		unsigned int tabs,
-
		bool start_tabs,
-
		bool is_top);

/**
 * Add tabulation to the output buffer
@@ -75,689 +92,358 @@ static void ucl_elt_array_write_yaml (const ucl_object_t *obj,
 * @param tabs number of tabs to add
 */
static inline void
-
ucl_add_tabs (struct ucl_emitter_functions *func, unsigned int tabs, bool compact)
+
ucl_add_tabs (const struct ucl_emitter_functions *func, unsigned int tabs,
+
		bool compact)
{
-
	if (!compact) {
+
	if (!compact && tabs > 0) {
		func->ucl_emitter_append_character (' ', tabs * 4, func->ud);
	}
}

/**
-
 * Serialise string
-
 * @param str string to emit
-
 * @param buf target buffer
+
 * Print key for the element
+
 * @param ctx
+
 * @param obj
 */
static void
-
ucl_elt_string_write_json (const char *str, size_t size,
-
		struct ucl_emitter_functions *func)
+
ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool compact)
{
-
	const char *p = str, *c = str;
-
	size_t len = 0;
-

-
	func->ucl_emitter_append_character ('"', 1, func->ud);
-
	while (size) {
-
		if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
-
			if (len > 0) {
-
				func->ucl_emitter_append_len (c, len, func->ud);
-
			}
-
			switch (*p) {
-
			case '\n':
-
				func->ucl_emitter_append_len ("\\n", 2, func->ud);
-
				break;
-
			case '\r':
-
				func->ucl_emitter_append_len ("\\r", 2, func->ud);
-
				break;
-
			case '\b':
-
				func->ucl_emitter_append_len ("\\b", 2, func->ud);
-
				break;
-
			case '\t':
-
				func->ucl_emitter_append_len ("\\t", 2, func->ud);
-
				break;
-
			case '\f':
-
				func->ucl_emitter_append_len ("\\f", 2, func->ud);
-
				break;
-
			case '\\':
-
				func->ucl_emitter_append_len ("\\\\", 2, func->ud);
-
				break;
-
			case '"':
-
				func->ucl_emitter_append_len ("\\\"", 2, func->ud);
-
				break;
-
			}
-
			len = 0;
-
			c = ++p;
+
	const struct ucl_emitter_functions *func = ctx->func;
+

+
	if (!print_key) {
+
		return;
+
	}
+

+
	if (ctx->id == UCL_EMIT_CONFIG) {
+
		if (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
+
			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
		}
		else {
-
			p ++;
-
			len ++;
+
			func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
		}
-
		size --;
-
	}
-
	if (len > 0) {
-
		func->ucl_emitter_append_len (c, len, func->ud);
-
	}
-
	func->ucl_emitter_append_character ('"', 1, func->ud);
-
}

-
/**
-
 * Write a single object to the buffer
-
 * @param obj object to write
-
 * @param buf target buffer
-
 */
-
static void
-
ucl_elt_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool compact)
-
{
-
	const ucl_object_t *cur;
-
	ucl_hash_iter_t it = NULL;
-

-
	if (start_tabs) {
-
		ucl_add_tabs (func, tabs, compact);
-
	}
-
	if (compact) {
-
		func->ucl_emitter_append_character ('{', 1, func->ud);
+
		if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
+
			func->ucl_emitter_append_len (" = ", 3, func->ud);
+
		}
+
		else {
+
			func->ucl_emitter_append_character (' ', 1, func->ud);
+
		}
	}
	else {
-
		func->ucl_emitter_append_len ("{\n", 2, func->ud);
-
	}
-
	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
-
		ucl_add_tabs (func, tabs + 1, compact);
-
		if (cur->keylen > 0) {
-
			ucl_elt_string_write_json (cur->key, cur->keylen, func);
+
		if (obj->keylen > 0) {
+
			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
		}
		else {
			func->ucl_emitter_append_len ("null", 4, func->ud);
		}
+

		if (compact) {
			func->ucl_emitter_append_character (':', 1, func->ud);
		}
		else {
			func->ucl_emitter_append_len (": ", 2, func->ud);
		}
-
		ucl_obj_write_json (cur, func, tabs + 1, false, compact);
-
		if (ucl_hash_iter_has_next (it)) {
-
			if (compact) {
-
				func->ucl_emitter_append_character (',', 1, func->ud);
-
			}
-
			else {
-
				func->ucl_emitter_append_len (",\n", 2, func->ud);
-
			}
-
		}
-
		else if (!compact) {
-
			func->ucl_emitter_append_character ('\n', 1, func->ud);
-
		}
	}
-
	ucl_add_tabs (func, tabs, compact);
-
	func->ucl_emitter_append_character ('}', 1, func->ud);
}

-
/**
-
 * Write a single array to the buffer
-
 * @param obj array to write
-
 * @param buf target buffer
-
 */
static void
-
ucl_elt_array_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool compact)
+
ucl_emitter_finish_object (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool compact, bool is_array)
{
-
	const ucl_object_t *cur = obj;
+
	const struct ucl_emitter_functions *func = ctx->func;

-
	if (start_tabs) {
-
		ucl_add_tabs (func, tabs, compact);
-
	}
-
	if (compact) {
-
		func->ucl_emitter_append_character ('[', 1, func->ud);
-
	}
-
	else {
-
		func->ucl_emitter_append_len ("[\n", 2, func->ud);
-
	}
-
	while (cur) {
-
		ucl_elt_write_json (cur, func, tabs + 1, true, compact);
-
		if (cur->next != NULL) {
-
			if (compact) {
-
				func->ucl_emitter_append_character (',', 1, func->ud);
+
	if (ctx->id == UCL_EMIT_CONFIG && obj != ctx->top) {
+
		if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
+
			if (!is_array) {
+
				/* Objects are split by ';' */
+
				func->ucl_emitter_append_len (";\n", 2, func->ud);
			}
			else {
+
				/* Use commas for arrays */
				func->ucl_emitter_append_len (",\n", 2, func->ud);
			}
		}
-
		else if (!compact) {
-
			func->ucl_emitter_append_character ('\n', 1, func->ud);
-
		}
-
		cur = cur->next;
-
	}
-
	ucl_add_tabs (func, tabs, compact);
-
	func->ucl_emitter_append_character (']', 1, func->ud);
-
}
-

-
/**
-
 * Emit a single element
-
 * @param obj object
-
 * @param buf buffer
-
 */
-
static void
-
ucl_elt_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool compact)
-
{
-
	bool flag;
-

-
	switch (obj->type) {
-
	case UCL_INT:
-
		if (start_tabs) {
-
			ucl_add_tabs (func, tabs, compact);
-
		}
-
		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
-
		break;
-
	case UCL_FLOAT:
-
	case UCL_TIME:
-
		if (start_tabs) {
-
			ucl_add_tabs (func, tabs, compact);
-
		}
-
		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
-
		break;
-
	case UCL_BOOLEAN:
-
		if (start_tabs) {
-
			ucl_add_tabs (func, tabs, compact);
-
		}
-
		flag = ucl_object_toboolean (obj);
-
		if (flag) {
-
			func->ucl_emitter_append_len ("true", 4, func->ud);
-
		}
		else {
-
			func->ucl_emitter_append_len ("false", 5, func->ud);
-
		}
-
		break;
-
	case UCL_STRING:
-
		if (start_tabs) {
-
			ucl_add_tabs (func, tabs, compact);
-
		}
-
		ucl_elt_string_write_json (obj->value.sv, obj->len, func);
-
		break;
-
	case UCL_NULL:
-
		if (start_tabs) {
-
			ucl_add_tabs (func, tabs, compact);
+
			func->ucl_emitter_append_character ('\n', 1, func->ud);
		}
-
		func->ucl_emitter_append_len ("null", 4, func->ud);
-
		break;
-
	case UCL_OBJECT:
-
		ucl_elt_obj_write_json (obj, func, tabs, start_tabs, compact);
-
		break;
-
	case UCL_ARRAY:
-
		ucl_elt_array_write_json (obj->value.av, func, tabs, start_tabs, compact);
-
		break;
-
	case UCL_USERDATA:
-
		break;
	}
}

/**
-
 * Write a single object to the buffer
-
 * @param obj object
-
 * @param buf target buffer
+
 * End standard ucl object
+
 * @param ctx emitter context
+
 * @param compact compact flag
 */
static void
-
ucl_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool compact)
+
ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool compact)
{
-
	const ucl_object_t *cur;
-
	bool is_array = (obj->next != NULL);
-

-
	if (is_array) {
-
		/* This is an array actually */
-
		if (start_tabs) {
-
			ucl_add_tabs (func, tabs, compact);
-
		}
+
	const struct ucl_emitter_functions *func = ctx->func;

+
	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
+
		ctx->ident --;
		if (compact) {
-
			func->ucl_emitter_append_character ('[', 1, func->ud);
+
			func->ucl_emitter_append_character ('}', 1, func->ud);
		}
		else {
-
			func->ucl_emitter_append_len ("[\n", 2, func->ud);
-
		}
-
		cur = obj;
-
		while (cur != NULL) {
-
			ucl_elt_write_json (cur, func, tabs + 1, true, compact);
-
			if (cur->next) {
-
				func->ucl_emitter_append_character (',', 1, func->ud);
-
			}
-
			if (!compact) {
+
			if (ctx->id != UCL_EMIT_CONFIG) {
+
				/* newline is already added for this format */
				func->ucl_emitter_append_character ('\n', 1, func->ud);
			}
-
			cur = cur->next;
+
			ucl_add_tabs (func, ctx->ident, compact);
+
			func->ucl_emitter_append_character ('}', 1, func->ud);
		}
-
		ucl_add_tabs (func, tabs, compact);
-
		func->ucl_emitter_append_character (']', 1, func->ud);
-
	}
-
	else {
-
		ucl_elt_write_json (obj, func, tabs, start_tabs, compact);
	}

+
	ucl_emitter_finish_object (ctx, obj, compact, false);
}

/**
-
 * Emit an object to json
-
 * @param obj object
-
 * @return json output (should be freed after using)
+
 * End standard ucl array
+
 * @param ctx emitter context
+
 * @param compact compact flag
 */
static void
-
ucl_object_emit_json (const ucl_object_t *obj, bool compact,
-
		struct ucl_emitter_functions *func)
+
ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool compact)
{
-
	ucl_obj_write_json (obj, func, 0, false, compact);
-
}
-

-
/**
-
 * Write a single object to the buffer
-
 * @param obj object to write
-
 * @param buf target buffer
-
 */
-
static void
-
ucl_elt_obj_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool is_top)
-
{
-
	const ucl_object_t *cur, *cur_obj;
-
	ucl_hash_iter_t it = NULL;
+
	const struct ucl_emitter_functions *func = ctx->func;

-
	if (start_tabs) {
-
		ucl_add_tabs (func, tabs, is_top);
-
	}
-
	if (!is_top) {
-
		func->ucl_emitter_append_len ("{\n", 2, func->ud);
+
	ctx->ident --;
+
	if (compact) {
+
		func->ucl_emitter_append_character (']', 1, func->ud);
	}
-

-
	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
-
		LL_FOREACH (cur, cur_obj) {
-
			ucl_add_tabs (func, tabs + 1, is_top);
-
			if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
-
				ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func);
-
			}
-
			else {
-
				func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud);
-
			}
-
			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
-
				func->ucl_emitter_append_len (" = ", 3, func->ud);
-
			}
-
			else {
-
				func->ucl_emitter_append_character (' ', 1, func->ud);
-
			}
-
			ucl_elt_write_config (cur_obj, func,
-
					is_top ? tabs : tabs + 1,
-
					false, false, false);
-
			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
-
				func->ucl_emitter_append_len (";\n", 2, func->ud);
-
			}
-
			else {
-
				func->ucl_emitter_append_character ('\n', 1, func->ud);
-
			}
+
	else {
+
		if (ctx->id != UCL_EMIT_CONFIG) {
+
			/* newline is already added for this format */
+
			func->ucl_emitter_append_character ('\n', 1, func->ud);
		}
+
		ucl_add_tabs (func, ctx->ident, compact);
+
		func->ucl_emitter_append_character (']', 1, func->ud);
	}

-
	ucl_add_tabs (func, tabs, is_top);
-
	if (!is_top) {
-
		func->ucl_emitter_append_character ('}', 1, func->ud);
-
	}
+
	ucl_emitter_finish_object (ctx, obj, compact, true);
}

/**
-
 * Write a single array to the buffer
-
 * @param obj array to write
-
 * @param buf target buffer
+
 * Start emit standard UCL array
+
 * @param ctx emitter context
+
 * @param obj object to write
+
 * @param compact compact flag
 */
static void
-
ucl_elt_array_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool is_top)
+
ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool print_key, bool compact)
{
-
	const ucl_object_t *cur = obj;
-

-
	if (start_tabs) {
-
		ucl_add_tabs (func, tabs, false);
-
	}
+
	const ucl_object_t *cur;
+
	const struct ucl_emitter_functions *func = ctx->func;
+
	bool first = true;

-
	func->ucl_emitter_append_len ("[\n", 2, func->ud);
-
	while (cur) {
-
		ucl_elt_write_config (cur, func, tabs + 1, true, false, false);
-
		func->ucl_emitter_append_len (",\n", 2, func->ud);
-
		cur = cur->next;
-
	}
-
	ucl_add_tabs (func, tabs, false);
-
	func->ucl_emitter_append_character (']', 1, func->ud);
-
}
+
	ucl_emitter_print_key (print_key, ctx, obj, compact);

-
/**
-
 * Emit a single element
-
 * @param obj object
-
 * @param buf buffer
-
 */
-
static void
-
ucl_elt_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
-
{
-
	bool flag;
-

-
	if (expand_array && obj->next != NULL) {
-
		ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top);
+
	if (compact) {
+
		func->ucl_emitter_append_character ('[', 1, func->ud);
	}
	else {
-
		switch (obj->type) {
-
		case UCL_INT:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
-
			break;
-
		case UCL_FLOAT:
-
		case UCL_TIME:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
-
			break;
-
		case UCL_BOOLEAN:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			flag = ucl_object_toboolean (obj);
-
			if (flag) {
-
				func->ucl_emitter_append_len ("true", 4, func->ud);
-
			}
-
			else {
-
				func->ucl_emitter_append_len ("false", 5, func->ud);
-
			}
-
			break;
-
		case UCL_STRING:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
-
			break;
-
		case UCL_NULL:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			func->ucl_emitter_append_len ("null", 4, func->ud);
-
			break;
-
		case UCL_OBJECT:
-
			ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top);
-
			break;
-
		case UCL_ARRAY:
-
			ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top);
-
			break;
-
		case UCL_USERDATA:
-
			break;
-
		}
+
		func->ucl_emitter_append_len ("[\n", 2, func->ud);
	}
-
}

-
/**
-
 * Emit an object to rcl
-
 * @param obj object
-
 * @return rcl output (should be freed after using)
-
 */
-
static void
-
ucl_object_emit_config (const ucl_object_t *obj, struct ucl_emitter_functions *func)
-
{
-
	ucl_elt_write_config (obj, func, 0, false, true, true);
-
}
+
	ctx->ident ++;

-

-
static void
-
ucl_obj_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs)
-
{
-
	bool is_array = (obj->next != NULL);
-

-
	if (is_array) {
-
		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false);
+
	if (obj->type == UCL_ARRAY) {
+
		/* explicit array */
+
		cur = obj->value.av;
	}
	else {
-
		ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true);
+
		/* implicit array */
+
		cur = obj;
+
	}
+

+
	while (cur) {
+
		ucl_emitter_common_elt (ctx, cur, first, false, compact);
+
		first = false;
+
		cur = cur->next;
	}
}

/**
-
 * Write a single object to the buffer
+
 * Start emit standard UCL object
+
 * @param ctx emitter context
 * @param obj object to write
-
 * @param buf target buffer
+
 * @param compact compact flag
 */
static void
-
ucl_elt_obj_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool is_top)
+
ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool print_key, bool compact)
{
-
	const ucl_object_t *cur;
	ucl_hash_iter_t it = NULL;
-

-
	if (start_tabs) {
-
		ucl_add_tabs (func, tabs, is_top);
-
	}
-
	if (!is_top) {
-
		func->ucl_emitter_append_len ("{\n", 2, func->ud);
+
	const ucl_object_t *cur, *elt;
+
	const struct ucl_emitter_functions *func = ctx->func;
+
	bool first = true;
+

+
	ucl_emitter_print_key (print_key, ctx, obj, compact);
+
	/*
+
	 * Print <ident_level>{
+
	 * <ident_level + 1><object content>
+
	 */
+
	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
+
		if (compact) {
+
			func->ucl_emitter_append_character ('{', 1, func->ud);
+
		}
+
		else {
+
			func->ucl_emitter_append_len ("{\n", 2, func->ud);
+
		}
+
		ctx->ident ++;
	}

	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
-
		ucl_add_tabs (func, tabs + 1, is_top);
-
		if (cur->keylen > 0) {
-
			ucl_elt_string_write_json (cur->key, cur->keylen, func);
+

+
		if (ctx->id == UCL_EMIT_CONFIG) {
+
			LL_FOREACH (cur, elt) {
+
				ucl_emitter_common_elt (ctx, elt, first, true, compact);
+
			}
		}
		else {
-
			func->ucl_emitter_append_len ("null", 4, func->ud);
-
		}
-
		func->ucl_emitter_append_len (": ", 2, func->ud);
-
		ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false);
-
		if (ucl_hash_iter_has_next(it)) {
-
			if (!is_top) {
-
				func->ucl_emitter_append_len (",\n", 2, func->ud);
+
			/* Expand implicit arrays */
+
			if (cur->next != NULL) {
+
				if (!first) {
+
					if (compact) {
+
						func->ucl_emitter_append_character (',', 1, func->ud);
+
					}
+
					else {
+
						func->ucl_emitter_append_len (",\n", 2, func->ud);
+
					}
+
				}
+
				ucl_add_tabs (func, ctx->ident, compact);
+
				ucl_emitter_common_start_array (ctx, cur, true, compact);
+
				ucl_emitter_common_end_array (ctx, cur, compact);
			}
			else {
-
				func->ucl_emitter_append_character ('\n', 1, func->ud);
+
				ucl_emitter_common_elt (ctx, cur, first, true, compact);
			}
		}
-
		else {
-
			func->ucl_emitter_append_character ('\n', 1, func->ud);
-
		}
-
	}

-
	ucl_add_tabs (func, tabs, is_top);
-
	if (!is_top) {
-
		func->ucl_emitter_append_character ('}', 1, func->ud);
+
		first = false;
	}
}

/**
-
 * Write a single array to the buffer
-
 * @param obj array to write
-
 * @param buf target buffer
+
 * Common choice of object emitting
+
 * @param ctx emitter context
+
 * @param obj object to print
+
 * @param first flag to mark the first element
+
 * @param print_key print key of an object
+
 * @param compact compact output
 */
static void
-
ucl_elt_array_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool is_top)
+
ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
+
		const ucl_object_t *obj, bool first, bool print_key, bool compact)
{
-
	const ucl_object_t *cur = obj;
-

-
	if (start_tabs) {
-
		ucl_add_tabs (func, tabs, false);
-
	}
+
	const struct ucl_emitter_functions *func = ctx->func;
+
	bool flag;

-
	func->ucl_emitter_append_len ("[\n", 2, func->ud);
-
	while (cur) {
-
		ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false);
-
		func->ucl_emitter_append_len (",\n", 2, func->ud);
-
		cur = cur->next;
+
	if (ctx->id != UCL_EMIT_CONFIG && !first) {
+
		if (compact) {
+
			func->ucl_emitter_append_character (',', 1, func->ud);
+
		}
+
		else {
+
			func->ucl_emitter_append_len (",\n", 2, func->ud);
+
		}
	}
-
	ucl_add_tabs (func, tabs, false);
-
	func->ucl_emitter_append_character (']', 1, func->ud);
-
}

-
/**
-
 * Emit a single element
-
 * @param obj object
-
 * @param buf buffer
-
 */
-
static void
-
ucl_elt_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
-
		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
-
{
-
	bool flag;
+
	ucl_add_tabs (func, ctx->ident, compact);

-
	if (expand_array && obj->next != NULL ) {
-
		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top);
-
	}
-
	else {
-
		switch (obj->type) {
-
		case UCL_INT:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
-
			break;
-
		case UCL_FLOAT:
-
		case UCL_TIME:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
-
			break;
-
		case UCL_BOOLEAN:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			flag = ucl_object_toboolean (obj);
-
			if (flag) {
-
				func->ucl_emitter_append_len ("true", 4, func->ud);
-
			}
-
			else {
-
				func->ucl_emitter_append_len ("false", 5, func->ud);
-
			}
-
			break;
-
		case UCL_STRING:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
-
			break;
-
		case UCL_NULL:
-
			if (start_tabs) {
-
				ucl_add_tabs (func, tabs, false);
-
			}
-
			func->ucl_emitter_append_len ("null", 4, func->ud);
-
			break;
-
		case UCL_OBJECT:
-
			ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top);
-
			break;
-
		case UCL_ARRAY:
-
			ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top);
-
			break;
-
		case UCL_USERDATA:
-
			break;
+
	switch (obj->type) {
+
	case UCL_INT:
+
		ucl_emitter_print_key (print_key, ctx, obj, compact);
+
		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
+
		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+
		break;
+
	case UCL_FLOAT:
+
	case UCL_TIME:
+
		ucl_emitter_print_key (print_key, ctx, obj, compact);
+
		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
+
		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+
		break;
+
	case UCL_BOOLEAN:
+
		ucl_emitter_print_key (print_key, ctx, obj, compact);
+
		flag = ucl_object_toboolean (obj);
+
		if (flag) {
+
			func->ucl_emitter_append_len ("true", 4, func->ud);
+
		}
+
		else {
+
			func->ucl_emitter_append_len ("false", 5, func->ud);
		}
+
		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+
		break;
+
	case UCL_STRING:
+
		ucl_emitter_print_key (print_key, ctx, obj, compact);
+
		ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
+
		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+
		break;
+
	case UCL_NULL:
+
		ucl_emitter_print_key (print_key, ctx, obj, compact);
+
		func->ucl_emitter_append_len ("null", 4, func->ud);
+
		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
+
		break;
+
	case UCL_OBJECT:
+
		ucl_emitter_common_start_object (ctx, obj, print_key, compact);
+
		ucl_emitter_common_end_object (ctx, obj, compact);
+
		break;
+
	case UCL_ARRAY:
+
		ucl_emitter_common_start_array (ctx, obj, print_key, compact);
+
		ucl_emitter_common_end_array (ctx, obj, compact);
+
		break;
+
	case UCL_USERDATA:
+
		break;
	}
}

-
/**
-
 * Emit an object to rcl
-
 * @param obj object
-
 * @return rcl output (should be freed after using)
-
 */
-
static void
-
ucl_object_emit_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func)
-
{
-
	ucl_elt_write_yaml (obj, func, 0, false, true, true);
-
}
-

/*
-
 * Generic utstring output
+
 * Specific standard implementations of the emitter functions
 */
-
static int
-
ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
-
{
-
	UT_string *buf = ud;
-

-
	if (len == 1) {
-
		utstring_append_c (buf, c);
-
	}
-
	else {
-
		utstring_reserve (buf, len);
-
		memset (&buf->d[buf->i], c, len);
-
		buf->i += len;
-
		buf->d[buf->i] = '\0';
-
	}
-

-
	return 0;
-
}
-

-
static int
-
ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud)
-
{
-
	UT_string *buf = ud;
-

-
	utstring_append_len (buf, str, len);
-

-
	return 0;
-
}
-

-
static int
-
ucl_utstring_append_int (int64_t val, void *ud)
-
{
-
	UT_string *buf = ud;
-

-
	utstring_printf (buf, "%jd", (intmax_t)val);
-
	return 0;
-
}
-

-
static int
-
ucl_utstring_append_double (double val, void *ud)
-
{
-
	UT_string *buf = ud;
-
	const double delta = 0.0000001;
-

-
	if (val == (double)(int)val) {
-
		utstring_printf (buf, "%.1lf", val);
-
	}
-
	else if (fabs (val - (double)(int)val) < delta) {
-
		/* Write at maximum precision */
-
		utstring_printf (buf, "%.*lg", DBL_DIG, val);
-
	}
-
	else {
-
		utstring_printf (buf, "%lf", val);
-
	}
-

-
	return 0;
-
}
-

+
#define UCL_EMIT_TYPE_IMPL(type, compact)		\
+
	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj, bool first, bool print_key) {	\
+
		ucl_emitter_common_elt (ctx, obj, first, print_key, (compact));	\
+
	}	\
+
	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj, bool print_key) {	\
+
		ucl_emitter_common_start_object (ctx, obj, print_key, (compact));	\
+
	}	\
+
	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj, bool print_key) {	\
+
		ucl_emitter_common_start_array (ctx, obj, print_key, (compact));	\
+
	}	\
+
	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj) {	\
+
		ucl_emitter_common_end_object (ctx, obj, (compact));	\
+
	}	\
+
	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
+
		const ucl_object_t *obj) {	\
+
		ucl_emitter_common_end_array (ctx, obj, (compact));	\
+
	}
+

+
UCL_EMIT_TYPE_IMPL(json, false);
+
UCL_EMIT_TYPE_IMPL(json_compact, true);
+
UCL_EMIT_TYPE_IMPL(config, false);
+
UCL_EMIT_TYPE_IMPL(yaml, false);

unsigned char *
ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
{
-
	UT_string *buf = NULL;
	unsigned char *res = NULL;
-
	struct ucl_emitter_functions func = {
-
		.ucl_emitter_append_character = ucl_utstring_append_character,
-
		.ucl_emitter_append_len = ucl_utstring_append_len,
-
		.ucl_emitter_append_int = ucl_utstring_append_int,
-
		.ucl_emitter_append_double = ucl_utstring_append_double
-
	};
-

+
	struct ucl_emitter_functions *func;
	if (obj == NULL) {
		return NULL;
	}

-
	utstring_new (buf);
-
	func.ud = buf;
-

-
	if (buf != NULL) {
-
		if (emit_type == UCL_EMIT_JSON) {
-
			ucl_object_emit_json (obj, false, &func);
-
		}
-
		else if (emit_type == UCL_EMIT_JSON_COMPACT) {
-
			ucl_object_emit_json (obj, true, &func);
-
		}
-
		else if (emit_type == UCL_EMIT_YAML) {
-
			ucl_object_emit_yaml (obj, &func);
-
		}
-
		else {
-
			ucl_object_emit_config (obj, &func);
-
		}
+
	func = ucl_object_emit_memory_funcs ((void **)&res);

-
		res = utstring_body (buf);
-
		free (buf);
+
	if (func != NULL) {
+
		ucl_object_emit_full (obj, emit_type, func);
+
		ucl_object_emit_funcs_free (func);
	}

	return res;
@@ -767,71 +453,19 @@ bool
ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
		struct ucl_emitter_functions *emitter)
{
-
	if (emit_type == UCL_EMIT_JSON) {
-
		ucl_object_emit_json (obj, false, emitter);
-
	}
-
	else if (emit_type == UCL_EMIT_JSON_COMPACT) {
-
		ucl_object_emit_json (obj, true, emitter);
-
	}
-
	else if (emit_type == UCL_EMIT_YAML) {
-
		ucl_object_emit_yaml (obj, emitter);
-
	}
-
	else {
-
		ucl_object_emit_config (obj, emitter);
-
	}
-

-
	/* XXX: need some error checks here */
-
	return true;
-
}
-

-

-
unsigned char *
-
ucl_object_emit_single_json (const 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);
+
	const struct ucl_emitter_context *ctx;
+
	struct ucl_emitter_context my_ctx;
+
	bool res = false;
+

+
	ctx = ucl_emit_get_standard_context (emit_type);
+
	if (ctx != NULL) {
+
		memcpy (&my_ctx, ctx, sizeof (my_ctx));
+
		my_ctx.func = emitter;
+
		my_ctx.ident = 0;
+
		my_ctx.top = obj;
+

+
		my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
+
		res = true;
	}

	return res;
modified external/libucl/src/ucl_internal.h
@@ -343,6 +343,22 @@ ucl_hash_insert_object (ucl_hash_t *hashlin, const ucl_object_t *obj)
}

/**
+
 * Get standard emitter context for a specified emit_type
+
 * @param emit_type type of emitter
+
 * @return context or NULL if input is invalid
+
 */
+
const struct ucl_emitter_context *
+
ucl_emit_get_standard_context (enum ucl_emitter emit_type);
+

+
/**
+
 * Serialise string
+
 * @param str string to emit
+
 * @param buf target buffer
+
 */
+
void ucl_elt_string_write_json (const char *str, size_t size,
+
		struct ucl_emitter_context *ctx);
+

+
/**
 * Emit a single object to string
 * @param obj
 * @return
modified external/libucl/src/ucl_parser.c
@@ -393,9 +393,16 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,

		/* Leave variable as is */
		if (!found) {
-
			memcpy (d, ptr, 2);
-
			d += 2;
-
			ret --;
+
			if (strict) {
+
				/* Copy '${' */
+
				memcpy (d, ptr, 2);
+
				d += 2;
+
				ret --;
+
			}
+
			else {
+
				memcpy (d, ptr, 1);
+
				d ++;
+
			}
		}
	}

modified external/libucl/uthash/utstring.h
@@ -44,6 +44,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

typedef struct {
    char *d;
+
    void **pd;
    size_t n; /* allocd size */
    size_t i; /* index of first unused byte */
} UT_string;
@@ -54,6 +55,7 @@ do { \
     (s)->d = (char*)realloc((s)->d, (s)->n + amt);        \
     if ((s)->d == NULL) oom();                            \
     (s)->n += amt;                                        \
+
     if ((s)->pd) *((s)->pd) = (s)->d;                     \
  }                                                        \
} while(0)

@@ -78,7 +80,7 @@ do { \

#define utstring_new(s)                                    \
do {                                                       \
-
   s = (UT_string*)calloc(sizeof(UT_string),1);            \
+
   s = (UT_string*)calloc(1, sizeof(UT_string));          \
   if (!s) oom();                                          \
   utstring_init(s);                                       \
} while(0)