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