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 && ucl_maybe_long_string (obj)) {
428			ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
429		}
430		else {
431			ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
432		}
433		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
434		break;
435	case UCL_NULL:
436		ucl_emitter_print_key (print_key, ctx, obj, compact);
437		func->ucl_emitter_append_len ("null", 4, func->ud);
438		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
439		break;
440	case UCL_OBJECT:
441		ucl_emitter_common_start_object (ctx, obj, print_key, compact);
442		ucl_emitter_common_end_object (ctx, obj, compact);
443		break;
444	case UCL_ARRAY:
445		ucl_emitter_common_start_array (ctx, obj, print_key, compact);
446		ucl_emitter_common_end_array (ctx, obj, compact);
447		break;
448	case UCL_USERDATA:
449		ud = (struct ucl_object_userdata *)obj;
450		ucl_emitter_print_key (print_key, ctx, obj, compact);
451		if (ud->emitter) {
452			ud_out = ud->emitter (obj->value.ud);
453			if (ud_out == NULL) {
454				ud_out = "null";
455			}
456		}
457		ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx);
458		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
459		break;
460	}
461
462	if (comment) {
463		DL_FOREACH (comment, cur_comment) {
464			func->ucl_emitter_append_len (cur_comment->value.sv,
465					cur_comment->len,
466					func->ud);
467			func->ucl_emitter_append_character ('\n', 1, func->ud);
468
469			if (cur_comment->next) {
470				ucl_add_tabs (func, ctx->indent, compact);
471			}
472		}
473	}
474}
475
476/*
477 * Specific standard implementations of the emitter functions
478 */
479#define UCL_EMIT_TYPE_IMPL(type, compact)		\
480	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
481		const ucl_object_t *obj, bool first, bool print_key) {	\
482		ucl_emitter_common_elt (ctx, obj, first, print_key, (compact));	\
483	}	\
484	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
485		const ucl_object_t *obj, bool print_key) {	\
486		ucl_emitter_common_start_object (ctx, obj, print_key, (compact));	\
487	}	\
488	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
489		const ucl_object_t *obj, bool print_key) {	\
490		ucl_emitter_common_start_array (ctx, obj, print_key, (compact));	\
491	}	\
492	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
493		const ucl_object_t *obj) {	\
494		ucl_emitter_common_end_object (ctx, obj, (compact));	\
495	}	\
496	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
497		const ucl_object_t *obj) {	\
498		ucl_emitter_common_end_array (ctx, obj, (compact));	\
499	}
500
501UCL_EMIT_TYPE_IMPL(json, false)
502UCL_EMIT_TYPE_IMPL(json_compact, true)
503UCL_EMIT_TYPE_IMPL(config, false)
504UCL_EMIT_TYPE_IMPL(yaml, false)
505
506static void
507ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
508		const ucl_object_t *obj, bool first, bool print_key)
509{
510	ucl_object_iter_t it;
511	struct ucl_object_userdata *ud;
512	const char *ud_out;
513	const ucl_object_t *cur, *celt;
514
515	switch (obj->type) {
516	case UCL_INT:
517		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
518		ucl_emitter_print_int_msgpack (ctx, ucl_object_toint (obj));
519		break;
520
521	case UCL_FLOAT:
522	case UCL_TIME:
523		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
524		ucl_emitter_print_double_msgpack (ctx, ucl_object_todouble (obj));
525		break;
526
527	case UCL_BOOLEAN:
528		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
529		ucl_emitter_print_bool_msgpack (ctx, ucl_object_toboolean (obj));
530		break;
531
532	case UCL_STRING:
533		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
534
535		if (obj->flags & UCL_OBJECT_BINARY) {
536			ucl_emitter_print_binary_string_msgpack (ctx, obj->value.sv,
537					obj->len);
538		}
539		else {
540			ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
541		}
542		break;
543
544	case UCL_NULL:
545		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
546		ucl_emitter_print_null_msgpack (ctx);
547		break;
548
549	case UCL_OBJECT:
550		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
551		ucl_emit_msgpack_start_obj (ctx, obj, print_key);
552		it = NULL;
553
554		while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
555			LL_FOREACH (cur, celt) {
556				ucl_emit_msgpack_elt (ctx, celt, false, true);
557				/* XXX:
558				 * in msgpack the length of objects is encoded within a single elt
559				 * so in case of multi-value keys we are using merely the first
560				 * element ignoring others
561				 */
562				break;
563			}
564		}
565
566		break;
567
568	case UCL_ARRAY:
569		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
570		ucl_emit_msgpack_start_array (ctx, obj, print_key);
571		it = NULL;
572
573		while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
574			ucl_emit_msgpack_elt (ctx, cur, false, false);
575		}
576
577		break;
578
579	case UCL_USERDATA:
580		ud = (struct ucl_object_userdata *)obj;
581		ucl_emitter_print_key_msgpack (print_key, ctx, obj);
582
583		if (ud->emitter) {
584			ud_out = ud->emitter (obj->value.ud);
585			if (ud_out == NULL) {
586				ud_out = "null";
587			}
588		}
589		ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
590		break;
591	}
592}
593
594static void
595ucl_emit_msgpack_start_obj (struct ucl_emitter_context *ctx,
596		const ucl_object_t *obj, bool print_key)
597{
598	ucl_emitter_print_object_msgpack (ctx, obj->len);
599}
600
601static void
602ucl_emit_msgpack_start_array (struct ucl_emitter_context *ctx,
603		const ucl_object_t *obj, bool print_key)
604{
605	ucl_emitter_print_array_msgpack (ctx, obj->len);
606}
607
608static void
609ucl_emit_msgpack_end_object (struct ucl_emitter_context *ctx,
610		const ucl_object_t *obj)
611{
612
613}
614
615static void
616ucl_emit_msgpack_end_array (struct ucl_emitter_context *ctx,
617		const ucl_object_t *obj)
618{
619
620}
621
622unsigned char *
623ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
624{
625	return ucl_object_emit_len (obj, emit_type, NULL);
626}
627
628unsigned char *
629ucl_object_emit_len (const ucl_object_t *obj, enum ucl_emitter emit_type,
630		size_t *outlen)
631{
632	unsigned char *res = NULL;
633	struct ucl_emitter_functions *func;
634	UT_string *s;
635
636	if (obj == NULL) {
637		return NULL;
638	}
639
640	func = ucl_object_emit_memory_funcs ((void **)&res);
641
642	if (func != NULL) {
643		s = func->ud;
644		ucl_object_emit_full (obj, emit_type, func, NULL);
645
646		if (outlen != NULL) {
647			*outlen = s->i;
648		}
649
650		ucl_object_emit_funcs_free (func);
651	}
652
653	return res;
654}
655
656bool
657ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
658		struct ucl_emitter_functions *emitter,
659		const ucl_object_t *comments)
660{
661	const struct ucl_emitter_context *ctx;
662	struct ucl_emitter_context my_ctx;
663	bool res = false;
664
665	ctx = ucl_emit_get_standard_context (emit_type);
666	if (ctx != NULL) {
667		memcpy (&my_ctx, ctx, sizeof (my_ctx));
668		my_ctx.func = emitter;
669		my_ctx.indent = 0;
670		my_ctx.top = obj;
671		my_ctx.comments = comments;
672
673		my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
674		res = true;
675	}
676
677	return res;
678}
679