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