xref: /freebsd/contrib/libucl/src/ucl_emitter_utils.c (revision 545ddfbe7d4fe8adfb862903b24eac1d5896c1ef)
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 				return -1;
293 			}
294 			free (buf);
295 		}
296 	}
297 
298 	return 0;
299 }
300 
301 static int
302 ucl_fd_append_len (const unsigned char *str, size_t len, void *ud)
303 {
304 	int fd = *(int *)ud;
305 
306 	return write (fd, str, len);
307 }
308 
309 static int
310 ucl_fd_append_int (int64_t val, void *ud)
311 {
312 	int fd = *(int *)ud;
313 	char intbuf[64];
314 
315 	snprintf (intbuf, sizeof (intbuf), "%jd", (intmax_t)val);
316 	return write (fd, intbuf, strlen (intbuf));
317 }
318 
319 static int
320 ucl_fd_append_double (double val, void *ud)
321 {
322 	int fd = *(int *)ud;
323 	const double delta = 0.0000001;
324 	char nbuf[64];
325 
326 	if (val == (double)(int)val) {
327 		snprintf (nbuf, sizeof (nbuf), "%.1lf", val);
328 	}
329 	else if (fabs (val - (double)(int)val) < delta) {
330 		/* Write at maximum precision */
331 		snprintf (nbuf, sizeof (nbuf), "%.*lg", DBL_DIG, val);
332 	}
333 	else {
334 		snprintf (nbuf, sizeof (nbuf), "%lf", val);
335 	}
336 
337 	return write (fd, nbuf, strlen (nbuf));
338 }
339 
340 struct ucl_emitter_functions*
341 ucl_object_emit_memory_funcs (void **pmem)
342 {
343 	struct ucl_emitter_functions *f;
344 	UT_string *s;
345 
346 	f = calloc (1, sizeof (*f));
347 
348 	if (f != NULL) {
349 		f->ucl_emitter_append_character = ucl_utstring_append_character;
350 		f->ucl_emitter_append_double = ucl_utstring_append_double;
351 		f->ucl_emitter_append_int = ucl_utstring_append_int;
352 		f->ucl_emitter_append_len = ucl_utstring_append_len;
353 		f->ucl_emitter_free_func = free;
354 		utstring_new (s);
355 		f->ud = s;
356 		*pmem = s->d;
357 		s->pd = pmem;
358 	}
359 
360 	return f;
361 }
362 
363 struct ucl_emitter_functions*
364 ucl_object_emit_file_funcs (FILE *fp)
365 {
366 	struct ucl_emitter_functions *f;
367 
368 	f = calloc (1, sizeof (*f));
369 
370 	if (f != NULL) {
371 		f->ucl_emitter_append_character = ucl_file_append_character;
372 		f->ucl_emitter_append_double = ucl_file_append_double;
373 		f->ucl_emitter_append_int = ucl_file_append_int;
374 		f->ucl_emitter_append_len = ucl_file_append_len;
375 		f->ucl_emitter_free_func = NULL;
376 		f->ud = fp;
377 	}
378 
379 	return f;
380 }
381 
382 struct ucl_emitter_functions*
383 ucl_object_emit_fd_funcs (int fd)
384 {
385 	struct ucl_emitter_functions *f;
386 	int *ip;
387 
388 	f = calloc (1, sizeof (*f));
389 
390 	if (f != NULL) {
391 		ip = malloc (sizeof (fd));
392 		if (ip == NULL) {
393 			free (f);
394 			return NULL;
395 		}
396 
397 		memcpy (ip, &fd, sizeof (fd));
398 		f->ucl_emitter_append_character = ucl_fd_append_character;
399 		f->ucl_emitter_append_double = ucl_fd_append_double;
400 		f->ucl_emitter_append_int = ucl_fd_append_int;
401 		f->ucl_emitter_append_len = ucl_fd_append_len;
402 		f->ucl_emitter_free_func = free;
403 		f->ud = ip;
404 	}
405 
406 	return f;
407 }
408 
409 void
410 ucl_object_emit_funcs_free (struct ucl_emitter_functions *f)
411 {
412 	if (f != NULL) {
413 		if (f->ucl_emitter_free_func != NULL) {
414 			f->ucl_emitter_free_func (f->ud);
415 		}
416 		free (f);
417 	}
418 }
419 
420 
421 unsigned char *
422 ucl_object_emit_single_json (const ucl_object_t *obj)
423 {
424 	UT_string *buf = NULL;
425 	unsigned char *res = NULL;
426 
427 	if (obj == NULL) {
428 		return NULL;
429 	}
430 
431 	utstring_new (buf);
432 
433 	if (buf != NULL) {
434 		switch (obj->type) {
435 		case UCL_OBJECT:
436 			ucl_utstring_append_len ("object", 6, buf);
437 			break;
438 		case UCL_ARRAY:
439 			ucl_utstring_append_len ("array", 5, buf);
440 			break;
441 		case UCL_INT:
442 			ucl_utstring_append_int (obj->value.iv, buf);
443 			break;
444 		case UCL_FLOAT:
445 		case UCL_TIME:
446 			ucl_utstring_append_double (obj->value.dv, buf);
447 			break;
448 		case UCL_NULL:
449 			ucl_utstring_append_len ("null", 4, buf);
450 			break;
451 		case UCL_BOOLEAN:
452 			if (obj->value.iv) {
453 				ucl_utstring_append_len ("true", 4, buf);
454 			}
455 			else {
456 				ucl_utstring_append_len ("false", 5, buf);
457 			}
458 			break;
459 		case UCL_STRING:
460 			ucl_utstring_append_len (obj->value.sv, obj->len, buf);
461 			break;
462 		case UCL_USERDATA:
463 			ucl_utstring_append_len ("userdata", 8, buf);
464 			break;
465 		}
466 		res = utstring_body (buf);
467 		free (buf);
468 	}
469 
470 	return res;
471 }
472 
473 #define LONG_STRING_LIMIT 80
474 
475 bool
476 ucl_maybe_long_string (const ucl_object_t *obj)
477 {
478 	if (obj->len > LONG_STRING_LIMIT || (obj->flags & UCL_OBJECT_MULTILINE)) {
479 		/* String is long enough, so search for newline characters in it */
480 		if (memchr (obj->value.sv, '\n', obj->len) != NULL) {
481 			return true;
482 		}
483 	}
484 
485 	return false;
486 }
487