xref: /freebsd/contrib/libucl/src/ucl_emitter.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
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 ucl_emitter.c
40  * Serialise UCL object to various of output formats
41  */
42 
43 static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
44 		const ucl_object_t *obj, bool first, bool print_key, bool compact);
45 
46 #define UCL_EMIT_TYPE_OPS(type)		\
47 	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
48 		const ucl_object_t *obj, bool first, bool print_key);	\
49 	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
50 		const ucl_object_t *obj, bool print_key);	\
51 	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
52 		const ucl_object_t *obj, bool print_key);	\
53 	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
54 		const ucl_object_t *obj);	\
55 	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
56 		const ucl_object_t *obj)
57 
58 /*
59  * JSON format operations
60  */
61 UCL_EMIT_TYPE_OPS(json);
62 UCL_EMIT_TYPE_OPS(json_compact);
63 UCL_EMIT_TYPE_OPS(config);
64 UCL_EMIT_TYPE_OPS(yaml);
65 
66 #define UCL_EMIT_TYPE_CONTENT(type) {	\
67 	.ucl_emitter_write_elt = ucl_emit_ ## type ## _elt,	\
68 	.ucl_emitter_start_object = ucl_emit_ ## type ##_start_obj,	\
69 	.ucl_emitter_start_array = ucl_emit_ ## type ##_start_array,	\
70 	.ucl_emitter_end_object = ucl_emit_ ## type ##_end_object,	\
71 	.ucl_emitter_end_array = ucl_emit_ ## type ##_end_array	\
72 }
73 
74 
75 const struct ucl_emitter_operations ucl_standartd_emitter_ops[] = {
76 	[UCL_EMIT_JSON] = UCL_EMIT_TYPE_CONTENT(json),
77 	[UCL_EMIT_JSON_COMPACT] = UCL_EMIT_TYPE_CONTENT(json_compact),
78 	[UCL_EMIT_CONFIG] = UCL_EMIT_TYPE_CONTENT(config),
79 	[UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml)
80 };
81 
82 /*
83  * Utility to check whether we need a top object
84  */
85 #define UCL_EMIT_IDENT_TOP_OBJ(ctx, obj) ((ctx)->top != (obj) || \
86 		((ctx)->id == UCL_EMIT_JSON_COMPACT || (ctx)->id == UCL_EMIT_JSON))
87 
88 
89 /**
90  * Add tabulation to the output buffer
91  * @param buf target buffer
92  * @param tabs number of tabs to add
93  */
94 static inline void
95 ucl_add_tabs (const struct ucl_emitter_functions *func, unsigned int tabs,
96 		bool compact)
97 {
98 	if (!compact && tabs > 0) {
99 		func->ucl_emitter_append_character (' ', tabs * 4, func->ud);
100 	}
101 }
102 
103 /**
104  * Print key for the element
105  * @param ctx
106  * @param obj
107  */
108 static void
109 ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx,
110 		const ucl_object_t *obj, bool compact)
111 {
112 	const struct ucl_emitter_functions *func = ctx->func;
113 
114 	if (!print_key) {
115 		return;
116 	}
117 
118 	if (ctx->id == UCL_EMIT_CONFIG) {
119 		if (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE) {
120 			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
121 		}
122 		else {
123 			func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
124 		}
125 
126 		if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
127 			func->ucl_emitter_append_len (" = ", 3, func->ud);
128 		}
129 		else {
130 			func->ucl_emitter_append_character (' ', 1, func->ud);
131 		}
132 	}
133 	else if (ctx->id == UCL_EMIT_YAML) {
134 		if (obj->keylen > 0 && (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE)) {
135 			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
136 		}
137 		else if (obj->keylen > 0) {
138 			func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
139 		}
140 		else {
141 			func->ucl_emitter_append_len ("null", 4, func->ud);
142 		}
143 
144 		func->ucl_emitter_append_len (": ", 2, func->ud);
145 	}
146 	else {
147 		if (obj->keylen > 0) {
148 			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
149 		}
150 		else {
151 			func->ucl_emitter_append_len ("null", 4, func->ud);
152 		}
153 
154 		if (compact) {
155 			func->ucl_emitter_append_character (':', 1, func->ud);
156 		}
157 		else {
158 			func->ucl_emitter_append_len (": ", 2, func->ud);
159 		}
160 	}
161 }
162 
163 static void
164 ucl_emitter_finish_object (struct ucl_emitter_context *ctx,
165 		const ucl_object_t *obj, bool compact, bool is_array)
166 {
167 	const struct ucl_emitter_functions *func = ctx->func;
168 
169 	if (ctx->id == UCL_EMIT_CONFIG && obj != ctx->top) {
170 		if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
171 			if (!is_array) {
172 				/* Objects are split by ';' */
173 				func->ucl_emitter_append_len (";\n", 2, func->ud);
174 			}
175 			else {
176 				/* Use commas for arrays */
177 				func->ucl_emitter_append_len (",\n", 2, func->ud);
178 			}
179 		}
180 		else {
181 			func->ucl_emitter_append_character ('\n', 1, func->ud);
182 		}
183 	}
184 }
185 
186 /**
187  * End standard ucl object
188  * @param ctx emitter context
189  * @param compact compact flag
190  */
191 static void
192 ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
193 		const ucl_object_t *obj, bool compact)
194 {
195 	const struct ucl_emitter_functions *func = ctx->func;
196 
197 	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
198 		ctx->indent --;
199 		if (compact) {
200 			func->ucl_emitter_append_character ('}', 1, func->ud);
201 		}
202 		else {
203 			if (ctx->id != UCL_EMIT_CONFIG) {
204 				/* newline is already added for this format */
205 				func->ucl_emitter_append_character ('\n', 1, func->ud);
206 			}
207 			ucl_add_tabs (func, ctx->indent, compact);
208 			func->ucl_emitter_append_character ('}', 1, func->ud);
209 		}
210 	}
211 
212 	ucl_emitter_finish_object (ctx, obj, compact, false);
213 }
214 
215 /**
216  * End standard ucl array
217  * @param ctx emitter context
218  * @param compact compact flag
219  */
220 static void
221 ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
222 		const ucl_object_t *obj, bool compact)
223 {
224 	const struct ucl_emitter_functions *func = ctx->func;
225 
226 	ctx->indent --;
227 	if (compact) {
228 		func->ucl_emitter_append_character (']', 1, func->ud);
229 	}
230 	else {
231 		if (ctx->id != UCL_EMIT_CONFIG) {
232 			/* newline is already added for this format */
233 			func->ucl_emitter_append_character ('\n', 1, func->ud);
234 		}
235 		ucl_add_tabs (func, ctx->indent, compact);
236 		func->ucl_emitter_append_character (']', 1, func->ud);
237 	}
238 
239 	ucl_emitter_finish_object (ctx, obj, compact, true);
240 }
241 
242 /**
243  * Start emit standard UCL array
244  * @param ctx emitter context
245  * @param obj object to write
246  * @param compact compact flag
247  */
248 static void
249 ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
250 		const ucl_object_t *obj, bool print_key, bool compact)
251 {
252 	const ucl_object_t *cur;
253 	ucl_object_iter_t iter = NULL;
254 	const struct ucl_emitter_functions *func = ctx->func;
255 	bool first = true;
256 
257 	ucl_emitter_print_key (print_key, ctx, obj, compact);
258 
259 	if (compact) {
260 		func->ucl_emitter_append_character ('[', 1, func->ud);
261 	}
262 	else {
263 		func->ucl_emitter_append_len ("[\n", 2, func->ud);
264 	}
265 
266 	ctx->indent ++;
267 
268 	if (obj->type == UCL_ARRAY) {
269 		/* explicit array */
270 		while ((cur = ucl_iterate_object (obj, &iter, true)) != NULL) {
271 			ucl_emitter_common_elt (ctx, cur, first, false, compact);
272 			first = false;
273 		}
274 	}
275 	else {
276 		/* implicit array */
277 		cur = obj;
278 		while (cur) {
279 			ucl_emitter_common_elt (ctx, cur, first, false, compact);
280 			first = false;
281 			cur = cur->next;
282 		}
283 	}
284 
285 
286 }
287 
288 /**
289  * Start emit standard UCL object
290  * @param ctx emitter context
291  * @param obj object to write
292  * @param compact compact flag
293  */
294 static void
295 ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
296 		const ucl_object_t *obj, bool print_key, bool compact)
297 {
298 	ucl_hash_iter_t it = NULL;
299 	const ucl_object_t *cur, *elt;
300 	const struct ucl_emitter_functions *func = ctx->func;
301 	bool first = true;
302 
303 	ucl_emitter_print_key (print_key, ctx, obj, compact);
304 	/*
305 	 * Print <ident_level>{
306 	 * <ident_level + 1><object content>
307 	 */
308 	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
309 		if (compact) {
310 			func->ucl_emitter_append_character ('{', 1, func->ud);
311 		}
312 		else {
313 			func->ucl_emitter_append_len ("{\n", 2, func->ud);
314 		}
315 		ctx->indent ++;
316 	}
317 
318 	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
319 
320 		if (ctx->id == UCL_EMIT_CONFIG) {
321 			LL_FOREACH (cur, elt) {
322 				ucl_emitter_common_elt (ctx, elt, first, true, compact);
323 			}
324 		}
325 		else {
326 			/* Expand implicit arrays */
327 			if (cur->next != NULL) {
328 				if (!first) {
329 					if (compact) {
330 						func->ucl_emitter_append_character (',', 1, func->ud);
331 					}
332 					else {
333 						func->ucl_emitter_append_len (",\n", 2, func->ud);
334 					}
335 				}
336 				ucl_add_tabs (func, ctx->indent, compact);
337 				ucl_emitter_common_start_array (ctx, cur, true, compact);
338 				ucl_emitter_common_end_array (ctx, cur, compact);
339 			}
340 			else {
341 				ucl_emitter_common_elt (ctx, cur, first, true, compact);
342 			}
343 		}
344 
345 		first = false;
346 	}
347 }
348 
349 /**
350  * Common choice of object emitting
351  * @param ctx emitter context
352  * @param obj object to print
353  * @param first flag to mark the first element
354  * @param print_key print key of an object
355  * @param compact compact output
356  */
357 static void
358 ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
359 		const ucl_object_t *obj, bool first, bool print_key, bool compact)
360 {
361 	const struct ucl_emitter_functions *func = ctx->func;
362 	bool flag;
363 	struct ucl_object_userdata *ud;
364 	const char *ud_out = "";
365 
366 	if (ctx->id != UCL_EMIT_CONFIG && !first) {
367 		if (compact) {
368 			func->ucl_emitter_append_character (',', 1, func->ud);
369 		}
370 		else {
371 			if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
372 				func->ucl_emitter_append_len ("\n", 1, func->ud);
373 			} else {
374 				func->ucl_emitter_append_len (",\n", 2, func->ud);
375 			}
376 		}
377 	}
378 
379 	ucl_add_tabs (func, ctx->indent, compact);
380 
381 	switch (obj->type) {
382 	case UCL_INT:
383 		ucl_emitter_print_key (print_key, ctx, obj, compact);
384 		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
385 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
386 		break;
387 	case UCL_FLOAT:
388 	case UCL_TIME:
389 		ucl_emitter_print_key (print_key, ctx, obj, compact);
390 		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
391 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
392 		break;
393 	case UCL_BOOLEAN:
394 		ucl_emitter_print_key (print_key, ctx, obj, compact);
395 		flag = ucl_object_toboolean (obj);
396 		if (flag) {
397 			func->ucl_emitter_append_len ("true", 4, func->ud);
398 		}
399 		else {
400 			func->ucl_emitter_append_len ("false", 5, func->ud);
401 		}
402 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
403 		break;
404 	case UCL_STRING:
405 		ucl_emitter_print_key (print_key, ctx, obj, compact);
406 		if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) {
407 			ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
408 		}
409 		else {
410 			ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
411 		}
412 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
413 		break;
414 	case UCL_NULL:
415 		ucl_emitter_print_key (print_key, ctx, obj, compact);
416 		func->ucl_emitter_append_len ("null", 4, func->ud);
417 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
418 		break;
419 	case UCL_OBJECT:
420 		ucl_emitter_common_start_object (ctx, obj, print_key, compact);
421 		ucl_emitter_common_end_object (ctx, obj, compact);
422 		break;
423 	case UCL_ARRAY:
424 		ucl_emitter_common_start_array (ctx, obj, print_key, compact);
425 		ucl_emitter_common_end_array (ctx, obj, compact);
426 		break;
427 	case UCL_USERDATA:
428 		ud = (struct ucl_object_userdata *)obj;
429 		ucl_emitter_print_key (print_key, ctx, obj, compact);
430 		if (ud->emitter) {
431 			ud_out = ud->emitter (obj->value.ud);
432 			if (ud_out == NULL) {
433 				ud_out = "null";
434 			}
435 		}
436 		ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx);
437 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
438 		break;
439 	}
440 }
441 
442 /*
443  * Specific standard implementations of the emitter functions
444  */
445 #define UCL_EMIT_TYPE_IMPL(type, compact)		\
446 	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
447 		const ucl_object_t *obj, bool first, bool print_key) {	\
448 		ucl_emitter_common_elt (ctx, obj, first, print_key, (compact));	\
449 	}	\
450 	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
451 		const ucl_object_t *obj, bool print_key) {	\
452 		ucl_emitter_common_start_object (ctx, obj, print_key, (compact));	\
453 	}	\
454 	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
455 		const ucl_object_t *obj, bool print_key) {	\
456 		ucl_emitter_common_start_array (ctx, obj, print_key, (compact));	\
457 	}	\
458 	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
459 		const ucl_object_t *obj) {	\
460 		ucl_emitter_common_end_object (ctx, obj, (compact));	\
461 	}	\
462 	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
463 		const ucl_object_t *obj) {	\
464 		ucl_emitter_common_end_array (ctx, obj, (compact));	\
465 	}
466 
467 UCL_EMIT_TYPE_IMPL(json, false)
468 UCL_EMIT_TYPE_IMPL(json_compact, true)
469 UCL_EMIT_TYPE_IMPL(config, false)
470 UCL_EMIT_TYPE_IMPL(yaml, false)
471 
472 unsigned char *
473 ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
474 {
475 	unsigned char *res = NULL;
476 	struct ucl_emitter_functions *func;
477 	if (obj == NULL) {
478 		return NULL;
479 	}
480 
481 	func = ucl_object_emit_memory_funcs ((void **)&res);
482 
483 	if (func != NULL) {
484 		ucl_object_emit_full (obj, emit_type, func);
485 		ucl_object_emit_funcs_free (func);
486 	}
487 
488 	return res;
489 }
490 
491 bool
492 ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
493 		struct ucl_emitter_functions *emitter)
494 {
495 	const struct ucl_emitter_context *ctx;
496 	struct ucl_emitter_context my_ctx;
497 	bool res = false;
498 
499 	ctx = ucl_emit_get_standard_context (emit_type);
500 	if (ctx != NULL) {
501 		memcpy (&my_ctx, ctx, sizeof (my_ctx));
502 		my_ctx.func = emitter;
503 		my_ctx.indent = 0;
504 		my_ctx.top = obj;
505 
506 		my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
507 		res = true;
508 	}
509 
510 	return res;
511 }
512