xref: /freebsd/contrib/libucl/src/ucl_emitter_utils.c (revision 5608fd23c27fa1e8ee595d7b678cbfd35d657fbe)
1 /* Copyright (c) 2014, 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 
32 #ifdef HAVE_FLOAT_H
33 #include <float.h>
34 #endif
35 #ifdef HAVE_MATH_H
36 #include <math.h>
37 #endif
38 
39 extern const struct ucl_emitter_operations ucl_standartd_emitter_ops[];
40 
41 static const struct ucl_emitter_context ucl_standard_emitters[] = {
42 	[UCL_EMIT_JSON] = {
43 		.name = "json",
44 		.id = UCL_EMIT_JSON,
45 		.func = NULL,
46 		.ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON]
47 	},
48 	[UCL_EMIT_JSON_COMPACT] = {
49 		.name = "json_compact",
50 		.id = UCL_EMIT_JSON_COMPACT,
51 		.func = NULL,
52 		.ops = &ucl_standartd_emitter_ops[UCL_EMIT_JSON_COMPACT]
53 	},
54 	[UCL_EMIT_CONFIG] = {
55 		.name = "config",
56 		.id = UCL_EMIT_CONFIG,
57 		.func = NULL,
58 		.ops = &ucl_standartd_emitter_ops[UCL_EMIT_CONFIG]
59 	},
60 	[UCL_EMIT_YAML] = {
61 		.name = "yaml",
62 		.id = UCL_EMIT_YAML,
63 		.func = NULL,
64 		.ops = &ucl_standartd_emitter_ops[UCL_EMIT_YAML]
65 	}
66 };
67 
68 /**
69  * Get standard emitter context for a specified emit_type
70  * @param emit_type type of emitter
71  * @return context or NULL if input is invalid
72  */
73 const struct ucl_emitter_context *
74 ucl_emit_get_standard_context (enum ucl_emitter emit_type)
75 {
76 	if (emit_type >= UCL_EMIT_JSON && emit_type <= UCL_EMIT_YAML) {
77 		return &ucl_standard_emitters[emit_type];
78 	}
79 
80 	return NULL;
81 }
82 
83 /**
84  * Serialise string
85  * @param str string to emit
86  * @param buf target buffer
87  */
88 void
89 ucl_elt_string_write_json (const char *str, size_t size,
90 		struct ucl_emitter_context *ctx)
91 {
92 	const char *p = str, *c = str;
93 	size_t len = 0;
94 	const struct ucl_emitter_functions *func = ctx->func;
95 
96 	if (ctx->id != UCL_EMIT_YAML) {
97 		func->ucl_emitter_append_character ('"', 1, func->ud);
98 	}
99 
100 	while (size) {
101 		if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
102 			if (len > 0) {
103 				func->ucl_emitter_append_len (c, len, func->ud);
104 			}
105 			switch (*p) {
106 			case '\n':
107 				func->ucl_emitter_append_len ("\\n", 2, func->ud);
108 				break;
109 			case '\r':
110 				func->ucl_emitter_append_len ("\\r", 2, func->ud);
111 				break;
112 			case '\b':
113 				func->ucl_emitter_append_len ("\\b", 2, func->ud);
114 				break;
115 			case '\t':
116 				func->ucl_emitter_append_len ("\\t", 2, func->ud);
117 				break;
118 			case '\f':
119 				func->ucl_emitter_append_len ("\\f", 2, func->ud);
120 				break;
121 			case '\\':
122 				func->ucl_emitter_append_len ("\\\\", 2, func->ud);
123 				break;
124 			case '"':
125 				func->ucl_emitter_append_len ("\\\"", 2, func->ud);
126 				break;
127 			}
128 			len = 0;
129 			c = ++p;
130 		}
131 		else {
132 			p ++;
133 			len ++;
134 		}
135 		size --;
136 	}
137 	if (len > 0) {
138 		func->ucl_emitter_append_len (c, len, func->ud);
139 	}
140 	if (ctx->id != UCL_EMIT_YAML) {
141 		func->ucl_emitter_append_character ('"', 1, func->ud);
142 	}
143 }
144 
145 /*
146  * Generic utstring output
147  */
148 static int
149 ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
150 {
151 	UT_string *buf = ud;
152 
153 	if (len == 1) {
154 		utstring_append_c (buf, c);
155 	}
156 	else {
157 		utstring_reserve (buf, len);
158 		memset (&buf->d[buf->i], c, len);
159 		buf->i += len;
160 		buf->d[buf->i] = '\0';
161 	}
162 
163 	return 0;
164 }
165 
166 static int
167 ucl_utstring_append_len (const unsigned char *str, size_t len, void *ud)
168 {
169 	UT_string *buf = ud;
170 
171 	utstring_append_len (buf, str, len);
172 
173 	return 0;
174 }
175 
176 static int
177 ucl_utstring_append_int (int64_t val, void *ud)
178 {
179 	UT_string *buf = ud;
180 
181 	utstring_printf (buf, "%jd", (intmax_t)val);
182 	return 0;
183 }
184 
185 static int
186 ucl_utstring_append_double (double val, void *ud)
187 {
188 	UT_string *buf = ud;
189 	const double delta = 0.0000001;
190 
191 	if (val == (double)(int)val) {
192 		utstring_printf (buf, "%.1lf", val);
193 	}
194 	else if (fabs (val - (double)(int)val) < delta) {
195 		/* Write at maximum precision */
196 		utstring_printf (buf, "%.*lg", DBL_DIG, val);
197 	}
198 	else {
199 		utstring_printf (buf, "%lf", val);
200 	}
201 
202 	return 0;
203 }
204 
205 /*
206  * Generic file output
207  */
208 static int
209 ucl_file_append_character (unsigned char c, size_t len, void *ud)
210 {
211 	FILE *fp = ud;
212 
213 	while (len --) {
214 		fputc (c, fp);
215 	}
216 
217 	return 0;
218 }
219 
220 static int
221 ucl_file_append_len (const unsigned char *str, size_t len, void *ud)
222 {
223 	FILE *fp = ud;
224 
225 	fwrite (str, len, 1, fp);
226 
227 	return 0;
228 }
229 
230 static int
231 ucl_file_append_int (int64_t val, void *ud)
232 {
233 	FILE *fp = ud;
234 
235 	fprintf (fp, "%jd", (intmax_t)val);
236 
237 	return 0;
238 }
239 
240 static int
241 ucl_file_append_double (double val, void *ud)
242 {
243 	FILE *fp = ud;
244 	const double delta = 0.0000001;
245 
246 	if (val == (double)(int)val) {
247 		fprintf (fp, "%.1lf", val);
248 	}
249 	else if (fabs (val - (double)(int)val) < delta) {
250 		/* Write at maximum precision */
251 		fprintf (fp, "%.*lg", DBL_DIG, val);
252 	}
253 	else {
254 		fprintf (fp, "%lf", val);
255 	}
256 
257 	return 0;
258 }
259 
260 /*
261  * Generic file descriptor writing functions
262  */
263 static int
264 ucl_fd_append_character (unsigned char c, size_t len, void *ud)
265 {
266 	int fd = *(int *)ud;
267 	unsigned char *buf;
268 
269 	if (len == 1) {
270 		write (fd, &c, 1);
271 	}
272 	else {
273 		buf = malloc (len);
274 		if (buf == NULL) {
275 			/* Fallback */
276 			while (len --) {
277 				write (fd, &c, 1);
278 			}
279 		}
280 		else {
281 			memset (buf, c, len);
282 			write (fd, buf, len);
283 			free (buf);
284 		}
285 	}
286 
287 	return 0;
288 }
289 
290 static int
291 ucl_fd_append_len (const unsigned char *str, size_t len, void *ud)
292 {
293 	int fd = *(int *)ud;
294 
295 	write (fd, str, len);
296 
297 	return 0;
298 }
299 
300 static int
301 ucl_fd_append_int (int64_t val, void *ud)
302 {
303 	int fd = *(int *)ud;
304 	char intbuf[64];
305 
306 	snprintf (intbuf, sizeof (intbuf), "%jd", (intmax_t)val);
307 	write (fd, intbuf, strlen (intbuf));
308 
309 	return 0;
310 }
311 
312 static int
313 ucl_fd_append_double (double val, void *ud)
314 {
315 	int fd = *(int *)ud;
316 	const double delta = 0.0000001;
317 	char nbuf[64];
318 
319 	if (val == (double)(int)val) {
320 		snprintf (nbuf, sizeof (nbuf), "%.1lf", val);
321 	}
322 	else if (fabs (val - (double)(int)val) < delta) {
323 		/* Write at maximum precision */
324 		snprintf (nbuf, sizeof (nbuf), "%.*lg", DBL_DIG, val);
325 	}
326 	else {
327 		snprintf (nbuf, sizeof (nbuf), "%lf", val);
328 	}
329 
330 	write (fd, nbuf, strlen (nbuf));
331 
332 	return 0;
333 }
334 
335 struct ucl_emitter_functions*
336 ucl_object_emit_memory_funcs (void **pmem)
337 {
338 	struct ucl_emitter_functions *f;
339 	UT_string *s;
340 
341 	f = calloc (1, sizeof (*f));
342 
343 	if (f != NULL) {
344 		f->ucl_emitter_append_character = ucl_utstring_append_character;
345 		f->ucl_emitter_append_double = ucl_utstring_append_double;
346 		f->ucl_emitter_append_int = ucl_utstring_append_int;
347 		f->ucl_emitter_append_len = ucl_utstring_append_len;
348 		f->ucl_emitter_free_func = free;
349 		utstring_new (s);
350 		f->ud = s;
351 		*pmem = s->d;
352 		s->pd = pmem;
353 	}
354 
355 	return f;
356 }
357 
358 struct ucl_emitter_functions*
359 ucl_object_emit_file_funcs (FILE *fp)
360 {
361 	struct ucl_emitter_functions *f;
362 
363 	f = calloc (1, sizeof (*f));
364 
365 	if (f != NULL) {
366 		f->ucl_emitter_append_character = ucl_file_append_character;
367 		f->ucl_emitter_append_double = ucl_file_append_double;
368 		f->ucl_emitter_append_int = ucl_file_append_int;
369 		f->ucl_emitter_append_len = ucl_file_append_len;
370 		f->ucl_emitter_free_func = NULL;
371 		f->ud = fp;
372 	}
373 
374 	return f;
375 }
376 
377 struct ucl_emitter_functions*
378 ucl_object_emit_fd_funcs (int fd)
379 {
380 	struct ucl_emitter_functions *f;
381 	int *ip;
382 
383 	f = calloc (1, sizeof (*f));
384 
385 	if (f != NULL) {
386 		ip = malloc (sizeof (fd));
387 		if (ip == NULL) {
388 			free (f);
389 			return NULL;
390 		}
391 
392 		memcpy (ip, &fd, sizeof (fd));
393 		f->ucl_emitter_append_character = ucl_fd_append_character;
394 		f->ucl_emitter_append_double = ucl_fd_append_double;
395 		f->ucl_emitter_append_int = ucl_fd_append_int;
396 		f->ucl_emitter_append_len = ucl_fd_append_len;
397 		f->ucl_emitter_free_func = free;
398 		f->ud = ip;
399 	}
400 
401 	return f;
402 }
403 
404 void
405 ucl_object_emit_funcs_free (struct ucl_emitter_functions *f)
406 {
407 	if (f != NULL) {
408 		if (f->ucl_emitter_free_func != NULL) {
409 			f->ucl_emitter_free_func (f->ud);
410 		}
411 		free (f);
412 	}
413 }
414 
415 
416 unsigned char *
417 ucl_object_emit_single_json (const ucl_object_t *obj)
418 {
419 	UT_string *buf = NULL;
420 	unsigned char *res = NULL;
421 
422 	if (obj == NULL) {
423 		return NULL;
424 	}
425 
426 	utstring_new (buf);
427 
428 	if (buf != NULL) {
429 		switch (obj->type) {
430 		case UCL_OBJECT:
431 			ucl_utstring_append_len ("object", 6, buf);
432 			break;
433 		case UCL_ARRAY:
434 			ucl_utstring_append_len ("array", 5, buf);
435 			break;
436 		case UCL_INT:
437 			ucl_utstring_append_int (obj->value.iv, buf);
438 			break;
439 		case UCL_FLOAT:
440 		case UCL_TIME:
441 			ucl_utstring_append_double (obj->value.dv, buf);
442 			break;
443 		case UCL_NULL:
444 			ucl_utstring_append_len ("null", 4, buf);
445 			break;
446 		case UCL_BOOLEAN:
447 			if (obj->value.iv) {
448 				ucl_utstring_append_len ("true", 4, buf);
449 			}
450 			else {
451 				ucl_utstring_append_len ("false", 5, buf);
452 			}
453 			break;
454 		case UCL_STRING:
455 			ucl_utstring_append_len (obj->value.sv, obj->len, buf);
456 			break;
457 		case UCL_USERDATA:
458 			ucl_utstring_append_len ("userdata", 8, buf);
459 			break;
460 		}
461 		res = utstring_body (buf);
462 		free (buf);
463 	}
464 
465 	return res;
466 }
467