ucl_emitter.c revision 275223
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
43static 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 */
61UCL_EMIT_TYPE_OPS(json);
62UCL_EMIT_TYPE_OPS(json_compact);
63UCL_EMIT_TYPE_OPS(config);
64UCL_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
75const 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 */
94static inline void
95ucl_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 */
108static void
109ucl_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
163static void
164ucl_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 */
191static void
192ucl_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 */
220static void
221ucl_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 */
248static void
249ucl_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	const struct ucl_emitter_functions *func = ctx->func;
254	bool first = true;
255
256	ucl_emitter_print_key (print_key, ctx, obj, compact);
257
258	if (compact) {
259		func->ucl_emitter_append_character ('[', 1, func->ud);
260	}
261	else {
262		func->ucl_emitter_append_len ("[\n", 2, func->ud);
263	}
264
265	ctx->indent ++;
266
267	if (obj->type == UCL_ARRAY) {
268		/* explicit array */
269		cur = obj->value.av;
270	}
271	else {
272		/* implicit array */
273		cur = obj;
274	}
275
276	while (cur) {
277		ucl_emitter_common_elt (ctx, cur, first, false, compact);
278		first = false;
279		cur = cur->next;
280	}
281}
282
283/**
284 * Start emit standard UCL object
285 * @param ctx emitter context
286 * @param obj object to write
287 * @param compact compact flag
288 */
289static void
290ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
291		const ucl_object_t *obj, bool print_key, bool compact)
292{
293	ucl_hash_iter_t it = NULL;
294	const ucl_object_t *cur, *elt;
295	const struct ucl_emitter_functions *func = ctx->func;
296	bool first = true;
297
298	ucl_emitter_print_key (print_key, ctx, obj, compact);
299	/*
300	 * Print <ident_level>{
301	 * <ident_level + 1><object content>
302	 */
303	if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
304		if (compact) {
305			func->ucl_emitter_append_character ('{', 1, func->ud);
306		}
307		else {
308			func->ucl_emitter_append_len ("{\n", 2, func->ud);
309		}
310		ctx->indent ++;
311	}
312
313	while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
314
315		if (ctx->id == UCL_EMIT_CONFIG) {
316			LL_FOREACH (cur, elt) {
317				ucl_emitter_common_elt (ctx, elt, first, true, compact);
318			}
319		}
320		else {
321			/* Expand implicit arrays */
322			if (cur->next != NULL) {
323				if (!first) {
324					if (compact) {
325						func->ucl_emitter_append_character (',', 1, func->ud);
326					}
327					else {
328						func->ucl_emitter_append_len (",\n", 2, func->ud);
329					}
330				}
331				ucl_add_tabs (func, ctx->indent, compact);
332				ucl_emitter_common_start_array (ctx, cur, true, compact);
333				ucl_emitter_common_end_array (ctx, cur, compact);
334			}
335			else {
336				ucl_emitter_common_elt (ctx, cur, first, true, compact);
337			}
338		}
339
340		first = false;
341	}
342}
343
344/**
345 * Common choice of object emitting
346 * @param ctx emitter context
347 * @param obj object to print
348 * @param first flag to mark the first element
349 * @param print_key print key of an object
350 * @param compact compact output
351 */
352static void
353ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
354		const ucl_object_t *obj, bool first, bool print_key, bool compact)
355{
356	const struct ucl_emitter_functions *func = ctx->func;
357	bool flag;
358	struct ucl_object_userdata *ud;
359	const char *ud_out = "";
360
361	if (ctx->id != UCL_EMIT_CONFIG && !first) {
362		if (compact) {
363			func->ucl_emitter_append_character (',', 1, func->ud);
364		}
365		else {
366			if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
367				func->ucl_emitter_append_len ("\n", 1, func->ud);
368			} else {
369				func->ucl_emitter_append_len (",\n", 2, func->ud);
370			}
371		}
372	}
373
374	ucl_add_tabs (func, ctx->indent, compact);
375
376	switch (obj->type) {
377	case UCL_INT:
378		ucl_emitter_print_key (print_key, ctx, obj, compact);
379		func->ucl_emitter_append_int (ucl_object_toint (obj), func->ud);
380		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
381		break;
382	case UCL_FLOAT:
383	case UCL_TIME:
384		ucl_emitter_print_key (print_key, ctx, obj, compact);
385		func->ucl_emitter_append_double (ucl_object_todouble (obj), func->ud);
386		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
387		break;
388	case UCL_BOOLEAN:
389		ucl_emitter_print_key (print_key, ctx, obj, compact);
390		flag = ucl_object_toboolean (obj);
391		if (flag) {
392			func->ucl_emitter_append_len ("true", 4, func->ud);
393		}
394		else {
395			func->ucl_emitter_append_len ("false", 5, func->ud);
396		}
397		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
398		break;
399	case UCL_STRING:
400		ucl_emitter_print_key (print_key, ctx, obj, compact);
401		if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) {
402			ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
403		}
404		else {
405			ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
406		}
407		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
408		break;
409	case UCL_NULL:
410		ucl_emitter_print_key (print_key, ctx, obj, compact);
411		func->ucl_emitter_append_len ("null", 4, func->ud);
412		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
413		break;
414	case UCL_OBJECT:
415		ucl_emitter_common_start_object (ctx, obj, print_key, compact);
416		ucl_emitter_common_end_object (ctx, obj, compact);
417		break;
418	case UCL_ARRAY:
419		ucl_emitter_common_start_array (ctx, obj, print_key, compact);
420		ucl_emitter_common_end_array (ctx, obj, compact);
421		break;
422	case UCL_USERDATA:
423		ud = (struct ucl_object_userdata *)obj;
424		ucl_emitter_print_key (print_key, ctx, obj, compact);
425		if (ud->emitter) {
426			ud_out = ud->emitter (obj->value.ud);
427			if (ud_out == NULL) {
428				ud_out = "null";
429			}
430		}
431		ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx);
432		ucl_emitter_finish_object (ctx, obj, compact, !print_key);
433		break;
434	}
435}
436
437/*
438 * Specific standard implementations of the emitter functions
439 */
440#define UCL_EMIT_TYPE_IMPL(type, compact)		\
441	static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx,	\
442		const ucl_object_t *obj, bool first, bool print_key) {	\
443		ucl_emitter_common_elt (ctx, obj, first, print_key, (compact));	\
444	}	\
445	static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx,	\
446		const ucl_object_t *obj, bool print_key) {	\
447		ucl_emitter_common_start_object (ctx, obj, print_key, (compact));	\
448	}	\
449	static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx,	\
450		const ucl_object_t *obj, bool print_key) {	\
451		ucl_emitter_common_start_array (ctx, obj, print_key, (compact));	\
452	}	\
453	static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx,	\
454		const ucl_object_t *obj) {	\
455		ucl_emitter_common_end_object (ctx, obj, (compact));	\
456	}	\
457	static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx,	\
458		const ucl_object_t *obj) {	\
459		ucl_emitter_common_end_array (ctx, obj, (compact));	\
460	}
461
462UCL_EMIT_TYPE_IMPL(json, false)
463UCL_EMIT_TYPE_IMPL(json_compact, true)
464UCL_EMIT_TYPE_IMPL(config, false)
465UCL_EMIT_TYPE_IMPL(yaml, false)
466
467unsigned char *
468ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
469{
470	unsigned char *res = NULL;
471	struct ucl_emitter_functions *func;
472	if (obj == NULL) {
473		return NULL;
474	}
475
476	func = ucl_object_emit_memory_funcs ((void **)&res);
477
478	if (func != NULL) {
479		ucl_object_emit_full (obj, emit_type, func);
480		ucl_object_emit_funcs_free (func);
481	}
482
483	return res;
484}
485
486bool
487ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
488		struct ucl_emitter_functions *emitter)
489{
490	const struct ucl_emitter_context *ctx;
491	struct ucl_emitter_context my_ctx;
492	bool res = false;
493
494	ctx = ucl_emit_get_standard_context (emit_type);
495	if (ctx != NULL) {
496		memcpy (&my_ctx, ctx, sizeof (my_ctx));
497		my_ctx.func = emitter;
498		my_ctx.indent = 0;
499		my_ctx.top = obj;
500
501		my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);
502		res = true;
503	}
504
505	return res;
506}
507