xref: /freebsd/contrib/libucl/src/ucl_emitter.c (revision 0e97acdf58fe27b09c4824a474b0344daf997c5f)
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 {
134 		if (obj->keylen > 0) {
135 			ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
136 		}
137 		else {
138 			func->ucl_emitter_append_len ("null", 4, func->ud);
139 		}
140 
141 		if (compact) {
142 			func->ucl_emitter_append_character (':', 1, func->ud);
143 		}
144 		else {
145 			func->ucl_emitter_append_len (": ", 2, func->ud);
146 		}
147 	}
148 }
149 
150 static void
151 ucl_emitter_finish_object (struct ucl_emitter_context *ctx,
152 		const ucl_object_t *obj, bool compact, bool is_array)
153 {
154 	const struct ucl_emitter_functions *func = ctx->func;
155 
156 	if (ctx->id == UCL_EMIT_CONFIG && obj != ctx->top) {
157 		if (obj->type != UCL_OBJECT && obj->type != UCL_ARRAY) {
158 			if (!is_array) {
159 				/* Objects are split by ';' */
160 				func->ucl_emitter_append_len (";\n", 2, func->ud);
161 			}
162 			else {
163 				/* Use commas for arrays */
164 				func->ucl_emitter_append_len (",\n", 2, func->ud);
165 			}
166 		}
167 		else {
168 			func->ucl_emitter_append_character ('\n', 1, func->ud);
169 		}
170 	}
171 }
172 
173 /**
174  * End standard ucl object
175  * @param ctx emitter context
176  * @param compact compact flag
177  */
178 static void
179 ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
180 		const ucl_object_t *obj, bool compact)
181 {
182 	const struct ucl_emitter_functions *func = ctx->func;
183 
184 	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
185 		ctx->ident --;
186 		if (compact) {
187 			func->ucl_emitter_append_character ('}', 1, func->ud);
188 		}
189 		else {
190 			if (ctx->id != UCL_EMIT_CONFIG) {
191 				/* newline is already added for this format */
192 				func->ucl_emitter_append_character ('\n', 1, func->ud);
193 			}
194 			ucl_add_tabs (func, ctx->ident, compact);
195 			func->ucl_emitter_append_character ('}', 1, func->ud);
196 		}
197 	}
198 
199 	ucl_emitter_finish_object (ctx, obj, compact, false);
200 }
201 
202 /**
203  * End standard ucl array
204  * @param ctx emitter context
205  * @param compact compact flag
206  */
207 static void
208 ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
209 		const ucl_object_t *obj, bool compact)
210 {
211 	const struct ucl_emitter_functions *func = ctx->func;
212 
213 	ctx->ident --;
214 	if (compact) {
215 		func->ucl_emitter_append_character (']', 1, func->ud);
216 	}
217 	else {
218 		if (ctx->id != UCL_EMIT_CONFIG) {
219 			/* newline is already added for this format */
220 			func->ucl_emitter_append_character ('\n', 1, func->ud);
221 		}
222 		ucl_add_tabs (func, ctx->ident, compact);
223 		func->ucl_emitter_append_character (']', 1, func->ud);
224 	}
225 
226 	ucl_emitter_finish_object (ctx, obj, compact, true);
227 }
228 
229 /**
230  * Start emit standard UCL array
231  * @param ctx emitter context
232  * @param obj object to write
233  * @param compact compact flag
234  */
235 static void
236 ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
237 		const ucl_object_t *obj, bool print_key, bool compact)
238 {
239 	const ucl_object_t *cur;
240 	const struct ucl_emitter_functions *func = ctx->func;
241 	bool first = true;
242 
243 	ucl_emitter_print_key (print_key, ctx, obj, compact);
244 
245 	if (compact) {
246 		func->ucl_emitter_append_character ('[', 1, func->ud);
247 	}
248 	else {
249 		func->ucl_emitter_append_len ("[\n", 2, func->ud);
250 	}
251 
252 	ctx->ident ++;
253 
254 	if (obj->type == UCL_ARRAY) {
255 		/* explicit array */
256 		cur = obj->value.av;
257 	}
258 	else {
259 		/* implicit array */
260 		cur = obj;
261 	}
262 
263 	while (cur) {
264 		ucl_emitter_common_elt (ctx, cur, first, false, compact);
265 		first = false;
266 		cur = cur->next;
267 	}
268 }
269 
270 /**
271  * Start emit standard UCL object
272  * @param ctx emitter context
273  * @param obj object to write
274  * @param compact compact flag
275  */
276 static void
277 ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
278 		const ucl_object_t *obj, bool print_key, bool compact)
279 {
280 	ucl_hash_iter_t it = NULL;
281 	const ucl_object_t *cur, *elt;
282 	const struct ucl_emitter_functions *func = ctx->func;
283 	bool first = true;
284 
285 	ucl_emitter_print_key (print_key, ctx, obj, compact);
286 	/*
287 	 * Print <ident_level>{
288 	 * <ident_level + 1><object content>
289 	 */
290 	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
291 		if (compact) {
292 			func->ucl_emitter_append_character ('{', 1, func->ud);
293 		}
294 		else {
295 			func->ucl_emitter_append_len ("{\n", 2, func->ud);
296 		}
297 		ctx->ident ++;
298 	}
299 
300 	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
301 
302 		if (ctx->id == UCL_EMIT_CONFIG) {
303 			LL_FOREACH (cur, elt) {
304 				ucl_emitter_common_elt (ctx, elt, first, true, compact);
305 			}
306 		}
307 		else {
308 			/* Expand implicit arrays */
309 			if (cur->next != NULL) {
310 				if (!first) {
311 					if (compact) {
312 						func->ucl_emitter_append_character (',', 1, func->ud);
313 					}
314 					else {
315 						func->ucl_emitter_append_len (",\n", 2, func->ud);
316 					}
317 				}
318 				ucl_add_tabs (func, ctx->ident, compact);
319 				ucl_emitter_common_start_array (ctx, cur, true, compact);
320 				ucl_emitter_common_end_array (ctx, cur, compact);
321 			}
322 			else {
323 				ucl_emitter_common_elt (ctx, cur, first, true, compact);
324 			}
325 		}
326 
327 		first = false;
328 	}
329 }
330 
331 /**
332  * Common choice of object emitting
333  * @param ctx emitter context
334  * @param obj object to print
335  * @param first flag to mark the first element
336  * @param print_key print key of an object
337  * @param compact compact output
338  */
339 static void
340 ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
341 		const ucl_object_t *obj, bool first, bool print_key, bool compact)
342 {
343 	const struct ucl_emitter_functions *func = ctx->func;
344 	bool flag;
345 
346 	if (ctx->id != UCL_EMIT_CONFIG && !first) {
347 		if (compact) {
348 			func->ucl_emitter_append_character (',', 1, func->ud);
349 		}
350 		else {
351 			func->ucl_emitter_append_len (",\n", 2, func->ud);
352 		}
353 	}
354 
355 	ucl_add_tabs (func, ctx->ident, compact);
356 
357 	switch (obj->type) {
358 	case UCL_INT:
359 		ucl_emitter_print_key (print_key, ctx, obj, compact);
360 		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
361 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
362 		break;
363 	case UCL_FLOAT:
364 	case UCL_TIME:
365 		ucl_emitter_print_key (print_key, ctx, obj, compact);
366 		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
367 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
368 		break;
369 	case UCL_BOOLEAN:
370 		ucl_emitter_print_key (print_key, ctx, obj, compact);
371 		flag = ucl_object_toboolean (obj);
372 		if (flag) {
373 			func->ucl_emitter_append_len ("true", 4, func->ud);
374 		}
375 		else {
376 			func->ucl_emitter_append_len ("false", 5, func->ud);
377 		}
378 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
379 		break;
380 	case UCL_STRING:
381 		ucl_emitter_print_key (print_key, ctx, obj, compact);
382 		ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
383 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
384 		break;
385 	case UCL_NULL:
386 		ucl_emitter_print_key (print_key, ctx, obj, compact);
387 		func->ucl_emitter_append_len ("null", 4, func->ud);
388 		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
389 		break;
390 	case UCL_OBJECT:
391 		ucl_emitter_common_start_object (ctx, obj, print_key, compact);
392 		ucl_emitter_common_end_object (ctx, obj, compact);
393 		break;
394 	case UCL_ARRAY:
395 		ucl_emitter_common_start_array (ctx, obj, print_key, compact);
396 		ucl_emitter_common_end_array (ctx, obj, compact);
397 		break;
398 	case UCL_USERDATA:
399 		break;
400 	}
401 }
402 
403 /*
404  * Specific standard implementations of the emitter functions
405  */
406 #define UCL_EMIT_TYPE_IMPL(type, compact)		\
407 	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
408 		const ucl_object_t *obj, bool first, bool print_key) {	\
409 		ucl_emitter_common_elt (ctx, obj, first, print_key, (compact));	\
410 	}	\
411 	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
412 		const ucl_object_t *obj, bool print_key) {	\
413 		ucl_emitter_common_start_object (ctx, obj, print_key, (compact));	\
414 	}	\
415 	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
416 		const ucl_object_t *obj, bool print_key) {	\
417 		ucl_emitter_common_start_array (ctx, obj, print_key, (compact));	\
418 	}	\
419 	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
420 		const ucl_object_t *obj) {	\
421 		ucl_emitter_common_end_object (ctx, obj, (compact));	\
422 	}	\
423 	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
424 		const ucl_object_t *obj) {	\
425 		ucl_emitter_common_end_array (ctx, obj, (compact));	\
426 	}
427 
428 UCL_EMIT_TYPE_IMPL(json, false);
429 UCL_EMIT_TYPE_IMPL(json_compact, true);
430 UCL_EMIT_TYPE_IMPL(config, false);
431 UCL_EMIT_TYPE_IMPL(yaml, false);
432 
433 unsigned char *
434 ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
435 {
436 	unsigned char *res = NULL;
437 	struct ucl_emitter_functions *func;
438 	if (obj == NULL) {
439 		return NULL;
440 	}
441 
442 	func = ucl_object_emit_memory_funcs ((void **)&res);
443 
444 	if (func != NULL) {
445 		ucl_object_emit_full (obj, emit_type, func);
446 		ucl_object_emit_funcs_free (func);
447 	}
448 
449 	return res;
450 }
451 
452 bool
453 ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
454 		struct ucl_emitter_functions *emitter)
455 {
456 	const struct ucl_emitter_context *ctx;
457 	struct ucl_emitter_context my_ctx;
458 	bool res = false;
459 
460 	ctx = ucl_emit_get_standard_context (emit_type);
461 	if (ctx != NULL) {
462 		memcpy (&my_ctx, ctx, sizeof (my_ctx));
463 		my_ctx.func = emitter;
464 		my_ctx.ident = 0;
465 		my_ctx.top = obj;
466 
467 		my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
468 		res = true;
469 	}
470 
471 	return res;
472 }
473