ucl_emitter.c revision 262398
1189251Ssam/* Copyright (c) 2013, Vsevolod Stakhov
2189251Ssam * All rights reserved.
3252726Srpaulo *
4189251Ssam * Redistribution and use in source and binary forms, with or without
5252726Srpaulo * modification, are permitted provided that the following conditions are met:
6252726Srpaulo *       * Redistributions of source code must retain the above copyright
7189251Ssam *         notice, this list of conditions and the following disclaimer.
8189251Ssam *       * Redistributions in binary form must reproduce the above copyright
9189251Ssam *         notice, this list of conditions and the following disclaimer in the
10189251Ssam *         documentation and/or other materials provided with the distribution.
11189251Ssam *
12189251Ssam * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13189251Ssam * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14252726Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15214734Srpaulo * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16214734Srpaulo * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17214734Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18214734Srpaulo * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19214734Srpaulo * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20189251Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21214734Srpaulo * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22252726Srpaulo */
23214734Srpaulo
24252726Srpaulo#include <float.h>
25214734Srpaulo#include <math.h>
26214734Srpaulo#include "ucl.h"
27214734Srpaulo#include "ucl_internal.h"
28214734Srpaulo#include "ucl_chartable.h"
29189251Ssam
30214734Srpaulo/**
31214734Srpaulo * @file rcl_emitter.c
32252726Srpaulo * Serialise UCL object to various of output formats
33252726Srpaulo */
34252726Srpaulo
35189251Ssam
36189251Ssamstatic void ucl_obj_write_json (ucl_object_t *obj,
37209158Srpaulo		struct ucl_emitter_functions *func,
38252726Srpaulo		unsigned int tabs,
39189251Ssam		bool start_tabs,
40252726Srpaulo		bool compact);
41189251Ssamstatic void ucl_elt_write_json (ucl_object_t *obj,
42189251Ssam		struct ucl_emitter_functions *func,
43189251Ssam		unsigned int tabs,
44189251Ssam		bool start_tabs,
45189251Ssam		bool compact);
46252726Srpaulostatic void ucl_elt_write_config (ucl_object_t *obj,
47252726Srpaulo		struct ucl_emitter_functions *func,
48252726Srpaulo		unsigned int tabs,
49252726Srpaulo		bool start_tabs,
50252726Srpaulo		bool is_top,
51252726Srpaulo		bool expand_array);
52252726Srpaulostatic void ucl_elt_write_yaml (ucl_object_t *obj,
53252726Srpaulo		struct ucl_emitter_functions *func,
54252726Srpaulo		unsigned int tabs,
55189251Ssam		bool start_tabs,
56189251Ssam		bool compact,
57189251Ssam		bool expand_array);
58189251Ssamstatic void ucl_elt_array_write_yaml (ucl_object_t *obj,
59189251Ssam		struct ucl_emitter_functions *func,
60189251Ssam		unsigned int tabs,
61189251Ssam		bool start_tabs,
62189251Ssam		bool is_top);
63189251Ssam
64189251Ssam/**
65189251Ssam * Add tabulation to the output buffer
66189251Ssam * @param buf target buffer
67189251Ssam * @param tabs number of tabs to add
68189251Ssam */
69189251Ssamstatic inline void
70189251Ssamucl_add_tabs (struct ucl_emitter_functions *func, unsigned int tabs, bool compact)
71189251Ssam{
72189251Ssam	if (!compact) {
73189251Ssam		func->ucl_emitter_append_character (' ', tabs * 4, func->ud);
74189251Ssam	}
75189251Ssam}
76189251Ssam
77189251Ssam/**
78252726Srpaulo * Serialise string
79189251Ssam * @param str string to emit
80252726Srpaulo * @param buf target buffer
81252726Srpaulo */
82189251Ssamstatic void
83189251Ssamucl_elt_string_write_json (const char *str, size_t size,
84189251Ssam		struct ucl_emitter_functions *func)
85252726Srpaulo{
86252726Srpaulo	const char *p = str, *c = str;
87189251Ssam	size_t len = 0;
88252726Srpaulo
89252726Srpaulo	func->ucl_emitter_append_character ('"', 1, func->ud);
90189251Ssam	while (size) {
91189251Ssam		if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
92252726Srpaulo			if (len > 0) {
93252726Srpaulo				func->ucl_emitter_append_len (c, len, func->ud);
94252726Srpaulo			}
95252726Srpaulo			switch (*p) {
96252726Srpaulo			case '\n':
97214734Srpaulo				func->ucl_emitter_append_len ("\\n", 2, func->ud);
98252726Srpaulo				break;
99252726Srpaulo			case '\r':
100189251Ssam				func->ucl_emitter_append_len ("\\r", 2, func->ud);
101189251Ssam				break;
102189251Ssam			case '\b':
103189251Ssam				func->ucl_emitter_append_len ("\\b", 2, func->ud);
104189251Ssam				break;
105189251Ssam			case '\t':
106189251Ssam				func->ucl_emitter_append_len ("\\t", 2, func->ud);
107189251Ssam				break;
108189251Ssam			case '\f':
109189251Ssam				func->ucl_emitter_append_len ("\\f", 2, func->ud);
110189251Ssam				break;
111189251Ssam			case '\\':
112189251Ssam				func->ucl_emitter_append_len ("\\\\", 2, func->ud);
113189251Ssam				break;
114189251Ssam			case '"':
115189251Ssam				func->ucl_emitter_append_len ("\\\"", 2, func->ud);
116189251Ssam				break;
117189251Ssam			}
118209158Srpaulo			len = 0;
119209158Srpaulo			c = ++p;
120209158Srpaulo		}
121209158Srpaulo		else {
122209158Srpaulo			p ++;
123214734Srpaulo			len ++;
124209158Srpaulo		}
125209158Srpaulo		size --;
126209158Srpaulo	}
127209158Srpaulo	if (len > 0) {
128209158Srpaulo		func->ucl_emitter_append_len (c, len, func->ud);
129209158Srpaulo	}
130209158Srpaulo	func->ucl_emitter_append_character ('"', 1, func->ud);
131209158Srpaulo}
132209158Srpaulo
133209158Srpaulo/**
134209158Srpaulo * Write a single object to the buffer
135209158Srpaulo * @param obj object to write
136209158Srpaulo * @param buf target buffer
137209158Srpaulo */
138209158Srpaulostatic void
139252726Srpauloucl_elt_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
140252726Srpaulo		unsigned int tabs, bool start_tabs, bool compact)
141214734Srpaulo{
142214734Srpaulo	ucl_object_t *cur;
143214734Srpaulo	ucl_hash_iter_t it = NULL;
144214734Srpaulo
145209158Srpaulo	if (start_tabs) {
146209158Srpaulo		ucl_add_tabs (func, tabs, compact);
147209158Srpaulo	}
148214734Srpaulo	if (compact) {
149214734Srpaulo		func->ucl_emitter_append_character ('{', 1, func->ud);
150214734Srpaulo	}
151209158Srpaulo	else {
152209158Srpaulo		func->ucl_emitter_append_len ("{\n", 2, func->ud);
153209158Srpaulo	}
154209158Srpaulo	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
155209158Srpaulo		ucl_add_tabs (func, tabs + 1, compact);
156214734Srpaulo		if (cur->keylen > 0) {
157209158Srpaulo			ucl_elt_string_write_json (cur->key, cur->keylen, func);
158209158Srpaulo		}
159209158Srpaulo		else {
160209158Srpaulo			func->ucl_emitter_append_len ("null", 4, func->ud);
161209158Srpaulo		}
162209158Srpaulo		if (compact) {
163209158Srpaulo			func->ucl_emitter_append_character (':', 1, func->ud);
164209158Srpaulo		}
165209158Srpaulo		else {
166209158Srpaulo			func->ucl_emitter_append_len (": ", 2, func->ud);
167209158Srpaulo		}
168209158Srpaulo		ucl_obj_write_json (cur, func, tabs + 1, false, compact);
169209158Srpaulo		if (ucl_hash_iter_has_next (it)) {
170209158Srpaulo			if (compact) {
171209158Srpaulo				func->ucl_emitter_append_character (',', 1, func->ud);
172209158Srpaulo			}
173209158Srpaulo			else {
174209158Srpaulo				func->ucl_emitter_append_len (",\n", 2, func->ud);
175209158Srpaulo			}
176209158Srpaulo		}
177209158Srpaulo		else if (!compact) {
178209158Srpaulo			func->ucl_emitter_append_character ('\n', 1, func->ud);
179209158Srpaulo		}
180209158Srpaulo	}
181209158Srpaulo	ucl_add_tabs (func, tabs, compact);
182209158Srpaulo	func->ucl_emitter_append_character ('}', 1, func->ud);
183209158Srpaulo}
184209158Srpaulo
185209158Srpaulo/**
186209158Srpaulo * Write a single array to the buffer
187209158Srpaulo * @param obj array to write
188209158Srpaulo * @param buf target buffer
189209158Srpaulo */
190209158Srpaulostatic void
191209158Srpauloucl_elt_array_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
192209158Srpaulo		unsigned int tabs, bool start_tabs, bool compact)
193209158Srpaulo{
194209158Srpaulo	ucl_object_t *cur = obj;
195209158Srpaulo
196209158Srpaulo	if (start_tabs) {
197209158Srpaulo		ucl_add_tabs (func, tabs, compact);
198209158Srpaulo	}
199189251Ssam	if (compact) {
200189251Ssam		func->ucl_emitter_append_character ('[', 1, func->ud);
201189251Ssam	}
202189251Ssam	else {
203189251Ssam		func->ucl_emitter_append_len ("[\n", 2, func->ud);
204209158Srpaulo	}
205209158Srpaulo	while (cur) {
206252726Srpaulo		ucl_elt_write_json (cur, func, tabs + 1, true, compact);
207252726Srpaulo		if (cur->next != NULL) {
208252726Srpaulo			if (compact) {
209189251Ssam				func->ucl_emitter_append_character (',', 1, func->ud);
210189251Ssam			}
211189251Ssam			else {
212189251Ssam				func->ucl_emitter_append_len (",\n", 2, func->ud);
213189251Ssam			}
214189251Ssam		}
215189251Ssam		else if (!compact) {
216189251Ssam			func->ucl_emitter_append_character ('\n', 1, func->ud);
217189251Ssam		}
218189251Ssam		cur = cur->next;
219189251Ssam	}
220189251Ssam	ucl_add_tabs (func, tabs, compact);
221214734Srpaulo	func->ucl_emitter_append_character (']', 1, func->ud);
222214734Srpaulo}
223189251Ssam
224189251Ssam/**
225189251Ssam * Emit a single element
226189251Ssam * @param obj object
227189251Ssam * @param buf buffer
228189251Ssam */
229189251Ssamstatic void
230189251Ssamucl_elt_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
231189251Ssam		unsigned int tabs, bool start_tabs, bool compact)
232209158Srpaulo{
233209158Srpaulo	bool flag;
234209158Srpaulo
235209158Srpaulo	switch (obj->type) {
236209158Srpaulo	case UCL_INT:
237209158Srpaulo		if (start_tabs) {
238209158Srpaulo			ucl_add_tabs (func, tabs, compact);
239209158Srpaulo		}
240209158Srpaulo		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
241209158Srpaulo		break;
242209158Srpaulo	case UCL_FLOAT:
243209158Srpaulo	case UCL_TIME:
244209158Srpaulo		if (start_tabs) {
245209158Srpaulo			ucl_add_tabs (func, tabs, compact);
246209158Srpaulo		}
247209158Srpaulo		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
248209158Srpaulo		break;
249209158Srpaulo	case UCL_BOOLEAN:
250209158Srpaulo		if (start_tabs) {
251209158Srpaulo			ucl_add_tabs (func, tabs, compact);
252209158Srpaulo		}
253189251Ssam		flag = ucl_object_toboolean (obj);
254209158Srpaulo		if (flag) {
255209158Srpaulo			func->ucl_emitter_append_len ("true", 4, func->ud);
256189251Ssam		}
257189251Ssam		else {
258189251Ssam			func->ucl_emitter_append_len ("false", 5, func->ud);
259252726Srpaulo		}
260252726Srpaulo		break;
261252726Srpaulo	case UCL_STRING:
262252726Srpaulo		if (start_tabs) {
263252726Srpaulo			ucl_add_tabs (func, tabs, compact);
264252726Srpaulo		}
265252726Srpaulo		ucl_elt_string_write_json (obj->value.sv, obj->len, func);
266252726Srpaulo		break;
267252726Srpaulo	case UCL_NULL:
268189251Ssam		if (start_tabs) {
269189251Ssam			ucl_add_tabs (func, tabs, compact);
270189251Ssam		}
271252726Srpaulo		func->ucl_emitter_append_len ("null", 4, func->ud);
272252726Srpaulo		break;
273252726Srpaulo	case UCL_OBJECT:
274252726Srpaulo		ucl_elt_obj_write_json (obj, func, tabs, start_tabs, compact);
275252726Srpaulo		break;
276252726Srpaulo	case UCL_ARRAY:
277252726Srpaulo		ucl_elt_array_write_json (obj->value.av, func, tabs, start_tabs, compact);
278189251Ssam		break;
279189251Ssam	case UCL_USERDATA:
280189251Ssam		break;
281189251Ssam	}
282189251Ssam}
283189251Ssam
284189251Ssam/**
285252726Srpaulo * Write a single object to the buffer
286252726Srpaulo * @param obj object
287252726Srpaulo * @param buf target buffer
288252726Srpaulo */
289252726Srpaulostatic void
290252726Srpauloucl_obj_write_json (ucl_object_t *obj, struct ucl_emitter_functions *func,
291252726Srpaulo		unsigned int tabs, bool start_tabs, bool compact)
292189251Ssam{
293189251Ssam	ucl_object_t *cur;
294189251Ssam	bool is_array = (obj->next != NULL);
295189251Ssam
296189251Ssam	if (is_array) {
297189251Ssam		/* This is an array actually */
298214734Srpaulo		if (start_tabs) {
299189251Ssam			ucl_add_tabs (func, tabs, compact);
300189251Ssam		}
301189251Ssam
302189251Ssam		if (compact) {
303189251Ssam			func->ucl_emitter_append_character ('[', 1, func->ud);
304189251Ssam		}
305189251Ssam		else {
306189251Ssam			func->ucl_emitter_append_len ("[\n", 2, func->ud);
307189251Ssam		}
308189251Ssam		cur = obj;
309189251Ssam		while (cur != NULL) {
310189251Ssam			ucl_elt_write_json (cur, func, tabs + 1, true, compact);
311189251Ssam			if (cur->next) {
312189251Ssam				func->ucl_emitter_append_character (',', 1, func->ud);
313189251Ssam			}
314209158Srpaulo			if (!compact) {
315209158Srpaulo				func->ucl_emitter_append_character ('\n', 1, func->ud);
316209158Srpaulo			}
317209158Srpaulo			cur = cur->next;
318209158Srpaulo		}
319209158Srpaulo		ucl_add_tabs (func, tabs, compact);
320209158Srpaulo		func->ucl_emitter_append_character (']', 1, func->ud);
321209158Srpaulo	}
322209158Srpaulo	else {
323209158Srpaulo		ucl_elt_write_json (obj, func, tabs, start_tabs, compact);
324209158Srpaulo	}
325209158Srpaulo
326209158Srpaulo}
327209158Srpaulo
328209158Srpaulo/**
329209158Srpaulo * Emit an object to json
330209158Srpaulo * @param obj object
331209158Srpaulo * @return json output (should be freed after using)
332209158Srpaulo */
333209158Srpaulostatic void
334209158Srpauloucl_object_emit_json (ucl_object_t *obj, bool compact, struct ucl_emitter_functions *func)
335209158Srpaulo{
336209158Srpaulo	ucl_obj_write_json (obj, func, 0, false, compact);
337209158Srpaulo}
338209158Srpaulo
339209158Srpaulo/**
340189251Ssam * Write a single object to the buffer
341209158Srpaulo * @param obj object to write
342189251Ssam * @param buf target buffer
343209158Srpaulo */
344189251Ssamstatic void
345189251Ssamucl_elt_obj_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
346189251Ssam		unsigned int tabs, bool start_tabs, bool is_top)
347189251Ssam{
348189251Ssam	ucl_object_t *cur, *cur_obj;
349189251Ssam	ucl_hash_iter_t it = NULL;
350189251Ssam
351189251Ssam	if (start_tabs) {
352189251Ssam		ucl_add_tabs (func, tabs, is_top);
353209158Srpaulo	}
354189251Ssam	if (!is_top) {
355189251Ssam		func->ucl_emitter_append_len ("{\n", 2, func->ud);
356189251Ssam	}
357189251Ssam
358252726Srpaulo	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
359252726Srpaulo		LL_FOREACH (cur, cur_obj) {
360252726Srpaulo			ucl_add_tabs (func, tabs + 1, is_top);
361252726Srpaulo			if (cur_obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
362252726Srpaulo				ucl_elt_string_write_json (cur_obj->key, cur_obj->keylen, func);
363252726Srpaulo			}
364252726Srpaulo			else {
365252726Srpaulo				func->ucl_emitter_append_len (cur_obj->key, cur_obj->keylen, func->ud);
366252726Srpaulo			}
367252726Srpaulo			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
368189251Ssam				func->ucl_emitter_append_len (" = ", 3, func->ud);
369189251Ssam			}
370189251Ssam			else {
371189251Ssam				func->ucl_emitter_append_character (' ', 1, func->ud);
372189251Ssam			}
373189251Ssam			ucl_elt_write_config (cur_obj, func,
374189251Ssam					is_top ? tabs : tabs + 1,
375189251Ssam					false, false, false);
376189251Ssam			if (cur_obj->type != UCL_OBJECT && cur_obj->type != UCL_ARRAY) {
377189251Ssam				func->ucl_emitter_append_len (";\n", 2, func->ud);
378189251Ssam			}
379189251Ssam			else {
380189251Ssam				func->ucl_emitter_append_character ('\n', 1, func->ud);
381189251Ssam			}
382189251Ssam		}
383189251Ssam	}
384189251Ssam
385189251Ssam	ucl_add_tabs (func, tabs, is_top);
386189251Ssam	if (!is_top) {
387189251Ssam		func->ucl_emitter_append_character ('}', 1, func->ud);
388189251Ssam	}
389189251Ssam}
390189251Ssam
391189251Ssam/**
392189251Ssam * Write a single array to the buffer
393189251Ssam * @param obj array to write
394189251Ssam * @param buf target buffer
395252726Srpaulo */
396189251Ssamstatic void
397189251Ssamucl_elt_array_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
398189251Ssam		unsigned int tabs, bool start_tabs, bool is_top)
399189251Ssam{
400189251Ssam	ucl_object_t *cur = obj;
401189251Ssam
402189251Ssam	if (start_tabs) {
403189251Ssam		ucl_add_tabs (func, tabs, false);
404252726Srpaulo	}
405189251Ssam
406189251Ssam	func->ucl_emitter_append_len ("[\n", 2, func->ud);
407189251Ssam	while (cur) {
408189251Ssam		ucl_elt_write_config (cur, func, tabs + 1, true, false, false);
409189251Ssam		func->ucl_emitter_append_len (",\n", 2, func->ud);
410189251Ssam		cur = cur->next;
411189251Ssam	}
412189251Ssam	ucl_add_tabs (func, tabs, false);
413209158Srpaulo	func->ucl_emitter_append_character (']', 1, func->ud);
414209158Srpaulo}
415252726Srpaulo
416252726Srpaulo/**
417252726Srpaulo * Emit a single element
418189251Ssam * @param obj object
419189251Ssam * @param buf buffer
420189251Ssam */
421189251Ssamstatic void
422189251Ssamucl_elt_write_config (ucl_object_t *obj, struct ucl_emitter_functions *func,
423189251Ssam		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
424189251Ssam{
425189251Ssam	bool flag;
426252726Srpaulo
427252726Srpaulo	if (expand_array && obj->next != NULL) {
428252726Srpaulo		ucl_elt_array_write_config (obj, func, tabs, start_tabs, is_top);
429252726Srpaulo	}
430252726Srpaulo	else {
431252726Srpaulo		switch (obj->type) {
432252726Srpaulo		case UCL_INT:
433189251Ssam			if (start_tabs) {
434189251Ssam				ucl_add_tabs (func, tabs, false);
435189251Ssam			}
436189251Ssam			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
437252726Srpaulo			break;
438252726Srpaulo		case UCL_FLOAT:
439252726Srpaulo		case UCL_TIME:
440252726Srpaulo			if (start_tabs) {
441252726Srpaulo				ucl_add_tabs (func, tabs, false);
442252726Srpaulo			}
443252726Srpaulo			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
444252726Srpaulo			break;
445252726Srpaulo		case UCL_BOOLEAN:
446189251Ssam			if (start_tabs) {
447189251Ssam				ucl_add_tabs (func, tabs, false);
448189251Ssam			}
449189251Ssam			flag = ucl_object_toboolean (obj);
450189251Ssam			if (flag) {
451189251Ssam				func->ucl_emitter_append_len ("true", 4, func->ud);
452214734Srpaulo			}
453252726Srpaulo			else {
454252726Srpaulo				func->ucl_emitter_append_len ("false", 5, func->ud);
455252726Srpaulo			}
456252726Srpaulo			break;
457252726Srpaulo		case UCL_STRING:
458252726Srpaulo			if (start_tabs) {
459252726Srpaulo				ucl_add_tabs (func, tabs, false);
460252726Srpaulo			}
461252726Srpaulo			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
462252726Srpaulo			break;
463252726Srpaulo		case UCL_NULL:
464252726Srpaulo			if (start_tabs) {
465252726Srpaulo				ucl_add_tabs (func, tabs, false);
466252726Srpaulo			}
467252726Srpaulo			func->ucl_emitter_append_len ("null", 4, func->ud);
468189251Ssam			break;
469189251Ssam		case UCL_OBJECT:
470189251Ssam			ucl_elt_obj_write_config (obj, func, tabs, start_tabs, is_top);
471252726Srpaulo			break;
472252726Srpaulo		case UCL_ARRAY:
473252726Srpaulo			ucl_elt_array_write_config (obj->value.av, func, tabs, start_tabs, is_top);
474252726Srpaulo			break;
475252726Srpaulo		case UCL_USERDATA:
476252726Srpaulo			break;
477189251Ssam		}
478189251Ssam	}
479189251Ssam}
480252726Srpaulo
481252726Srpaulo/**
482252726Srpaulo * Emit an object to rcl
483252726Srpaulo * @param obj object
484252726Srpaulo * @return rcl output (should be freed after using)
485252726Srpaulo */
486252726Srpaulostatic void
487252726Srpauloucl_object_emit_config (ucl_object_t *obj, struct ucl_emitter_functions *func)
488252726Srpaulo{
489252726Srpaulo	ucl_elt_write_config (obj, func, 0, false, true, true);
490252726Srpaulo}
491252726Srpaulo
492252726Srpaulo
493252726Srpaulostatic void
494252726Srpauloucl_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
495252726Srpaulo		unsigned int tabs, bool start_tabs)
496252726Srpaulo{
497252726Srpaulo	bool is_array = (obj->next != NULL);
498252726Srpaulo
499252726Srpaulo	if (is_array) {
500252726Srpaulo		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, false);
501189251Ssam	}
502214734Srpaulo	else {
503252726Srpaulo		ucl_elt_write_yaml (obj, func, tabs, start_tabs, false, true);
504252726Srpaulo	}
505252726Srpaulo}
506189251Ssam
507189251Ssam/**
508189251Ssam * Write a single object to the buffer
509252726Srpaulo * @param obj object to write
510252726Srpaulo * @param buf target buffer
511252726Srpaulo */
512252726Srpaulostatic void
513252726Srpauloucl_elt_obj_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
514252726Srpaulo		unsigned int tabs, bool start_tabs, bool is_top)
515252726Srpaulo{
516252726Srpaulo	ucl_object_t *cur;
517252726Srpaulo	ucl_hash_iter_t it = NULL;
518252726Srpaulo
519252726Srpaulo	if (start_tabs) {
520252726Srpaulo		ucl_add_tabs (func, tabs, is_top);
521252726Srpaulo	}
522252726Srpaulo	if (!is_top) {
523252726Srpaulo		func->ucl_emitter_append_len ("{\n", 2, func->ud);
524252726Srpaulo	}
525252726Srpaulo
526252726Srpaulo	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
527252726Srpaulo		ucl_add_tabs (func, tabs + 1, is_top);
528252726Srpaulo		if (cur->keylen > 0) {
529252726Srpaulo			ucl_elt_string_write_json (cur->key, cur->keylen, func);
530252726Srpaulo		}
531252726Srpaulo		else {
532252726Srpaulo			func->ucl_emitter_append_len ("null", 4, func->ud);
533252726Srpaulo		}
534252726Srpaulo		func->ucl_emitter_append_len (": ", 2, func->ud);
535252726Srpaulo		ucl_obj_write_yaml (cur, func, is_top ? tabs : tabs + 1, false);
536252726Srpaulo		if (ucl_hash_iter_has_next(it)) {
537252726Srpaulo			if (!is_top) {
538252726Srpaulo				func->ucl_emitter_append_len (",\n", 2, func->ud);
539252726Srpaulo			}
540252726Srpaulo			else {
541252726Srpaulo				func->ucl_emitter_append_character ('\n', 1, func->ud);
542252726Srpaulo			}
543252726Srpaulo		}
544252726Srpaulo		else {
545252726Srpaulo			func->ucl_emitter_append_character ('\n', 1, func->ud);
546252726Srpaulo		}
547189251Ssam	}
548189251Ssam
549189251Ssam	ucl_add_tabs (func, tabs, is_top);
550189251Ssam	if (!is_top) {
551214734Srpaulo		func->ucl_emitter_append_character ('}', 1, func->ud);
552252726Srpaulo	}
553252726Srpaulo}
554252726Srpaulo
555252726Srpaulo/**
556252726Srpaulo * Write a single array to the buffer
557252726Srpaulo * @param obj array to write
558252726Srpaulo * @param buf target buffer
559252726Srpaulo */
560252726Srpaulostatic void
561252726Srpauloucl_elt_array_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
562252726Srpaulo		unsigned int tabs, bool start_tabs, bool is_top)
563252726Srpaulo{
564189251Ssam	ucl_object_t *cur = obj;
565189251Ssam
566189251Ssam	if (start_tabs) {
567214734Srpaulo		ucl_add_tabs (func, tabs, false);
568214734Srpaulo	}
569214734Srpaulo
570214734Srpaulo	func->ucl_emitter_append_len ("[\n", 2, func->ud);
571214734Srpaulo	while (cur) {
572214734Srpaulo		ucl_elt_write_yaml (cur, func, tabs + 1, true, false, false);
573214734Srpaulo		func->ucl_emitter_append_len (",\n", 2, func->ud);
574214734Srpaulo		cur = cur->next;
575214734Srpaulo	}
576214734Srpaulo	ucl_add_tabs (func, tabs, false);
577214734Srpaulo	func->ucl_emitter_append_character (']', 1, func->ud);
578214734Srpaulo}
579214734Srpaulo
580214734Srpaulo/**
581214734Srpaulo * Emit a single element
582214734Srpaulo * @param obj object
583214734Srpaulo * @param buf buffer
584214734Srpaulo */
585214734Srpaulostatic void
586214734Srpauloucl_elt_write_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func,
587214734Srpaulo		unsigned int tabs, bool start_tabs, bool is_top, bool expand_array)
588214734Srpaulo{
589214734Srpaulo	bool flag;
590214734Srpaulo
591214734Srpaulo	if (expand_array && obj->next != NULL ) {
592214734Srpaulo		ucl_elt_array_write_yaml (obj, func, tabs, start_tabs, is_top);
593214734Srpaulo	}
594214734Srpaulo	else {
595214734Srpaulo		switch (obj->type) {
596214734Srpaulo		case UCL_INT:
597214734Srpaulo			if (start_tabs) {
598214734Srpaulo				ucl_add_tabs (func, tabs, false);
599214734Srpaulo			}
600214734Srpaulo			func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
601214734Srpaulo			break;
602214734Srpaulo		case UCL_FLOAT:
603214734Srpaulo		case UCL_TIME:
604214734Srpaulo			if (start_tabs) {
605214734Srpaulo				ucl_add_tabs (func, tabs, false);
606214734Srpaulo			}
607214734Srpaulo			func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
608214734Srpaulo			break;
609214734Srpaulo		case UCL_BOOLEAN:
610214734Srpaulo			if (start_tabs) {
611214734Srpaulo				ucl_add_tabs (func, tabs, false);
612214734Srpaulo			}
613214734Srpaulo			flag = ucl_object_toboolean (obj);
614214734Srpaulo			if (flag) {
615214734Srpaulo				func->ucl_emitter_append_len ("true", 4, func->ud);
616214734Srpaulo			}
617214734Srpaulo			else {
618214734Srpaulo				func->ucl_emitter_append_len ("false", 5, func->ud);
619214734Srpaulo			}
620214734Srpaulo			break;
621214734Srpaulo		case UCL_STRING:
622214734Srpaulo			if (start_tabs) {
623214734Srpaulo				ucl_add_tabs (func, tabs, false);
624214734Srpaulo			}
625214734Srpaulo			ucl_elt_string_write_json (obj->value.sv, obj->len, func);
626214734Srpaulo			break;
627214734Srpaulo		case UCL_NULL:
628214734Srpaulo			if (start_tabs) {
629214734Srpaulo				ucl_add_tabs (func, tabs, false);
630214734Srpaulo			}
631214734Srpaulo			func->ucl_emitter_append_len ("null", 4, func->ud);
632214734Srpaulo			break;
633214734Srpaulo		case UCL_OBJECT:
634214734Srpaulo			ucl_elt_obj_write_yaml (obj, func, tabs, start_tabs, is_top);
635214734Srpaulo			break;
636214734Srpaulo		case UCL_ARRAY:
637252726Srpaulo			ucl_elt_array_write_yaml (obj->value.av, func, tabs, start_tabs, is_top);
638252726Srpaulo			break;
639252726Srpaulo		case UCL_USERDATA:
640252726Srpaulo			break;
641252726Srpaulo		}
642252726Srpaulo	}
643252726Srpaulo}
644252726Srpaulo
645252726Srpaulo/**
646252726Srpaulo * Emit an object to rcl
647252726Srpaulo * @param obj object
648252726Srpaulo * @return rcl output (should be freed after using)
649252726Srpaulo */
650252726Srpaulostatic void
651252726Srpauloucl_object_emit_yaml (ucl_object_t *obj, struct ucl_emitter_functions *func)
652252726Srpaulo{
653252726Srpaulo	ucl_elt_write_yaml (obj, func, 0, false, true, true);
654252726Srpaulo}
655252726Srpaulo
656252726Srpaulo/*
657252726Srpaulo * Generic utstring output
658252726Srpaulo */
659252726Srpaulostatic int
660252726Srpauloucl_utstring_append_character (unsigned char c, size_t len, void *ud)
661252726Srpaulo{
662252726Srpaulo	UT_string *buf = ud;
663252726Srpaulo
664252726Srpaulo	if (len == 1) {
665252726Srpaulo		utstring_append_c (buf, c);
666252726Srpaulo	}
667252726Srpaulo	else {
668252726Srpaulo		utstring_reserve (buf, len);
669252726Srpaulo		memset (&buf->d[buf->i], c, len);
670252726Srpaulo		buf->i += len;
671252726Srpaulo		buf->d[buf->i] = '\0';
672252726Srpaulo	}
673252726Srpaulo
674252726Srpaulo	return 0;
675252726Srpaulo}
676252726Srpaulo
677252726Srpaulostatic int
678252726Srpauloucl_utstring_append_len (const unsigned char *str, size_t len, void *ud)
679252726Srpaulo{
680252726Srpaulo	UT_string *buf = ud;
681252726Srpaulo
682252726Srpaulo	utstring_append_len (buf, str, len);
683252726Srpaulo
684252726Srpaulo	return 0;
685252726Srpaulo}
686252726Srpaulo
687252726Srpaulostatic int
688252726Srpauloucl_utstring_append_int (int64_t val, void *ud)
689252726Srpaulo{
690189251Ssam	UT_string *buf = ud;
691189251Ssam
692189251Ssam	utstring_printf (buf, "%jd", (intmax_t)val);
693189251Ssam	return 0;
694189251Ssam}
695189251Ssam
696189251Ssamstatic int
697189251Ssamucl_utstring_append_double (double val, void *ud)
698189251Ssam{
699189251Ssam	UT_string *buf = ud;
700189251Ssam	const double delta = 0.0000001;
701189251Ssam
702189251Ssam	if (val == (double)(int)val) {
703189251Ssam		utstring_printf (buf, "%.1lf", val);
704189251Ssam	}
705252726Srpaulo	else if (fabs (val - (double)(int)val) < delta) {
706252726Srpaulo		/* Write at maximum precision */
707252726Srpaulo		utstring_printf (buf, "%.*lg", DBL_DIG, val);
708252726Srpaulo	}
709189251Ssam	else {
710209158Srpaulo		utstring_printf (buf, "%lf", val);
711209158Srpaulo	}
712209158Srpaulo
713209158Srpaulo	return 0;
714214734Srpaulo}
715214734Srpaulo
716214734Srpaulo
717214734Srpaulounsigned char *
718214734Srpauloucl_object_emit (ucl_object_t *obj, enum ucl_emitter emit_type)
719214734Srpaulo{
720214734Srpaulo	UT_string *buf = NULL;
721214734Srpaulo	unsigned char *res = NULL;
722214734Srpaulo	struct ucl_emitter_functions func = {
723214734Srpaulo		.ucl_emitter_append_character = ucl_utstring_append_character,
724214734Srpaulo		.ucl_emitter_append_len = ucl_utstring_append_len,
725214734Srpaulo		.ucl_emitter_append_int = ucl_utstring_append_int,
726214734Srpaulo		.ucl_emitter_append_double = ucl_utstring_append_double
727214734Srpaulo	};
728252726Srpaulo
729252726Srpaulo	if (obj == NULL) {
730252726Srpaulo		return NULL;
731252726Srpaulo	}
732252726Srpaulo
733252726Srpaulo	utstring_new (buf);
734252726Srpaulo	func.ud = buf;
735252726Srpaulo
736252726Srpaulo	if (buf != NULL) {
737252726Srpaulo		if (emit_type == UCL_EMIT_JSON) {
738189251Ssam			ucl_object_emit_json (obj, false, &func);
739189251Ssam		}
740189251Ssam		else if (emit_type == UCL_EMIT_JSON_COMPACT) {
741189251Ssam			ucl_object_emit_json (obj, true, &func);
742189251Ssam		}
743189251Ssam		else if (emit_type == UCL_EMIT_YAML) {
744189251Ssam			ucl_object_emit_yaml (obj, &func);
745189251Ssam		}
746189251Ssam		else {
747189251Ssam			ucl_object_emit_config (obj, &func);
748189251Ssam		}
749189251Ssam
750189251Ssam		res = utstring_body (buf);
751189251Ssam		free (buf);
752189251Ssam	}
753189251Ssam
754189251Ssam	return res;
755252726Srpaulo}
756189251Ssam
757252726Srpaulobool
758252726Srpauloucl_object_emit_full (ucl_object_t *obj, enum ucl_emitter emit_type,
759252726Srpaulo		struct ucl_emitter_functions *emitter)
760252726Srpaulo{
761252726Srpaulo	if (emit_type == UCL_EMIT_JSON) {
762189251Ssam		ucl_object_emit_json (obj, false, emitter);
763189251Ssam	}
764189251Ssam	else if (emit_type == UCL_EMIT_JSON_COMPACT) {
765189251Ssam		ucl_object_emit_json (obj, true, emitter);
766189251Ssam	}
767189251Ssam	else if (emit_type == UCL_EMIT_YAML) {
768214734Srpaulo		ucl_object_emit_yaml (obj, emitter);
769189251Ssam	}
770214734Srpaulo	else {
771214734Srpaulo		ucl_object_emit_config (obj, emitter);
772214734Srpaulo	}
773189251Ssam
774214734Srpaulo	/* XXX: need some error checks here */
775189251Ssam	return true;
776189251Ssam}
777189251Ssam
778214734Srpaulo
779252726Srpaulounsigned char *
780252726Srpauloucl_object_emit_single_json (ucl_object_t *obj)
781252726Srpaulo{
782252726Srpaulo	UT_string *buf = NULL;
783252726Srpaulo	unsigned char *res = NULL;
784214734Srpaulo
785189251Ssam	if (obj == NULL) {
786214734Srpaulo		return NULL;
787189251Ssam	}
788252726Srpaulo
789252726Srpaulo	utstring_new (buf);
790189251Ssam
791189251Ssam	if (buf != NULL) {
792189251Ssam		switch (obj->type) {
793189251Ssam		case UCL_OBJECT:
794189251Ssam			ucl_utstring_append_len ("object", 6, buf);
795189251Ssam			break;
796252726Srpaulo		case UCL_ARRAY:
797252726Srpaulo			ucl_utstring_append_len ("array", 5, buf);
798189251Ssam			break;
799189251Ssam		case UCL_INT:
800189251Ssam			ucl_utstring_append_int (obj->value.iv, buf);
801189251Ssam			break;
802189251Ssam		case UCL_FLOAT:
803189251Ssam		case UCL_TIME:
804189251Ssam			ucl_utstring_append_double (obj->value.dv, buf);
805189251Ssam			break;
806189251Ssam		case UCL_NULL:
807189251Ssam			ucl_utstring_append_len ("null", 4, buf);
808189251Ssam			break;
809189251Ssam		case UCL_BOOLEAN:
810214734Srpaulo			if (obj->value.iv) {
811189251Ssam				ucl_utstring_append_len ("true", 4, buf);
812252726Srpaulo			}
813189251Ssam			else {
814189251Ssam				ucl_utstring_append_len ("false", 5, buf);
815189251Ssam			}
816189251Ssam			break;
817189251Ssam		case UCL_STRING:
818214734Srpaulo			ucl_utstring_append_len (obj->value.sv, obj->len, buf);
819189251Ssam			break;
820189251Ssam		case UCL_USERDATA:
821189251Ssam			ucl_utstring_append_len ("userdata", 8, buf);
822189251Ssam			break;
823189251Ssam		}
824252726Srpaulo		res = utstring_body (buf);
825214734Srpaulo		free (buf);
826209158Srpaulo	}
827252726Srpaulo
828189251Ssam	return res;
829189251Ssam}
830189251Ssam