xref: /freebsd/contrib/libucl/src/ucl_emitter.c (revision 401ab69cff8fa2320a9f8ea4baa114a6da6c952b)
1 /* Copyright (c) 2013, Vsevolod Stakhov
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *       * Redistributions of source code must retain the above copyright
7  *         notice, this list of conditions and the following disclaimer.
8  *       * Redistributions in binary form must reproduce the above copyright
9  *         notice, this list of conditions and the following disclaimer in the
10  *         documentation and/or other materials provided with the distribution.
11  *
12  * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15  * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "ucl.h"
29 #include "ucl_internal.h"
30 #include "ucl_chartable.h"
31 #ifdef HAVE_FLOAT_H
32 #include <float.h>
33 #endif
34 #ifdef HAVE_MATH_H
35 #include <math.h>
36 #endif
37 
38 /**
39  * @file ucl_emitter.c
40  * Serialise UCL object to various of output formats
41  */
42 
43 static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
44 		const ucl_object_t *obj, bool first, bool print_key, bool compact);
45 
46 #define UCL_EMIT_TYPE_OPS(type)		\
47 	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
48 		const ucl_object_t *obj, bool first, bool print_key);	\
49 	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
50 		const ucl_object_t *obj, bool print_key);	\
51 	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
52 		const ucl_object_t *obj, bool print_key);	\
53 	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
54 		const ucl_object_t *obj);	\
55 	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
56 		const ucl_object_t *obj)
57 
58 /*
59  * JSON format operations
60  */
61 UCL_EMIT_TYPE_OPS(json);
62 UCL_EMIT_TYPE_OPS(json_compact);
63 UCL_EMIT_TYPE_OPS(config);
64 UCL_EMIT_TYPE_OPS(yaml);
65 UCL_EMIT_TYPE_OPS(msgpack);
66 
67 #define UCL_EMIT_TYPE_CONTENT(type) {	\
68 	.ucl_emitter_write_elt = ucl_emit_ ## type ## _elt,	\
69 	.ucl_emitter_start_object = ucl_emit_ ## type ##_start_obj,	\
70 	.ucl_emitter_start_array = ucl_emit_ ## type ##_start_array,	\
71 	.ucl_emitter_end_object = ucl_emit_ ## type ##_end_object,	\
72 	.ucl_emitter_end_array = ucl_emit_ ## type ##_end_array	\
73 }
74 
75 const struct ucl_emitter_operations ucl_standartd_emitter_ops[] = {
76 	[UCL_EMIT_JSON] = UCL_EMIT_TYPE_CONTENT(json),
77 	[UCL_EMIT_JSON_COMPACT] = UCL_EMIT_TYPE_CONTENT(json_compact),
78 	[UCL_EMIT_CONFIG] = UCL_EMIT_TYPE_CONTENT(config),
79 	[UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml),
80 	[UCL_EMIT_MSGPACK] = UCL_EMIT_TYPE_CONTENT(msgpack)
81 };
82 
83 /*
84  * Utility to check whether we need a top object
85  */
86 #define UCL_EMIT_IDENT_TOP_OBJ(ctx, obj) ((ctx)->top != (obj) || \
87 		((ctx)->id == UCL_EMIT_JSON_COMPACT || (ctx)->id == UCL_EMIT_JSON))
88 
89 
90 /**
91  * Add tabulation to the output buffer
92  * @param buf target buffer
93  * @param tabs number of tabs to add
94  */
95 static inline void
96 ucl_add_tabs (const struct ucl_emitter_functions *func, unsigned int tabs,
97 		bool compact)
98 {
99 	if (!compact && tabs > 0) {
100 		func->ucl_emitter_append_character (' ', tabs * 4, func->ud);
101 	}
102 }
103 
104 /**
105  * Print key for the element
106  * @param ctx
107  * @param obj
108  */
109 static void
110 ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx,
111 		const ucl_object_t *obj, bool compact)
112 {
113 	const struct ucl_emitter_functions *func = ctx->func;
114 
115 	if (!print_key) {
116 		return;
117 	}
118 
119 	if (ctx->id == UCL_EMIT_CONFIG) {
120 		if (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
121 			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
122 		}
123 		else {
124 			func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
125 		}
126 
127 		if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
128 			func->ucl_emitter_append_len (" = ", 3, func->ud);
129 		}
130 		else {
131 			func->ucl_emitter_append_character (' ', 1, func->ud);
132 		}
133 	}
134 	else if (ctx->id == UCL_EMIT_YAML) {
135 		if (obj->keylen > 0 && (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE)) {
136 			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
137 		}
138 		else if (obj->keylen > 0) {
139 			func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
140 		}
141 		else {
142 			func->ucl_emitter_append_len ("null", 4, func->ud);
143 		}
144 
145 		func->ucl_emitter_append_len (": ", 2, func->ud);
146 	}
147 	else {
148 		if (obj->keylen > 0) {
149 			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
150 		}
151 		else {
152 			func->ucl_emitter_append_len ("null", 4, func->ud);
153 		}
154 
155 		if (compact) {
156 			func->ucl_emitter_append_character (':', 1, func->ud);
157 		}
158 		else {
159 			func->ucl_emitter_append_len (": ", 2, func->ud);
160 		}
161 	}
162 }
163 
164 static void
165 ucl_emitter_finish_object (struct ucl_emitter_context *ctx,
166 		const ucl_object_t *obj, bool compact, bool is_array)
167 {
168 	const struct ucl_emitter_functions *func = ctx->func;
169 
170 	if (ctx->id == UCL_EMIT_CONFIG && obj != ctx->top) {
171 		if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
172 			if (!is_array) {
173 				/* Objects are split by ';' */
174 				func->ucl_emitter_append_len (";\n", 2, func->ud);
175 			}
176 			else {
177 				/* Use commas for arrays */
178 				func->ucl_emitter_append_len (",\n", 2, func->ud);
179 			}
180 		}
181 		else {
182 			func->ucl_emitter_append_character ('\n', 1, func->ud);
183 		}
184 	}
185 }
186 
187 /**
188  * End standard ucl object
189  * @param ctx emitter context
190  * @param compact compact flag
191  */
192 static void
193 ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
194 		const ucl_object_t *obj, bool compact)
195 {
196 	const struct ucl_emitter_functions *func = ctx->func;
197 
198 	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
199 		ctx->indent --;
200 		if (compact) {
201 			func->ucl_emitter_append_character ('}', 1, func->ud);
202 		}
203 		else {
204 			if (ctx->id != UCL_EMIT_CONFIG) {
205 				/* newline is already added for this format */
206 				func->ucl_emitter_append_character ('\n', 1, func->ud);
207 			}
208 			ucl_add_tabs (func, ctx->indent, compact);
209 			func->ucl_emitter_append_character ('}', 1, func->ud);
210 		}
211 	}
212 
213 	ucl_emitter_finish_object (ctx, obj, compact, false);
214 }
215 
216 /**
217  * End standard ucl array
218  * @param ctx emitter context
219  * @param compact compact flag
220  */
221 static void
222 ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
223 		const ucl_object_t *obj, bool compact)
224 {
225 	const struct ucl_emitter_functions *func = ctx->func;
226 
227 	ctx->indent --;
228 	if (compact) {
229 		func->ucl_emitter_append_character (']', 1, func->ud);
230 	}
231 	else {
232 		if (ctx->id != UCL_EMIT_CONFIG) {
233 			/* newline is already added for this format */
234 			func->ucl_emitter_append_character ('\n', 1, func->ud);
235 		}
236 		ucl_add_tabs (func, ctx->indent, compact);
237 		func->ucl_emitter_append_character (']', 1, func->ud);
238 	}
239 
240 	ucl_emitter_finish_object (ctx, obj, compact, true);
241 }
242 
243 /**
244  * Start emit standard UCL array
245  * @param ctx emitter context
246  * @param obj object to write
247  * @param compact compact flag
248  */
249 static void
250 ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
251 		const ucl_object_t *obj, bool print_key, bool compact)
252 {
253 	const ucl_object_t *cur;
254 	ucl_object_iter_t iter = NULL;
255 	const struct ucl_emitter_functions *func = ctx->func;
256 	bool first = true;
257 
258 	ucl_emitter_print_key (print_key, ctx, obj, compact);
259 
260 	if (compact) {
261 		func->ucl_emitter_append_character ('[', 1, func->ud);
262 	}
263 	else {
264 		func->ucl_emitter_append_len ("[\n", 2, func->ud);
265 	}
266 
267 	ctx->indent ++;
268 
269 	if (obj->type == UCL_ARRAY) {
270 		/* explicit array */
271 		while ((cur = ucl_object_iterate (obj, &iter, true)) != NULL) {
272 			ucl_emitter_common_elt (ctx, cur, first, false, compact);
273 			first = false;
274 		}
275 	}
276 	else {
277 		/* implicit array */
278 		cur = obj;
279 		while (cur) {
280 			ucl_emitter_common_elt (ctx, cur, first, false, compact);
281 			first = false;
282 			cur = cur->next;
283 		}
284 	}
285 
286 
287 }
288 
289 /**
290  * Start emit standard UCL object
291  * @param ctx emitter context
292  * @param obj object to write
293  * @param compact compact flag
294  */
295 static void
296 ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
297 		const ucl_object_t *obj, bool print_key, bool compact)
298 {
299 	ucl_hash_iter_t it = NULL;
300 	const ucl_object_t *cur, *elt;
301 	const struct ucl_emitter_functions *func = ctx->func;
302 	bool first = true;
303 
304 	ucl_emitter_print_key (print_key, ctx, obj, compact);
305 	/*
306 	 * Print <ident_level>{
307 	 * <ident_level + 1><object content>
308 	 */
309 	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
310 		if (compact) {
311 			func->ucl_emitter_append_character ('{', 1, func->ud);
312 		}
313 		else {
314 			func->ucl_emitter_append_len ("{\n", 2, func->ud);
315 		}
316 		ctx->indent ++;
317 	}
318 
319 	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
320 
321 		if (ctx->id == UCL_EMIT_CONFIG) {
322 			LL_FOREACH (cur, elt) {
323 				ucl_emitter_common_elt (ctx, elt, first, true, compact);
324 			}
325 		}
326 		else {
327 			/* Expand implicit arrays */
328 			if (cur->next != NULL) {
329 				if (!first) {
330 					if (compact) {
331 						func->ucl_emitter_append_character (',', 1, func->ud);
332 					}
333 					else {
334 						func->ucl_emitter_append_len (",\n", 2, func->ud);
335 					}
336 				}
337 				ucl_add_tabs (func, ctx->indent, compact);
338 				ucl_emitter_common_start_array (ctx, cur, true, compact);
339 				ucl_emitter_common_end_array (ctx, cur, compact);
340 			}
341 			else {
342 				ucl_emitter_common_elt (ctx, cur, first, true, compact);
343 			}
344 		}
345 
346 		first = false;
347 	}
348 }
349 
350 /**
351  * Common choice of object emitting
352  * @param ctx emitter context
353  * @param obj object to print
354  * @param first flag to mark the first element
355  * @param print_key print key of an object
356  * @param compact compact output
357  */
358 static void
359 ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
360 		const ucl_object_t *obj, bool first, bool print_key, bool compact)
361 {
362 	const struct ucl_emitter_functions *func = ctx->func;
363 	bool flag;
364 	struct ucl_object_userdata *ud;
365 	const ucl_object_t *comment = NULL, *cur_comment;
366 	const char *ud_out = "";
367 
368 	if (ctx->id != UCL_EMIT_CONFIG && !first) {
369 		if (compact) {
370 			func->ucl_emitter_append_character (',', 1, func->ud);
371 		}
372 		else {
373 			if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
374 				func->ucl_emitter_append_len ("\n", 1, func->ud);
375 			} else {
376 				func->ucl_emitter_append_len (",\n", 2, func->ud);
377 			}
378 		}
379 	}
380 
381 	ucl_add_tabs (func, ctx->indent, compact);
382 
383 	if (ctx->comments && ctx->id == UCL_EMIT_CONFIG) {
384 		comment = ucl_object_lookup_len (ctx->comments, (const char *)&obj,
385 				sizeof (void *));
386 
387 		if (comment) {
388 			if (!(comment->flags & UCL_OBJECT_INHERITED)) {
389 				DL_FOREACH (comment, cur_comment) {
390 					func->ucl_emitter_append_len (cur_comment->value.sv,
391 							cur_comment->len,
392 							func->ud);
393 					func->ucl_emitter_append_character ('\n', 1, func->ud);
394 					ucl_add_tabs (func, ctx->indent, compact);
395 				}
396 
397 				comment = NULL;
398 			}
399 		}
400 	}
401 
402 	switch (obj->type) {
403 	case UCL_INT:
404 		ucl_emitter_print_key (print_key, ctx, obj, compact);
405 		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
406 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
407 		break;
408 	case UCL_FLOAT:
409 	case UCL_TIME:
410 		ucl_emitter_print_key (print_key, ctx, obj, compact);
411 		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
412 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
413 		break;
414 	case UCL_BOOLEAN:
415 		ucl_emitter_print_key (print_key, ctx, obj, compact);
416 		flag = ucl_object_toboolean (obj);
417 		if (flag) {
418 			func->ucl_emitter_append_len ("true", 4, func->ud);
419 		}
420 		else {
421 			func->ucl_emitter_append_len ("false", 5, func->ud);
422 		}
423 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
424 		break;
425 	case UCL_STRING:
426 		ucl_emitter_print_key (print_key, ctx, obj, compact);
427 		if (ctx->id == UCL_EMIT_CONFIG) {
428 			if (ucl_maybe_long_string (obj)) {
429 				ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
430 			} else {
431 				if (obj->flags & UCL_OBJECT_SQUOTED) {
432 					ucl_elt_string_write_squoted (obj->value.sv, obj->len, ctx);
433 				} else {
434 					ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
435 				}
436 			}
437 		}
438 		else {
439 			ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
440 		}
441 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
442 		break;
443 	case UCL_NULL:
444 		ucl_emitter_print_key (print_key, ctx, obj, compact);
445 		func->ucl_emitter_append_len ("null", 4, func->ud);
446 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
447 		break;
448 	case UCL_OBJECT:
449 		ucl_emitter_common_start_object (ctx, obj, print_key, compact);
450 		ucl_emitter_common_end_object (ctx, obj, compact);
451 		break;
452 	case UCL_ARRAY:
453 		ucl_emitter_common_start_array (ctx, obj, print_key, compact);
454 		ucl_emitter_common_end_array (ctx, obj, compact);
455 		break;
456 	case UCL_USERDATA:
457 		ud = (struct ucl_object_userdata *)obj;
458 		ucl_emitter_print_key (print_key, ctx, obj, compact);
459 		if (ud->emitter) {
460 			ud_out = ud->emitter (obj->value.ud);
461 			if (ud_out == NULL) {
462 				ud_out = "null";
463 			}
464 		}
465 		ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx);
466 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
467 		break;
468 	}
469 
470 	if (comment) {
471 		DL_FOREACH (comment, cur_comment) {
472 			func->ucl_emitter_append_len (cur_comment->value.sv,
473 					cur_comment->len,
474 					func->ud);
475 			func->ucl_emitter_append_character ('\n', 1, func->ud);
476 
477 			if (cur_comment->next) {
478 				ucl_add_tabs (func, ctx->indent, compact);
479 			}
480 		}
481 	}
482 }
483 
484 /*
485  * Specific standard implementations of the emitter functions
486  */
487 #define UCL_EMIT_TYPE_IMPL(type, compact)		\
488 	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
489 		const ucl_object_t *obj, bool first, bool print_key) {	\
490 		ucl_emitter_common_elt (ctx, obj, first, print_key, (compact));	\
491 	}	\
492 	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
493 		const ucl_object_t *obj, bool print_key) {	\
494 		ucl_emitter_common_start_object (ctx, obj, print_key, (compact));	\
495 	}	\
496 	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
497 		const ucl_object_t *obj, bool print_key) {	\
498 		ucl_emitter_common_start_array (ctx, obj, print_key, (compact));	\
499 	}	\
500 	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
501 		const ucl_object_t *obj) {	\
502 		ucl_emitter_common_end_object (ctx, obj, (compact));	\
503 	}	\
504 	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
505 		const ucl_object_t *obj) {	\
506 		ucl_emitter_common_end_array (ctx, obj, (compact));	\
507 	}
508 
509 UCL_EMIT_TYPE_IMPL(json, false)
510 UCL_EMIT_TYPE_IMPL(json_compact, true)
511 UCL_EMIT_TYPE_IMPL(config, false)
512 UCL_EMIT_TYPE_IMPL(yaml, false)
513 
514 static void
515 ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
516 		const ucl_object_t *obj, bool first, bool print_key)
517 {
518 	ucl_object_iter_t it;
519 	struct ucl_object_userdata *ud;
520 	const char *ud_out;
521 	const ucl_object_t *cur, *celt;
522 
523 	switch (obj->type) {
524 	case UCL_INT:
525 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
526 		ucl_emitter_print_int_msgpack (ctx, ucl_object_toint (obj));
527 		break;
528 
529 	case UCL_FLOAT:
530 	case UCL_TIME:
531 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
532 		ucl_emitter_print_double_msgpack (ctx, ucl_object_todouble (obj));
533 		break;
534 
535 	case UCL_BOOLEAN:
536 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
537 		ucl_emitter_print_bool_msgpack (ctx, ucl_object_toboolean (obj));
538 		break;
539 
540 	case UCL_STRING:
541 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
542 
543 		if (obj->flags & UCL_OBJECT_BINARY) {
544 			ucl_emitter_print_binary_string_msgpack (ctx, obj->value.sv,
545 					obj->len);
546 		}
547 		else {
548 			ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
549 		}
550 		break;
551 
552 	case UCL_NULL:
553 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
554 		ucl_emitter_print_null_msgpack (ctx);
555 		break;
556 
557 	case UCL_OBJECT:
558 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
559 		ucl_emit_msgpack_start_obj (ctx, obj, print_key);
560 		it = NULL;
561 
562 		while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
563 			LL_FOREACH (cur, celt) {
564 				ucl_emit_msgpack_elt (ctx, celt, false, true);
565 				/* XXX:
566 				 * in msgpack the length of objects is encoded within a single elt
567 				 * so in case of multi-value keys we are using merely the first
568 				 * element ignoring others
569 				 */
570 				break;
571 			}
572 		}
573 
574 		break;
575 
576 	case UCL_ARRAY:
577 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
578 		ucl_emit_msgpack_start_array (ctx, obj, print_key);
579 		it = NULL;
580 
581 		while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
582 			ucl_emit_msgpack_elt (ctx, cur, false, false);
583 		}
584 
585 		break;
586 
587 	case UCL_USERDATA:
588 		ud = (struct ucl_object_userdata *)obj;
589 		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
590 
591 		if (ud->emitter) {
592 			ud_out = ud->emitter (obj->value.ud);
593 			if (ud_out == NULL) {
594 				ud_out = "null";
595 			}
596 		}
597 		ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
598 		break;
599 	}
600 }
601 
602 static void
603 ucl_emit_msgpack_start_obj (struct ucl_emitter_context *ctx,
604 		const ucl_object_t *obj, bool print_key)
605 {
606 	ucl_emitter_print_object_msgpack (ctx, obj->len);
607 }
608 
609 static void
610 ucl_emit_msgpack_start_array (struct ucl_emitter_context *ctx,
611 		const ucl_object_t *obj, bool print_key)
612 {
613 	ucl_emitter_print_array_msgpack (ctx, obj->len);
614 }
615 
616 static void
617 ucl_emit_msgpack_end_object (struct ucl_emitter_context *ctx,
618 		const ucl_object_t *obj)
619 {
620 
621 }
622 
623 static void
624 ucl_emit_msgpack_end_array (struct ucl_emitter_context *ctx,
625 		const ucl_object_t *obj)
626 {
627 
628 }
629 
630 unsigned char *
631 ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
632 {
633 	return ucl_object_emit_len (obj, emit_type, NULL);
634 }
635 
636 unsigned char *
637 ucl_object_emit_len (const ucl_object_t *obj, enum ucl_emitter emit_type,
638 		size_t *outlen)
639 {
640 	unsigned char *res = NULL;
641 	struct ucl_emitter_functions *func;
642 	UT_string *s;
643 
644 	if (obj == NULL) {
645 		return NULL;
646 	}
647 
648 	func = ucl_object_emit_memory_funcs ((void **)&res);
649 
650 	if (func != NULL) {
651 		s = func->ud;
652 		ucl_object_emit_full (obj, emit_type, func, NULL);
653 
654 		if (outlen != NULL) {
655 			*outlen = s->i;
656 		}
657 
658 		ucl_object_emit_funcs_free (func);
659 	}
660 
661 	return res;
662 }
663 
664 bool
665 ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
666 		struct ucl_emitter_functions *emitter,
667 		const ucl_object_t *comments)
668 {
669 	const struct ucl_emitter_context *ctx;
670 	struct ucl_emitter_context my_ctx;
671 	bool res = false;
672 
673 	ctx = ucl_emit_get_standard_context (emit_type);
674 	if (ctx != NULL) {
675 		memcpy (&my_ctx, ctx, sizeof (my_ctx));
676 		my_ctx.func = emitter;
677 		my_ctx.indent = 0;
678 		my_ctx.top = obj;
679 		my_ctx.comments = comments;
680 
681 		my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
682 		res = true;
683 	}
684 
685 	return res;
686 }
687