xref: /freebsd/contrib/libucl/src/ucl_emitter.c (revision 3bdf775801b218aa5a89564839405b122f4b233e)
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 
44 static void ucl_obj_write_json (ucl_object_t *obj,
45 		struct ucl_emitter_functions *func,
46 		unsigned int tabs,
47 		bool start_tabs,
48 		bool compact);
49 static void ucl_elt_write_json (ucl_object_t *obj,
50 		struct ucl_emitter_functions *func,
51 		unsigned int tabs,
52 		bool start_tabs,
53 		bool compact);
54 static void ucl_elt_write_config (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);
60 static void ucl_elt_write_yaml (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);
66 static void ucl_elt_array_write_yaml (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  */
77 static inline void
78 ucl_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  */
90 static void
91 ucl_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  */
146 static void
147 ucl_elt_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
148 		unsigned int tabs, bool start_tabs, bool compact)
149 {
150 	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  */
198 static void
199 ucl_elt_array_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
200 		unsigned int tabs, bool start_tabs, bool compact)
201 {
202 	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  */
237 static void
238 ucl_elt_write_json (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  */
297 static void
298 ucl_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
299 		unsigned int tabs, bool start_tabs, bool compact)
300 {
301 	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  */
341 static void
342 ucl_object_emit_json (ucl_object_t *obj, bool compact, struct ucl_emitter_functions *func)
343 {
344 	ucl_obj_write_json (obj, func, 0, false, compact);
345 }
346 
347 /**
348  * Write a single object to the buffer
349  * @param obj object to write
350  * @param buf target buffer
351  */
352 static void
353 ucl_elt_obj_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
354 		unsigned int tabs, bool start_tabs, bool is_top)
355 {
356 	ucl_object_t *cur, *cur_obj;
357 	ucl_hash_iter_t it = NULL;
358 
359 	if (start_tabs) {
360 		ucl_add_tabs (func, tabs, is_top);
361 	}
362 	if (!is_top) {
363 		func->ucl_emitter_append_len ("{\n", 2, func->ud);
364 	}
365 
366 	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
367 		LL_FOREACH (cur, cur_obj) {
368 			ucl_add_tabs (func, tabs + 1, is_top);
369 			if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
370 				ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func);
371 			}
372 			else {
373 				func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud);
374 			}
375 			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
376 				func->ucl_emitter_append_len (" = ", 3, func->ud);
377 			}
378 			else {
379 				func->ucl_emitter_append_character (' ', 1, func->ud);
380 			}
381 			ucl_elt_write_config (cur_obj, func,
382 					is_top ? tabs : tabs + 1,
383 					false, false, false);
384 			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
385 				func->ucl_emitter_append_len (";\n", 2, func->ud);
386 			}
387 			else {
388 				func->ucl_emitter_append_character ('\n', 1, func->ud);
389 			}
390 		}
391 	}
392 
393 	ucl_add_tabs (func, tabs, is_top);
394 	if (!is_top) {
395 		func->ucl_emitter_append_character ('}', 1, func->ud);
396 	}
397 }
398 
399 /**
400  * Write a single array to the buffer
401  * @param obj array to write
402  * @param buf target buffer
403  */
404 static void
405 ucl_elt_array_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
406 		unsigned int tabs, bool start_tabs, bool is_top)
407 {
408 	ucl_object_t *cur = obj;
409 
410 	if (start_tabs) {
411 		ucl_add_tabs (func, tabs, false);
412 	}
413 
414 	func->ucl_emitter_append_len ("[\n", 2, func->ud);
415 	while (cur) {
416 		ucl_elt_write_config (cur, func, tabs + 1, true, false, false);
417 		func->ucl_emitter_append_len (",\n", 2, func->ud);
418 		cur = cur->next;
419 	}
420 	ucl_add_tabs (func, tabs, false);
421 	func->ucl_emitter_append_character (']', 1, func->ud);
422 }
423 
424 /**
425  * Emit a single element
426  * @param obj object
427  * @param buf buffer
428  */
429 static void
430 ucl_elt_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
431 		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
432 {
433 	bool flag;
434 
435 	if (expand_array && obj->next != NULL) {
436 		ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top);
437 	}
438 	else {
439 		switch (obj->type) {
440 		case UCL_INT:
441 			if (start_tabs) {
442 				ucl_add_tabs (func, tabs, false);
443 			}
444 			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
445 			break;
446 		case UCL_FLOAT:
447 		case UCL_TIME:
448 			if (start_tabs) {
449 				ucl_add_tabs (func, tabs, false);
450 			}
451 			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
452 			break;
453 		case UCL_BOOLEAN:
454 			if (start_tabs) {
455 				ucl_add_tabs (func, tabs, false);
456 			}
457 			flag = ucl_object_toboolean (obj);
458 			if (flag) {
459 				func->ucl_emitter_append_len ("true", 4, func->ud);
460 			}
461 			else {
462 				func->ucl_emitter_append_len ("false", 5, func->ud);
463 			}
464 			break;
465 		case UCL_STRING:
466 			if (start_tabs) {
467 				ucl_add_tabs (func, tabs, false);
468 			}
469 			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
470 			break;
471 		case UCL_NULL:
472 			if (start_tabs) {
473 				ucl_add_tabs (func, tabs, false);
474 			}
475 			func->ucl_emitter_append_len ("null", 4, func->ud);
476 			break;
477 		case UCL_OBJECT:
478 			ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top);
479 			break;
480 		case UCL_ARRAY:
481 			ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top);
482 			break;
483 		case UCL_USERDATA:
484 			break;
485 		}
486 	}
487 }
488 
489 /**
490  * Emit an object to rcl
491  * @param obj object
492  * @return rcl output (should be freed after using)
493  */
494 static void
495 ucl_object_emit_config (ucl_object_t *obj, struct ucl_emitter_functions *func)
496 {
497 	ucl_elt_write_config (obj, func, 0, false, true, true);
498 }
499 
500 
501 static void
502 ucl_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
503 		unsigned int tabs, bool start_tabs)
504 {
505 	bool is_array = (obj->next != NULL);
506 
507 	if (is_array) {
508 		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false);
509 	}
510 	else {
511 		ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true);
512 	}
513 }
514 
515 /**
516  * Write a single object to the buffer
517  * @param obj object to write
518  * @param buf target buffer
519  */
520 static void
521 ucl_elt_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
522 		unsigned int tabs, bool start_tabs, bool is_top)
523 {
524 	ucl_object_t *cur;
525 	ucl_hash_iter_t it = NULL;
526 
527 	if (start_tabs) {
528 		ucl_add_tabs (func, tabs, is_top);
529 	}
530 	if (!is_top) {
531 		func->ucl_emitter_append_len ("{\n", 2, func->ud);
532 	}
533 
534 	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
535 		ucl_add_tabs (func, tabs + 1, is_top);
536 		if (cur->keylen > 0) {
537 			ucl_elt_string_write_json (cur->key, cur->keylen, func);
538 		}
539 		else {
540 			func->ucl_emitter_append_len ("null", 4, func->ud);
541 		}
542 		func->ucl_emitter_append_len (": ", 2, func->ud);
543 		ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false);
544 		if (ucl_hash_iter_has_next(it)) {
545 			if (!is_top) {
546 				func->ucl_emitter_append_len (",\n", 2, func->ud);
547 			}
548 			else {
549 				func->ucl_emitter_append_character ('\n', 1, func->ud);
550 			}
551 		}
552 		else {
553 			func->ucl_emitter_append_character ('\n', 1, func->ud);
554 		}
555 	}
556 
557 	ucl_add_tabs (func, tabs, is_top);
558 	if (!is_top) {
559 		func->ucl_emitter_append_character ('}', 1, func->ud);
560 	}
561 }
562 
563 /**
564  * Write a single array to the buffer
565  * @param obj array to write
566  * @param buf target buffer
567  */
568 static void
569 ucl_elt_array_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
570 		unsigned int tabs, bool start_tabs, bool is_top)
571 {
572 	ucl_object_t *cur = obj;
573 
574 	if (start_tabs) {
575 		ucl_add_tabs (func, tabs, false);
576 	}
577 
578 	func->ucl_emitter_append_len ("[\n", 2, func->ud);
579 	while (cur) {
580 		ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false);
581 		func->ucl_emitter_append_len (",\n", 2, func->ud);
582 		cur = cur->next;
583 	}
584 	ucl_add_tabs (func, tabs, false);
585 	func->ucl_emitter_append_character (']', 1, func->ud);
586 }
587 
588 /**
589  * Emit a single element
590  * @param obj object
591  * @param buf buffer
592  */
593 static void
594 ucl_elt_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
595 		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
596 {
597 	bool flag;
598 
599 	if (expand_array && obj->next != NULL ) {
600 		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top);
601 	}
602 	else {
603 		switch (obj->type) {
604 		case UCL_INT:
605 			if (start_tabs) {
606 				ucl_add_tabs (func, tabs, false);
607 			}
608 			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
609 			break;
610 		case UCL_FLOAT:
611 		case UCL_TIME:
612 			if (start_tabs) {
613 				ucl_add_tabs (func, tabs, false);
614 			}
615 			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
616 			break;
617 		case UCL_BOOLEAN:
618 			if (start_tabs) {
619 				ucl_add_tabs (func, tabs, false);
620 			}
621 			flag = ucl_object_toboolean (obj);
622 			if (flag) {
623 				func->ucl_emitter_append_len ("true", 4, func->ud);
624 			}
625 			else {
626 				func->ucl_emitter_append_len ("false", 5, func->ud);
627 			}
628 			break;
629 		case UCL_STRING:
630 			if (start_tabs) {
631 				ucl_add_tabs (func, tabs, false);
632 			}
633 			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
634 			break;
635 		case UCL_NULL:
636 			if (start_tabs) {
637 				ucl_add_tabs (func, tabs, false);
638 			}
639 			func->ucl_emitter_append_len ("null", 4, func->ud);
640 			break;
641 		case UCL_OBJECT:
642 			ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top);
643 			break;
644 		case UCL_ARRAY:
645 			ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top);
646 			break;
647 		case UCL_USERDATA:
648 			break;
649 		}
650 	}
651 }
652 
653 /**
654  * Emit an object to rcl
655  * @param obj object
656  * @return rcl output (should be freed after using)
657  */
658 static void
659 ucl_object_emit_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func)
660 {
661 	ucl_elt_write_yaml (obj, func, 0, false, true, true);
662 }
663 
664 /*
665  * Generic utstring output
666  */
667 static int
668 ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
669 {
670 	UT_string *buf = ud;
671 
672 	if (len == 1) {
673 		utstring_append_c (buf, c);
674 	}
675 	else {
676 		utstring_reserve (buf, len);
677 		memset (&buf->d[buf->i], c, len);
678 		buf->i += len;
679 		buf->d[buf->i] = '\0';
680 	}
681 
682 	return 0;
683 }
684 
685 static int
686 ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud)
687 {
688 	UT_string *buf = ud;
689 
690 	utstring_append_len (buf, str, len);
691 
692 	return 0;
693 }
694 
695 static int
696 ucl_utstring_append_int (int64_t val, void *ud)
697 {
698 	UT_string *buf = ud;
699 
700 	utstring_printf (buf, "%jd", (intmax_t)val);
701 	return 0;
702 }
703 
704 static int
705 ucl_utstring_append_double (double val, void *ud)
706 {
707 	UT_string *buf = ud;
708 	const double delta = 0.0000001;
709 
710 	if (val == (double)(int)val) {
711 		utstring_printf (buf, "%.1lf", val);
712 	}
713 	else if (fabs (val - (double)(int)val) < delta) {
714 		/* Write at maximum precision */
715 		utstring_printf (buf, "%.*lg", DBL_DIG, val);
716 	}
717 	else {
718 		utstring_printf (buf, "%lf", val);
719 	}
720 
721 	return 0;
722 }
723 
724 
725 unsigned char *
726 ucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type)
727 {
728 	UT_string *buf = NULL;
729 	unsigned char *res = NULL;
730 	struct ucl_emitter_functions func = {
731 		.ucl_emitter_append_character = ucl_utstring_append_character,
732 		.ucl_emitter_append_len = ucl_utstring_append_len,
733 		.ucl_emitter_append_int = ucl_utstring_append_int,
734 		.ucl_emitter_append_double = ucl_utstring_append_double
735 	};
736 
737 	if (obj == NULL) {
738 		return NULL;
739 	}
740 
741 	utstring_new (buf);
742 	func.ud = buf;
743 
744 	if (buf != NULL) {
745 		if (emit_type == UCL_EMIT_JSON) {
746 			ucl_object_emit_json (obj, false, &func);
747 		}
748 		else if (emit_type == UCL_EMIT_JSON_COMPACT) {
749 			ucl_object_emit_json (obj, true, &func);
750 		}
751 		else if (emit_type == UCL_EMIT_YAML) {
752 			ucl_object_emit_yaml (obj, &func);
753 		}
754 		else {
755 			ucl_object_emit_config (obj, &func);
756 		}
757 
758 		res = utstring_body (buf);
759 		free (buf);
760 	}
761 
762 	return res;
763 }
764 
765 bool
766 ucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
767 		struct ucl_emitter_functions *emitter)
768 {
769 	if (emit_type == UCL_EMIT_JSON) {
770 		ucl_object_emit_json (obj, false, emitter);
771 	}
772 	else if (emit_type == UCL_EMIT_JSON_COMPACT) {
773 		ucl_object_emit_json (obj, true, emitter);
774 	}
775 	else if (emit_type == UCL_EMIT_YAML) {
776 		ucl_object_emit_yaml (obj, emitter);
777 	}
778 	else {
779 		ucl_object_emit_config (obj, emitter);
780 	}
781 
782 	/* XXX: need some error checks here */
783 	return true;
784 }
785 
786 
787 unsigned char *
788 ucl_object_emit_single_json (ucl_object_t *obj)
789 {
790 	UT_string *buf = NULL;
791 	unsigned char *res = NULL;
792 
793 	if (obj == NULL) {
794 		return NULL;
795 	}
796 
797 	utstring_new (buf);
798 
799 	if (buf != NULL) {
800 		switch (obj->type) {
801 		case UCL_OBJECT:
802 			ucl_utstring_append_len ("object", 6, buf);
803 			break;
804 		case UCL_ARRAY:
805 			ucl_utstring_append_len ("array", 5, buf);
806 			break;
807 		case UCL_INT:
808 			ucl_utstring_append_int (obj->value.iv, buf);
809 			break;
810 		case UCL_FLOAT:
811 		case UCL_TIME:
812 			ucl_utstring_append_double (obj->value.dv, buf);
813 			break;
814 		case UCL_NULL:
815 			ucl_utstring_append_len ("null", 4, buf);
816 			break;
817 		case UCL_BOOLEAN:
818 			if (obj->value.iv) {
819 				ucl_utstring_append_len ("true", 4, buf);
820 			}
821 			else {
822 				ucl_utstring_append_len ("false", 5, buf);
823 			}
824 			break;
825 		case UCL_STRING:
826 			ucl_utstring_append_len (obj->value.sv, obj->len, buf);
827 			break;
828 		case UCL_USERDATA:
829 			ucl_utstring_append_len ("userdata", 8, buf);
830 			break;
831 		}
832 		res = utstring_body (buf);
833 		free (buf);
834 	}
835 
836 	return res;
837 }
838