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