]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/libucl/src/ucl_emitter.c
Copy stable/9 to releng/9.3 as part of the 9.3-RELEASE cycle.
[FreeBSD/releng/9.3.git] / contrib / libucl / src / ucl_emitter.c
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 }