diff options
Diffstat (limited to 'src/ucl_emitter.c')
| -rw-r--r-- | src/ucl_emitter.c | 922 |
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; |
