summaryrefslogtreecommitdiff
path: root/src/ucl_emitter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ucl_emitter.c')
-rw-r--r--src/ucl_emitter.c922
1 files changed, 278 insertions, 644 deletions
diff --git a/src/ucl_emitter.c b/src/ucl_emitter.c
index 04c3d4b13232..11fe50c00602 100644
--- a/src/ucl_emitter.c
+++ b/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;
+ const struct ucl_emitter_functions *func = ctx->func;
- 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;
+ 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);
-}
+ const struct ucl_emitter_functions *func = ctx->func;
-/**
- * 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;
-
- 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);
- }
-
- 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);
-}
+ const ucl_object_t *cur;
+ const struct ucl_emitter_functions *func = ctx->func;
+ bool first = true;
-/**
- * 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;
+ ucl_emitter_print_key (print_key, ctx, obj, compact);
- 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;
+ const ucl_object_t *cur, *elt;
+ const struct ucl_emitter_functions *func = ctx->func;
+ bool first = true;
- if (start_tabs) {
- ucl_add_tabs (func, tabs, is_top);
- }
- if (!is_top) {
- func->ucl_emitter_append_len ("{\n", 2, func->ud);
+ 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';
+#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)); \
}
- 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;
-}
-
+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;
+ func = ucl_object_emit_memory_funcs ((void **)&res);
- 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);
- }
-
- 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;
- }
+ const struct ucl_emitter_context *ctx;
+ struct ucl_emitter_context my_ctx;
+ bool res = false;
- utstring_new (buf);
+ 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;
- 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);
+ my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
+ res = true;
}
return res;