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
43static 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 */
61UCL_EMIT_TYPE_OPS(json);
62UCL_EMIT_TYPE_OPS(json_compact);
63UCL_EMIT_TYPE_OPS(config);
64UCL_EMIT_TYPE_OPS(yaml);
65UCL_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
75const 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 */
95static inline void
96ucl_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 */
109static void
110ucl_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
164static void
165ucl_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 */
192static void
193ucl_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 */
221static void
222ucl_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 */
249static void
250ucl_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 */
295static void
296ucl_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 */
358static void
359ucl_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
509UCL_EMIT_TYPE_IMPL(json, false)
510UCL_EMIT_TYPE_IMPL(json_compact, true)
511UCL_EMIT_TYPE_IMPL(config, false)
512UCL_EMIT_TYPE_IMPL(yaml, false)
513
514static void
515ucl_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
602static void
603ucl_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
609static void
610ucl_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
616static void
617ucl_emit_msgpack_end_object (struct ucl_emitter_context *ctx,
618		const ucl_object_t *obj)
619{
620
621}
622
623static void
624ucl_emit_msgpack_end_array (struct ucl_emitter_context *ctx,
625		const ucl_object_t *obj)
626{
627
628}
629
630unsigned char *
631ucl_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
636unsigned char *
637ucl_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
664bool
665ucl_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