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