]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/libucl/src/ucl_emitter.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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 #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 }