xref: /freebsd/contrib/libucl/src/ucl_emitter.c (revision 0572ccaa4543b0abef8ef81e384c1d04de9f3da1)
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 (const 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 (const 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 (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);
60 static 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);
66 static 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  */
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 (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  */
198 static void
199 ucl_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  */
237 static void
238 ucl_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  */
297 static void
298 ucl_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  */
341 static void
342 ucl_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  */
353 static void
354 ucl_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  */
405 static void
406 ucl_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  */
430 static void
431 ucl_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  */
495 static void
496 ucl_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 
502 static void
503 ucl_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  */
521 static void
522 ucl_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  */
569 static void
570 ucl_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  */
594 static void
595 ucl_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  */
659 static void
660 ucl_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  */
668 static int
669 ucl_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 
686 static int
687 ucl_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 
696 static int
697 ucl_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 
705 static int
706 ucl_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 
726 unsigned char *
727 ucl_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 
766 bool
767 ucl_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 
788 unsigned char *
789 ucl_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