ucl_emitter.c revision 268896
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 rcl_emitter.c
40 * Serialise UCL object to various of output formats
41 */
42
43
44static void ucl_obj_write_json (const ucl_object_t *obj,
45		struct ucl_emitter_functions *func,
46		unsigned int tabs,
47		bool start_tabs,
48		bool compact);
49static void ucl_elt_write_json (const ucl_object_t *obj,
50		struct ucl_emitter_functions *func,
51		unsigned int tabs,
52		bool start_tabs,
53		bool compact);
54static void ucl_elt_write_config (const ucl_object_t *obj,
55		struct ucl_emitter_functions *func,
56		unsigned int tabs,
57		bool start_tabs,
58		bool is_top,
59		bool expand_array);
60static void ucl_elt_write_yaml (const ucl_object_t *obj,
61		struct ucl_emitter_functions *func,
62		unsigned int tabs,
63		bool start_tabs,
64		bool compact,
65		bool expand_array);
66static void ucl_elt_array_write_yaml (const ucl_object_t *obj,
67		struct ucl_emitter_functions *func,
68		unsigned int tabs,
69		bool start_tabs,
70		bool is_top);
71
72/**
73 * Add tabulation to the output buffer
74 * @param buf target buffer
75 * @param tabs number of tabs to add
76 */
77static inline void
78ucl_add_tabs (struct ucl_emitter_functions *func, unsigned int tabs, bool compact)
79{
80	if (!compact) {
81		func->ucl_emitter_append_character (' ', tabs * 4, func->ud);
82	}
83}
84
85/**
86 * Serialise string
87 * @param str string to emit
88 * @param buf target buffer
89 */
90static void
91ucl_elt_string_write_json (const char *str, size_t size,
92		struct ucl_emitter_functions *func)
93{
94	const char *p = str, *c = str;
95	size_t len = 0;
96
97	func->ucl_emitter_append_character ('"', 1, func->ud);
98	while (size) {
99		if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
100			if (len > 0) {
101				func->ucl_emitter_append_len (c, len, func->ud);
102			}
103			switch (*p) {
104			case '\n':
105				func->ucl_emitter_append_len ("\\n", 2, func->ud);
106				break;
107			case '\r':
108				func->ucl_emitter_append_len ("\\r", 2, func->ud);
109				break;
110			case '\b':
111				func->ucl_emitter_append_len ("\\b", 2, func->ud);
112				break;
113			case '\t':
114				func->ucl_emitter_append_len ("\\t", 2, func->ud);
115				break;
116			case '\f':
117				func->ucl_emitter_append_len ("\\f", 2, func->ud);
118				break;
119			case '\\':
120				func->ucl_emitter_append_len ("\\\\", 2, func->ud);
121				break;
122			case '"':
123				func->ucl_emitter_append_len ("\\\"", 2, func->ud);
124				break;
125			}
126			len = 0;
127			c = ++p;
128		}
129		else {
130			p ++;
131			len ++;
132		}
133		size --;
134	}
135	if (len > 0) {
136		func->ucl_emitter_append_len (c, len, func->ud);
137	}
138	func->ucl_emitter_append_character ('"', 1, func->ud);
139}
140
141/**
142 * Write a single object to the buffer
143 * @param obj object to write
144 * @param buf target buffer
145 */
146static void
147ucl_elt_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
148		unsigned int tabs, bool start_tabs, bool compact)
149{
150	const ucl_object_t *cur;
151	ucl_hash_iter_t it = NULL;
152
153	if (start_tabs) {
154		ucl_add_tabs (func, tabs, compact);
155	}
156	if (compact) {
157		func->ucl_emitter_append_character ('{', 1, func->ud);
158	}
159	else {
160		func->ucl_emitter_append_len ("{\n", 2, func->ud);
161	}
162	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
163		ucl_add_tabs (func, tabs + 1, compact);
164		if (cur->keylen > 0) {
165			ucl_elt_string_write_json (cur->key, cur->keylen, func);
166		}
167		else {
168			func->ucl_emitter_append_len ("null", 4, func->ud);
169		}
170		if (compact) {
171			func->ucl_emitter_append_character (':', 1, func->ud);
172		}
173		else {
174			func->ucl_emitter_append_len (": ", 2, func->ud);
175		}
176		ucl_obj_write_json (cur, func, tabs + 1, false, compact);
177		if (ucl_hash_iter_has_next (it)) {
178			if (compact) {
179				func->ucl_emitter_append_character (',', 1, func->ud);
180			}
181			else {
182				func->ucl_emitter_append_len (",\n", 2, func->ud);
183			}
184		}
185		else if (!compact) {
186			func->ucl_emitter_append_character ('\n', 1, func->ud);
187		}
188	}
189	ucl_add_tabs (func, tabs, compact);
190	func->ucl_emitter_append_character ('}', 1, func->ud);
191}
192
193/**
194 * Write a single array to the buffer
195 * @param obj array to write
196 * @param buf target buffer
197 */
198static void
199ucl_elt_array_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
200		unsigned int tabs, bool start_tabs, bool compact)
201{
202	const ucl_object_t *cur = obj;
203
204	if (start_tabs) {
205		ucl_add_tabs (func, tabs, compact);
206	}
207	if (compact) {
208		func->ucl_emitter_append_character ('[', 1, func->ud);
209	}
210	else {
211		func->ucl_emitter_append_len ("[\n", 2, func->ud);
212	}
213	while (cur) {
214		ucl_elt_write_json (cur, func, tabs + 1, true, compact);
215		if (cur->next != NULL) {
216			if (compact) {
217				func->ucl_emitter_append_character (',', 1, func->ud);
218			}
219			else {
220				func->ucl_emitter_append_len (",\n", 2, func->ud);
221			}
222		}
223		else if (!compact) {
224			func->ucl_emitter_append_character ('\n', 1, func->ud);
225		}
226		cur = cur->next;
227	}
228	ucl_add_tabs (func, tabs, compact);
229	func->ucl_emitter_append_character (']', 1, func->ud);
230}
231
232/**
233 * Emit a single element
234 * @param obj object
235 * @param buf buffer
236 */
237static void
238ucl_elt_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
239		unsigned int tabs, bool start_tabs, bool compact)
240{
241	bool flag;
242
243	switch (obj->type) {
244	case UCL_INT:
245		if (start_tabs) {
246			ucl_add_tabs (func, tabs, compact);
247		}
248		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
249		break;
250	case UCL_FLOAT:
251	case UCL_TIME:
252		if (start_tabs) {
253			ucl_add_tabs (func, tabs, compact);
254		}
255		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
256		break;
257	case UCL_BOOLEAN:
258		if (start_tabs) {
259			ucl_add_tabs (func, tabs, compact);
260		}
261		flag = ucl_object_toboolean (obj);
262		if (flag) {
263			func->ucl_emitter_append_len ("true", 4, func->ud);
264		}
265		else {
266			func->ucl_emitter_append_len ("false", 5, func->ud);
267		}
268		break;
269	case UCL_STRING:
270		if (start_tabs) {
271			ucl_add_tabs (func, tabs, compact);
272		}
273		ucl_elt_string_write_json (obj->value.sv, obj->len, func);
274		break;
275	case UCL_NULL:
276		if (start_tabs) {
277			ucl_add_tabs (func, tabs, compact);
278		}
279		func->ucl_emitter_append_len ("null", 4, func->ud);
280		break;
281	case UCL_OBJECT:
282		ucl_elt_obj_write_json (obj, func, tabs, start_tabs, compact);
283		break;
284	case UCL_ARRAY:
285		ucl_elt_array_write_json (obj->value.av, func, tabs, start_tabs, compact);
286		break;
287	case UCL_USERDATA:
288		break;
289	}
290}
291
292/**
293 * Write a single object to the buffer
294 * @param obj object
295 * @param buf target buffer
296 */
297static void
298ucl_obj_write_json (const ucl_object_t *obj, struct ucl_emitter_functions *func,
299		unsigned int tabs, bool start_tabs, bool compact)
300{
301	const ucl_object_t *cur;
302	bool is_array = (obj->next != NULL);
303
304	if (is_array) {
305		/* This is an array actually */
306		if (start_tabs) {
307			ucl_add_tabs (func, tabs, compact);
308		}
309
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		cur = obj;
317		while (cur != NULL) {
318			ucl_elt_write_json (cur, func, tabs + 1, true, compact);
319			if (cur->next) {
320				func->ucl_emitter_append_character (',', 1, func->ud);
321			}
322			if (!compact) {
323				func->ucl_emitter_append_character ('\n', 1, func->ud);
324			}
325			cur = cur->next;
326		}
327		ucl_add_tabs (func, tabs, compact);
328		func->ucl_emitter_append_character (']', 1, func->ud);
329	}
330	else {
331		ucl_elt_write_json (obj, func, tabs, start_tabs, compact);
332	}
333
334}
335
336/**
337 * Emit an object to json
338 * @param obj object
339 * @return json output (should be freed after using)
340 */
341static void
342ucl_object_emit_json (const ucl_object_t *obj, bool compact,
343		struct ucl_emitter_functions *func)
344{
345	ucl_obj_write_json (obj, func, 0, false, compact);
346}
347
348/**
349 * Write a single object to the buffer
350 * @param obj object to write
351 * @param buf target buffer
352 */
353static void
354ucl_elt_obj_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
355		unsigned int tabs, bool start_tabs, bool is_top)
356{
357	const ucl_object_t *cur, *cur_obj;
358	ucl_hash_iter_t it = NULL;
359
360	if (start_tabs) {
361		ucl_add_tabs (func, tabs, is_top);
362	}
363	if (!is_top) {
364		func->ucl_emitter_append_len ("{\n", 2, func->ud);
365	}
366
367	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
368		LL_FOREACH (cur, cur_obj) {
369			ucl_add_tabs (func, tabs + 1, is_top);
370			if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
371				ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func);
372			}
373			else {
374				func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud);
375			}
376			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
377				func->ucl_emitter_append_len (" = ", 3, func->ud);
378			}
379			else {
380				func->ucl_emitter_append_character (' ', 1, func->ud);
381			}
382			ucl_elt_write_config (cur_obj, func,
383					is_top ? tabs : tabs + 1,
384					false, false, false);
385			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
386				func->ucl_emitter_append_len (";\n", 2, func->ud);
387			}
388			else {
389				func->ucl_emitter_append_character ('\n', 1, func->ud);
390			}
391		}
392	}
393
394	ucl_add_tabs (func, tabs, is_top);
395	if (!is_top) {
396		func->ucl_emitter_append_character ('}', 1, func->ud);
397	}
398}
399
400/**
401 * Write a single array to the buffer
402 * @param obj array to write
403 * @param buf target buffer
404 */
405static void
406ucl_elt_array_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
407		unsigned int tabs, bool start_tabs, bool is_top)
408{
409	const ucl_object_t *cur = obj;
410
411	if (start_tabs) {
412		ucl_add_tabs (func, tabs, false);
413	}
414
415	func->ucl_emitter_append_len ("[\n", 2, func->ud);
416	while (cur) {
417		ucl_elt_write_config (cur, func, tabs + 1, true, false, false);
418		func->ucl_emitter_append_len (",\n", 2, func->ud);
419		cur = cur->next;
420	}
421	ucl_add_tabs (func, tabs, false);
422	func->ucl_emitter_append_character (']', 1, func->ud);
423}
424
425/**
426 * Emit a single element
427 * @param obj object
428 * @param buf buffer
429 */
430static void
431ucl_elt_write_config (const ucl_object_t *obj, struct ucl_emitter_functions *func,
432		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
433{
434	bool flag;
435
436	if (expand_array && obj->next != NULL) {
437		ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top);
438	}
439	else {
440		switch (obj->type) {
441		case UCL_INT:
442			if (start_tabs) {
443				ucl_add_tabs (func, tabs, false);
444			}
445			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
446			break;
447		case UCL_FLOAT:
448		case UCL_TIME:
449			if (start_tabs) {
450				ucl_add_tabs (func, tabs, false);
451			}
452			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
453			break;
454		case UCL_BOOLEAN:
455			if (start_tabs) {
456				ucl_add_tabs (func, tabs, false);
457			}
458			flag = ucl_object_toboolean (obj);
459			if (flag) {
460				func->ucl_emitter_append_len ("true", 4, func->ud);
461			}
462			else {
463				func->ucl_emitter_append_len ("false", 5, func->ud);
464			}
465			break;
466		case UCL_STRING:
467			if (start_tabs) {
468				ucl_add_tabs (func, tabs, false);
469			}
470			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
471			break;
472		case UCL_NULL:
473			if (start_tabs) {
474				ucl_add_tabs (func, tabs, false);
475			}
476			func->ucl_emitter_append_len ("null", 4, func->ud);
477			break;
478		case UCL_OBJECT:
479			ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top);
480			break;
481		case UCL_ARRAY:
482			ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top);
483			break;
484		case UCL_USERDATA:
485			break;
486		}
487	}
488}
489
490/**
491 * Emit an object to rcl
492 * @param obj object
493 * @return rcl output (should be freed after using)
494 */
495static void
496ucl_object_emit_config (const ucl_object_t *obj, struct ucl_emitter_functions *func)
497{
498	ucl_elt_write_config (obj, func, 0, false, true, true);
499}
500
501
502static void
503ucl_obj_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
504		unsigned int tabs, bool start_tabs)
505{
506	bool is_array = (obj->next != NULL);
507
508	if (is_array) {
509		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false);
510	}
511	else {
512		ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true);
513	}
514}
515
516/**
517 * Write a single object to the buffer
518 * @param obj object to write
519 * @param buf target buffer
520 */
521static void
522ucl_elt_obj_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
523		unsigned int tabs, bool start_tabs, bool is_top)
524{
525	const ucl_object_t *cur;
526	ucl_hash_iter_t it = NULL;
527
528	if (start_tabs) {
529		ucl_add_tabs (func, tabs, is_top);
530	}
531	if (!is_top) {
532		func->ucl_emitter_append_len ("{\n", 2, func->ud);
533	}
534
535	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
536		ucl_add_tabs (func, tabs + 1, is_top);
537		if (cur->keylen > 0) {
538			ucl_elt_string_write_json (cur->key, cur->keylen, func);
539		}
540		else {
541			func->ucl_emitter_append_len ("null", 4, func->ud);
542		}
543		func->ucl_emitter_append_len (": ", 2, func->ud);
544		ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false);
545		if (ucl_hash_iter_has_next(it)) {
546			if (!is_top) {
547				func->ucl_emitter_append_len (",\n", 2, func->ud);
548			}
549			else {
550				func->ucl_emitter_append_character ('\n', 1, func->ud);
551			}
552		}
553		else {
554			func->ucl_emitter_append_character ('\n', 1, func->ud);
555		}
556	}
557
558	ucl_add_tabs (func, tabs, is_top);
559	if (!is_top) {
560		func->ucl_emitter_append_character ('}', 1, func->ud);
561	}
562}
563
564/**
565 * Write a single array to the buffer
566 * @param obj array to write
567 * @param buf target buffer
568 */
569static void
570ucl_elt_array_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
571		unsigned int tabs, bool start_tabs, bool is_top)
572{
573	const ucl_object_t *cur = obj;
574
575	if (start_tabs) {
576		ucl_add_tabs (func, tabs, false);
577	}
578
579	func->ucl_emitter_append_len ("[\n", 2, func->ud);
580	while (cur) {
581		ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false);
582		func->ucl_emitter_append_len (",\n", 2, func->ud);
583		cur = cur->next;
584	}
585	ucl_add_tabs (func, tabs, false);
586	func->ucl_emitter_append_character (']', 1, func->ud);
587}
588
589/**
590 * Emit a single element
591 * @param obj object
592 * @param buf buffer
593 */
594static void
595ucl_elt_write_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func,
596		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
597{
598	bool flag;
599
600	if (expand_array && obj->next != NULL ) {
601		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top);
602	}
603	else {
604		switch (obj->type) {
605		case UCL_INT:
606			if (start_tabs) {
607				ucl_add_tabs (func, tabs, false);
608			}
609			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
610			break;
611		case UCL_FLOAT:
612		case UCL_TIME:
613			if (start_tabs) {
614				ucl_add_tabs (func, tabs, false);
615			}
616			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
617			break;
618		case UCL_BOOLEAN:
619			if (start_tabs) {
620				ucl_add_tabs (func, tabs, false);
621			}
622			flag = ucl_object_toboolean (obj);
623			if (flag) {
624				func->ucl_emitter_append_len ("true", 4, func->ud);
625			}
626			else {
627				func->ucl_emitter_append_len ("false", 5, func->ud);
628			}
629			break;
630		case UCL_STRING:
631			if (start_tabs) {
632				ucl_add_tabs (func, tabs, false);
633			}
634			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
635			break;
636		case UCL_NULL:
637			if (start_tabs) {
638				ucl_add_tabs (func, tabs, false);
639			}
640			func->ucl_emitter_append_len ("null", 4, func->ud);
641			break;
642		case UCL_OBJECT:
643			ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top);
644			break;
645		case UCL_ARRAY:
646			ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top);
647			break;
648		case UCL_USERDATA:
649			break;
650		}
651	}
652}
653
654/**
655 * Emit an object to rcl
656 * @param obj object
657 * @return rcl output (should be freed after using)
658 */
659static void
660ucl_object_emit_yaml (const ucl_object_t *obj, struct ucl_emitter_functions *func)
661{
662	ucl_elt_write_yaml (obj, func, 0, false, true, true);
663}
664
665/*
666 * Generic utstring output
667 */
668static int
669ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
670{
671	UT_string *buf = ud;
672
673	if (len == 1) {
674		utstring_append_c (buf, c);
675	}
676	else {
677		utstring_reserve (buf, len);
678		memset (&buf->d[buf->i], c, len);
679		buf->i += len;
680		buf->d[buf->i] = '\0';
681	}
682
683	return 0;
684}
685
686static int
687ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud)
688{
689	UT_string *buf = ud;
690
691	utstring_append_len (buf, str, len);
692
693	return 0;
694}
695
696static int
697ucl_utstring_append_int (int64_t val, void *ud)
698{
699	UT_string *buf = ud;
700
701	utstring_printf (buf, "%jd", (intmax_t)val);
702	return 0;
703}
704
705static int
706ucl_utstring_append_double (double val, void *ud)
707{
708	UT_string *buf = ud;
709	const double delta = 0.0000001;
710
711	if (val == (double)(int)val) {
712		utstring_printf (buf, "%.1lf", val);
713	}
714	else if (fabs (val - (double)(int)val) < delta) {
715		/* Write at maximum precision */
716		utstring_printf (buf, "%.*lg", DBL_DIG, val);
717	}
718	else {
719		utstring_printf (buf, "%lf", val);
720	}
721
722	return 0;
723}
724
725
726unsigned char *
727ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
728{
729	UT_string *buf = NULL;
730	unsigned char *res = NULL;
731	struct ucl_emitter_functions func = {
732		.ucl_emitter_append_character = ucl_utstring_append_character,
733		.ucl_emitter_append_len = ucl_utstring_append_len,
734		.ucl_emitter_append_int = ucl_utstring_append_int,
735		.ucl_emitter_append_double = ucl_utstring_append_double
736	};
737
738	if (obj == NULL) {
739		return NULL;
740	}
741
742	utstring_new (buf);
743	func.ud = buf;
744
745	if (buf != NULL) {
746		if (emit_type == UCL_EMIT_JSON) {
747			ucl_object_emit_json (obj, false, &func);
748		}
749		else if (emit_type == UCL_EMIT_JSON_COMPACT) {
750			ucl_object_emit_json (obj, true, &func);
751		}
752		else if (emit_type == UCL_EMIT_YAML) {
753			ucl_object_emit_yaml (obj, &func);
754		}
755		else {
756			ucl_object_emit_config (obj, &func);
757		}
758
759		res = utstring_body (buf);
760		free (buf);
761	}
762
763	return res;
764}
765
766bool
767ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
768		struct ucl_emitter_functions *emitter)
769{
770	if (emit_type == UCL_EMIT_JSON) {
771		ucl_object_emit_json (obj, false, emitter);
772	}
773	else if (emit_type == UCL_EMIT_JSON_COMPACT) {
774		ucl_object_emit_json (obj, true, emitter);
775	}
776	else if (emit_type == UCL_EMIT_YAML) {
777		ucl_object_emit_yaml (obj, emitter);
778	}
779	else {
780		ucl_object_emit_config (obj, emitter);
781	}
782
783	/* XXX: need some error checks here */
784	return true;
785}
786
787
788unsigned char *
789ucl_object_emit_single_json (const ucl_object_t *obj)
790{
791	UT_string *buf = NULL;
792	unsigned char *res = NULL;
793
794	if (obj == NULL) {
795		return NULL;
796	}
797
798	utstring_new (buf);
799
800	if (buf != NULL) {
801		switch (obj->type) {
802		case UCL_OBJECT:
803			ucl_utstring_append_len ("object", 6, buf);
804			break;
805		case UCL_ARRAY:
806			ucl_utstring_append_len ("array", 5, buf);
807			break;
808		case UCL_INT:
809			ucl_utstring_append_int (obj->value.iv, buf);
810			break;
811		case UCL_FLOAT:
812		case UCL_TIME:
813			ucl_utstring_append_double (obj->value.dv, buf);
814			break;
815		case UCL_NULL:
816			ucl_utstring_append_len ("null", 4, buf);
817			break;
818		case UCL_BOOLEAN:
819			if (obj->value.iv) {
820				ucl_utstring_append_len ("true", 4, buf);
821			}
822			else {
823				ucl_utstring_append_len ("false", 5, buf);
824			}
825			break;
826		case UCL_STRING:
827			ucl_utstring_append_len (obj->value.sv, obj->len, buf);
828			break;
829		case UCL_USERDATA:
830			ucl_utstring_append_len ("userdata", 8, buf);
831			break;
832		}
833		res = utstring_body (buf);
834		free (buf);
835	}
836
837	return res;
838}
839