Deleted Added
full compact
ucl_emitter.c (262398) ucl_emitter.c (263648)
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
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>
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
26#include "ucl.h"
27#include "ucl_internal.h"
28#include "ucl_chartable.h"
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
29
30/**
31 * @file rcl_emitter.c
32 * Serialise UCL object to various of output formats
33 */
34
35
36static 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);
41static 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);
46static 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);
52static 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);
58static 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 */
69static inline void
70ucl_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 */
82static void
83ucl_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 */
138static void
139ucl_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 */
190static void
191ucl_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 */
229static void
230ucl_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 */
289static void
290ucl_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 */
333static void
334ucl_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 */
344static void
345ucl_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 */
396static void
397ucl_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 */
421static void
422ucl_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 */
486static void
487ucl_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
493static void
494ucl_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 */
512static void
513ucl_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 */
560static void
561ucl_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 */
585static void
586ucl_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 */
650static void
651ucl_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 */
659static int
660ucl_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
677static int
678ucl_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
687static int
688ucl_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
696static int
697ucl_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
717unsigned char *
718ucl_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
757bool
758ucl_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
779unsigned char *
780ucl_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}
37
38/**
39 * @file rcl_emitter.c
40 * Serialise UCL object to various of output formats
41 */
42
43
44static 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);
49static 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);
54static 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);
60static 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);
66static 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 */
77static inline void
78ucl_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 */
90static void
91ucl_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 */
146static void
147ucl_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 */
198static void
199ucl_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 */
237static void
238ucl_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 */
297static void
298ucl_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 */
341static void
342ucl_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 */
352static void
353ucl_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 */
404static void
405ucl_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 */
429static void
430ucl_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 */
494static void
495ucl_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
501static void
502ucl_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 */
520static void
521ucl_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 */
568static void
569ucl_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 */
593static void
594ucl_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 */
658static void
659ucl_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 */
667static int
668ucl_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
685static int
686ucl_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
695static int
696ucl_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
704static int
705ucl_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
725unsigned char *
726ucl_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
765bool
766ucl_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
787unsigned char *
788ucl_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}