xref: /freebsd/contrib/libucl/lua/lua_ucl.c (revision d9f0ce31900a48d1a2bfc1c8c86f79d1e831451a)
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 /**
25  * @file lua ucl bindings
26  */
27 
28 #include "ucl.h"
29 #include "ucl_internal.h"
30 #include "lua_ucl.h"
31 #include <strings.h>
32 #include <zconf.h>
33 
34 /***
35  * @module ucl
36  * This lua module allows to parse objects from strings and to store data into
37  * ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
38  * @example
39 local ucl = require("ucl")
40 
41 local parser = ucl.parser()
42 local res,err = parser:parse_string('{key=value}')
43 
44 if not res then
45 	print('parser error: ' .. err)
46 else
47 	local obj = parser:get_object()
48 	local got = ucl.to_format(obj, 'json')
49 endif
50 
51 local table = {
52   str = 'value',
53   num = 100500,
54   null = ucl.null,
55   func = function ()
56     return 'huh'
57   end
58 }
59 
60 print(ucl.to_format(table, 'ucl'))
61 -- Output:
62 --[[
63 num = 100500;
64 str = "value";
65 null = null;
66 func = "huh";
67 --]]
68  */
69 
70 #define PARSER_META "ucl.parser.meta"
71 #define EMITTER_META "ucl.emitter.meta"
72 #define NULL_META "null.emitter.meta"
73 #define OBJECT_META "ucl.object.meta"
74 
75 static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj);
76 static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array);
77 static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx);
78 static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx);
79 
80 static void *ucl_null;
81 
82 /**
83  * Push a single element of an object to lua
84  * @param L
85  * @param key
86  * @param obj
87  */
88 static void
89 ucl_object_lua_push_element (lua_State *L, const char *key,
90 		const ucl_object_t *obj)
91 {
92 	lua_pushstring (L, key);
93 	ucl_object_push_lua (L, obj, true);
94 	lua_settable (L, -3);
95 }
96 
97 static void
98 lua_ucl_userdata_dtor (void *ud)
99 {
100 	struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
101 
102 	luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx);
103 	if (fd->ret != NULL) {
104 		free (fd->ret);
105 	}
106 	free (fd);
107 }
108 
109 static const char *
110 lua_ucl_userdata_emitter (void *ud)
111 {
112 	struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
113 	const char *out = "";
114 
115 	lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx);
116 
117 	lua_pcall (fd->L, 0, 1, 0);
118 	out = lua_tostring (fd->L, -1);
119 
120 	if (out != NULL) {
121 		/* We need to store temporary string in a more appropriate place */
122 		if (fd->ret) {
123 			free (fd->ret);
124 		}
125 		fd->ret = strdup (out);
126 	}
127 
128 	lua_settop (fd->L, 0);
129 
130 	return fd->ret;
131 }
132 
133 /**
134  * Push a single object to lua
135  * @param L
136  * @param obj
137  * @return
138  */
139 static int
140 ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
141 		bool allow_array)
142 {
143 	const ucl_object_t *cur;
144 	ucl_object_iter_t it = NULL;
145 	int nelt = 0;
146 
147 	if (allow_array && obj->next != NULL) {
148 		/* Actually we need to push this as an array */
149 		return ucl_object_lua_push_array (L, obj);
150 	}
151 
152 	/* Optimize allocation by preallocation of table */
153 	while (ucl_object_iterate (obj, &it, true) != NULL) {
154 		nelt ++;
155 	}
156 
157 	lua_createtable (L, 0, nelt);
158 	it = NULL;
159 
160 	while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
161 		ucl_object_lua_push_element (L, ucl_object_key (cur), cur);
162 	}
163 
164 	return 1;
165 }
166 
167 /**
168  * Push an array to lua as table indexed by integers
169  * @param L
170  * @param obj
171  * @return
172  */
173 static int
174 ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
175 {
176 	const ucl_object_t *cur;
177 	ucl_object_iter_t it;
178 	int i = 1, nelt = 0;
179 
180 	if (obj->type == UCL_ARRAY) {
181 		nelt = obj->len;
182 		it = ucl_object_iterate_new (obj);
183 		lua_createtable (L, nelt, 0);
184 
185 		while ((cur = ucl_object_iterate_safe (it, true))) {
186 			ucl_object_push_lua (L, cur, false);
187 			lua_rawseti (L, -2, i);
188 			i ++;
189 		}
190 	}
191 	else {
192 		/* Optimize allocation by preallocation of table */
193 		LL_FOREACH (obj, cur) {
194 			nelt ++;
195 		}
196 
197 		lua_createtable (L, nelt, 0);
198 
199 		LL_FOREACH (obj, cur) {
200 			ucl_object_push_lua (L, cur, false);
201 			lua_rawseti (L, -2, i);
202 			i ++;
203 		}
204 	}
205 
206 	return 1;
207 }
208 
209 /**
210  * Push a simple object to lua depending on its actual type
211  */
212 static int
213 ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
214 		bool allow_array)
215 {
216 	struct ucl_lua_funcdata *fd;
217 
218 	if (allow_array && obj->next != NULL) {
219 		/* Actually we need to push this as an array */
220 		return ucl_object_lua_push_array (L, obj);
221 	}
222 
223 	switch (obj->type) {
224 	case UCL_BOOLEAN:
225 		lua_pushboolean (L, ucl_obj_toboolean (obj));
226 		break;
227 	case UCL_STRING:
228 		lua_pushstring (L, ucl_obj_tostring (obj));
229 		break;
230 	case UCL_INT:
231 #if LUA_VERSION_NUM >= 501
232 		lua_pushinteger (L, ucl_obj_toint (obj));
233 #else
234 		lua_pushnumber (L, ucl_obj_toint (obj));
235 #endif
236 		break;
237 	case UCL_FLOAT:
238 	case UCL_TIME:
239 		lua_pushnumber (L, ucl_obj_todouble (obj));
240 		break;
241 	case UCL_NULL:
242 		lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
243 		break;
244 	case UCL_USERDATA:
245 		fd = (struct ucl_lua_funcdata *)obj->value.ud;
246 		lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx);
247 		break;
248 	default:
249 		lua_pushnil (L);
250 		break;
251 	}
252 
253 	return 1;
254 }
255 
256 /***
257  * @function ucl_object_push_lua(L, obj, allow_array)
258  * This is a `C` function to push `UCL` object as lua variable. This function
259  * converts `obj` to lua representation using the following conversions:
260  *
261  * - *scalar* values are directly presented by lua objects
262  * - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
263  * this can be used to pass functions from lua to c and vice-versa
264  * - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations
265  * - *objects* are converted to lua tables with string indicies
266  * @param {lua_State} L lua state pointer
267  * @param {ucl_object_t} obj object to push
268  * @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays)
269  * @return {int} `1` if an object is pushed to lua
270  */
271 int
272 ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
273 {
274 	switch (obj->type) {
275 	case UCL_OBJECT:
276 		return ucl_object_lua_push_object (L, obj, allow_array);
277 	case UCL_ARRAY:
278 		return ucl_object_lua_push_array (L, obj);
279 	default:
280 		return ucl_object_lua_push_scalar (L, obj, allow_array);
281 	}
282 }
283 
284 /**
285  * Parse lua table into object top
286  * @param L
287  * @param top
288  * @param idx
289  */
290 static ucl_object_t *
291 ucl_object_lua_fromtable (lua_State *L, int idx)
292 {
293 	ucl_object_t *obj, *top = NULL;
294 	size_t keylen;
295 	const char *k;
296 	bool is_array = true;
297 	int max = INT_MIN;
298 
299 	if (idx < 0) {
300 		/* For negative indicies we want to invert them */
301 		idx = lua_gettop (L) + idx + 1;
302 	}
303 	/* Check for array */
304 	lua_pushnil (L);
305 	while (lua_next (L, idx) != 0) {
306 		if (lua_type (L, -2) == LUA_TNUMBER) {
307 			double num = lua_tonumber (L, -2);
308 			if (num == (int)num) {
309 				if (num > max) {
310 					max = num;
311 				}
312 			}
313 			else {
314 				/* Keys are not integer */
315 				lua_pop (L, 2);
316 				is_array = false;
317 				break;
318 			}
319 		}
320 		else {
321 			/* Keys are not numeric */
322 			lua_pop (L, 2);
323 			is_array = false;
324 			break;
325 		}
326 		lua_pop (L, 1);
327 	}
328 
329 	/* Table iterate */
330 	if (is_array) {
331 		int i;
332 
333 		top = ucl_object_typed_new (UCL_ARRAY);
334 		for (i = 1; i <= max; i ++) {
335 			lua_pushinteger (L, i);
336 			lua_gettable (L, idx);
337 			obj = ucl_object_lua_fromelt (L, lua_gettop (L));
338 			if (obj != NULL) {
339 				ucl_array_append (top, obj);
340 			}
341 			lua_pop (L, 1);
342 		}
343 	}
344 	else {
345 		lua_pushnil (L);
346 		top = ucl_object_typed_new (UCL_OBJECT);
347 		while (lua_next (L, idx) != 0) {
348 			/* copy key to avoid modifications */
349 			k = lua_tolstring (L, -2, &keylen);
350 			obj = ucl_object_lua_fromelt (L, lua_gettop (L));
351 
352 			if (obj != NULL) {
353 				ucl_object_insert_key (top, obj, k, keylen, true);
354 			}
355 			lua_pop (L, 1);
356 		}
357 	}
358 
359 	return top;
360 }
361 
362 /**
363  * Get a single element from lua to object obj
364  * @param L
365  * @param obj
366  * @param idx
367  */
368 static ucl_object_t *
369 ucl_object_lua_fromelt (lua_State *L, int idx)
370 {
371 	int type;
372 	double num;
373 	ucl_object_t *obj = NULL;
374 	struct ucl_lua_funcdata *fd;
375 
376 	type = lua_type (L, idx);
377 
378 	switch (type) {
379 	case LUA_TSTRING:
380 		obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0);
381 		break;
382 	case LUA_TNUMBER:
383 		num = lua_tonumber (L, idx);
384 		if (num == (int64_t)num) {
385 			obj = ucl_object_fromint (num);
386 		}
387 		else {
388 			obj = ucl_object_fromdouble (num);
389 		}
390 		break;
391 	case LUA_TBOOLEAN:
392 		obj = ucl_object_frombool (lua_toboolean (L, idx));
393 		break;
394 	case LUA_TUSERDATA:
395 		if (lua_topointer (L, idx) == ucl_null) {
396 			obj = ucl_object_typed_new (UCL_NULL);
397 		}
398 		break;
399 	case LUA_TTABLE:
400 	case LUA_TFUNCTION:
401 	case LUA_TTHREAD:
402 		if (luaL_getmetafield (L, idx, "__gen_ucl")) {
403 			if (lua_isfunction (L, -1)) {
404 				lua_settop (L, 3); /* gen, obj, func */
405 				lua_insert (L, 1); /* func, gen, obj */
406 				lua_insert (L, 2); /* func, obj, gen */
407 				lua_call(L, 2, 1);
408 				obj = ucl_object_lua_fromelt (L, 1);
409 			}
410 			lua_pop (L, 2);
411 		}
412 		else {
413 			if (type == LUA_TTABLE) {
414 				obj = ucl_object_lua_fromtable (L, idx);
415 			}
416 			else if (type == LUA_TFUNCTION) {
417 				fd = malloc (sizeof (*fd));
418 				if (fd != NULL) {
419 					lua_pushvalue (L, idx);
420 					fd->L = L;
421 					fd->ret = NULL;
422 					fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
423 
424 					obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
425 							lua_ucl_userdata_emitter, (void *)fd);
426 				}
427 			}
428 		}
429 		break;
430 	}
431 
432 	return obj;
433 }
434 
435 /**
436  * @function ucl_object_lua_import(L, idx)
437  * Extracts ucl object from lua variable at `idx` position,
438  * @see ucl_object_push_lua for conversion definitions
439  * @param {lua_state} L lua state machine pointer
440  * @param {int} idx index where the source variable is placed
441  * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
442  * this object thus needs to be unref'ed after usage.
443  */
444 ucl_object_t *
445 ucl_object_lua_import (lua_State *L, int idx)
446 {
447 	ucl_object_t *obj;
448 	int t;
449 
450 	t = lua_type (L, idx);
451 	switch (t) {
452 	case LUA_TTABLE:
453 		obj = ucl_object_lua_fromtable (L, idx);
454 		break;
455 	default:
456 		obj = ucl_object_lua_fromelt (L, idx);
457 		break;
458 	}
459 
460 	return obj;
461 }
462 
463 static int
464 lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
465 {
466 	unsigned char *result;
467 
468 	result = ucl_object_emit (obj, type);
469 
470 	if (result != NULL) {
471 		lua_pushstring (L, (const char *)result);
472 		free (result);
473 	}
474 	else {
475 		lua_pushnil (L);
476 	}
477 
478 	return 1;
479 }
480 
481 static int
482 lua_ucl_parser_init (lua_State *L)
483 {
484 	struct ucl_parser *parser, **pparser;
485 	int flags = 0;
486 
487 	if (lua_gettop (L) >= 1) {
488 		flags = lua_tonumber (L, 1);
489 	}
490 
491 	parser = ucl_parser_new (flags);
492 	if (parser == NULL) {
493 		lua_pushnil (L);
494 	}
495 
496 	pparser = lua_newuserdata (L, sizeof (parser));
497 	*pparser = parser;
498 	luaL_getmetatable (L, PARSER_META);
499 	lua_setmetatable (L, -2);
500 
501 	return 1;
502 }
503 
504 static struct ucl_parser *
505 lua_ucl_parser_get (lua_State *L, int index)
506 {
507 	return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
508 }
509 
510 static ucl_object_t *
511 lua_ucl_object_get (lua_State *L, int index)
512 {
513 	return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META));
514 }
515 
516 static void
517 lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj)
518 {
519 	ucl_object_t **pobj;
520 
521 	pobj = lua_newuserdata (L, sizeof (*pobj));
522 	*pobj = obj;
523 	luaL_getmetatable (L, OBJECT_META);
524 	lua_setmetatable (L, -2);
525 }
526 
527 /***
528  * @method parser:parse_file(name)
529  * Parse UCL object from file.
530  * @param {string} name filename to parse
531  * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
532 @example
533 local parser = ucl.parser()
534 local res,err = parser:parse_file('/some/file.conf')
535 
536 if not res then
537 	print('parser error: ' .. err)
538 else
539 	-- Do something with object
540 end
541  */
542 static int
543 lua_ucl_parser_parse_file (lua_State *L)
544 {
545 	struct ucl_parser *parser;
546 	const char *file;
547 	int ret = 2;
548 
549 	parser = lua_ucl_parser_get (L, 1);
550 	file = luaL_checkstring (L, 2);
551 
552 	if (parser != NULL && file != NULL) {
553 		if (ucl_parser_add_file (parser, file)) {
554 			lua_pushboolean (L, true);
555 			ret = 1;
556 		}
557 		else {
558 			lua_pushboolean (L, false);
559 			lua_pushstring (L, ucl_parser_get_error (parser));
560 		}
561 	}
562 	else {
563 		lua_pushboolean (L, false);
564 		lua_pushstring (L, "invalid arguments");
565 	}
566 
567 	return ret;
568 }
569 
570 /***
571  * @method parser:parse_string(input)
572  * Parse UCL object from file.
573  * @param {string} input string to parse
574  * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
575  */
576 static int
577 lua_ucl_parser_parse_string (lua_State *L)
578 {
579 	struct ucl_parser *parser;
580 	const char *string;
581 	size_t llen;
582 	int ret = 2;
583 
584 	parser = lua_ucl_parser_get (L, 1);
585 	string = luaL_checklstring (L, 2, &llen);
586 
587 	if (parser != NULL && string != NULL) {
588 		if (ucl_parser_add_chunk (parser, (const unsigned char *)string, llen)) {
589 			lua_pushboolean (L, true);
590 			ret = 1;
591 		}
592 		else {
593 			lua_pushboolean (L, false);
594 			lua_pushstring (L, ucl_parser_get_error (parser));
595 		}
596 	}
597 	else {
598 		lua_pushboolean (L, false);
599 		lua_pushstring (L, "invalid arguments");
600 	}
601 
602 	return ret;
603 }
604 
605 /***
606  * @method parser:get_object()
607  * Get top object from parser and export it to lua representation.
608  * @return {variant or nil} ucl object as lua native variable
609  */
610 static int
611 lua_ucl_parser_get_object (lua_State *L)
612 {
613 	struct ucl_parser *parser;
614 	ucl_object_t *obj;
615 	int ret = 1;
616 
617 	parser = lua_ucl_parser_get (L, 1);
618 	obj = ucl_parser_get_object (parser);
619 
620 	if (obj != NULL) {
621 		ret = ucl_object_push_lua (L, obj, false);
622 		/* no need to keep reference */
623 		ucl_object_unref (obj);
624 	}
625 	else {
626 		lua_pushnil (L);
627 	}
628 
629 	return ret;
630 }
631 
632 /***
633  * @method parser:get_object_wrapped()
634  * Get top object from parser and export it to userdata object without
635  * unwrapping to lua.
636  * @return {ucl.object or nil} ucl object wrapped variable
637  */
638 static int
639 lua_ucl_parser_get_object_wrapped (lua_State *L)
640 {
641 	struct ucl_parser *parser;
642 	ucl_object_t *obj;
643 	int ret = 1;
644 
645 	parser = lua_ucl_parser_get (L, 1);
646 	obj = ucl_parser_get_object (parser);
647 
648 	if (obj != NULL) {
649 		lua_ucl_push_opaque (L, obj);
650 	}
651 	else {
652 		lua_pushnil (L);
653 	}
654 
655 	return ret;
656 }
657 
658 /***
659  * @method parser:validate(schema)
660  * Validates the top object in the parser against schema. Schema might be
661  * another object or a string that represents file to load schema from.
662  *
663  * @param {string/table} schema input schema
664  * @return {result,err} two values: boolean result and the corresponding error
665  *
666  */
667 static int
668 lua_ucl_parser_validate (lua_State *L)
669 {
670 	struct ucl_parser *parser, *schema_parser;
671 	ucl_object_t *schema;
672 	const char *schema_file;
673 	struct ucl_schema_error err;
674 
675 	parser = lua_ucl_parser_get (L, 1);
676 
677 	if (parser && parser->top_obj) {
678 		if (lua_type (L, 2) == LUA_TTABLE) {
679 			schema = ucl_object_lua_import (L, 2);
680 
681 			if (schema == NULL) {
682 				lua_pushboolean (L, false);
683 				lua_pushstring (L, "cannot load schema from lua table");
684 
685 				return 2;
686 			}
687 		}
688 		else if (lua_type (L, 2) == LUA_TSTRING) {
689 			schema_parser = ucl_parser_new (0);
690 			schema_file = luaL_checkstring (L, 2);
691 
692 			if (!ucl_parser_add_file (schema_parser, schema_file)) {
693 				lua_pushboolean (L, false);
694 				lua_pushfstring (L, "cannot parse schema file \"%s\": "
695 						"%s", schema_file, ucl_parser_get_error (parser));
696 				ucl_parser_free (schema_parser);
697 
698 				return 2;
699 			}
700 
701 			schema = ucl_parser_get_object (schema_parser);
702 			ucl_parser_free (schema_parser);
703 		}
704 		else {
705 			lua_pushboolean (L, false);
706 			lua_pushstring (L, "invalid schema argument");
707 
708 			return 2;
709 		}
710 
711 		if (!ucl_object_validate (schema, parser->top_obj, &err)) {
712 			lua_pushboolean (L, false);
713 			lua_pushfstring (L, "validation error: "
714 					"%s", err.msg);
715 		}
716 		else {
717 			lua_pushboolean (L, true);
718 			lua_pushnil (L);
719 		}
720 
721 		ucl_object_unref (schema);
722 	}
723 	else {
724 		lua_pushboolean (L, false);
725 		lua_pushstring (L, "invalid parser or empty top object");
726 	}
727 
728 	return 2;
729 }
730 
731 static int
732 lua_ucl_parser_gc (lua_State *L)
733 {
734 	struct ucl_parser *parser;
735 
736 	parser = lua_ucl_parser_get (L, 1);
737 	ucl_parser_free (parser);
738 
739 	return 0;
740 }
741 
742 /***
743  * @method object:unwrap()
744  * Unwraps opaque ucl object to the native lua object (performing copying)
745  * @return {variant} any lua object
746  */
747 static int
748 lua_ucl_object_unwrap (lua_State *L)
749 {
750 	ucl_object_t *obj;
751 
752 	obj = lua_ucl_object_get (L, 1);
753 
754 	if (obj) {
755 		ucl_object_push_lua (L, obj, true);
756 	}
757 	else {
758 		lua_pushnil (L);
759 	}
760 
761 	return 1;
762 }
763 
764 /***
765  * @method object:tostring(type)
766  * Unwraps opaque ucl object to string (json by default). Optionally you can
767  * specify output format:
768  *
769  * - `json` - fine printed json
770  * - `json-compact` - compacted json
771  * - `config` - fine printed configuration
772  * - `ucl` - same as `config`
773  * - `yaml` - embedded yaml
774  * @param {string} type optional
775  * @return {string} string representation of the opaque ucl object
776  */
777 static int
778 lua_ucl_object_tostring (lua_State *L)
779 {
780 	ucl_object_t *obj;
781 	enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
782 
783 	obj = lua_ucl_object_get (L, 1);
784 
785 	if (obj) {
786 		if (lua_gettop (L) > 1) {
787 			if (lua_type (L, 2) == LUA_TSTRING) {
788 				const char *strtype = lua_tostring (L, 2);
789 
790 				if (strcasecmp (strtype, "json") == 0) {
791 					format = UCL_EMIT_JSON;
792 				}
793 				else if (strcasecmp (strtype, "json-compact") == 0) {
794 					format = UCL_EMIT_JSON_COMPACT;
795 				}
796 				else if (strcasecmp (strtype, "yaml") == 0) {
797 					format = UCL_EMIT_YAML;
798 				}
799 				else if (strcasecmp (strtype, "config") == 0 ||
800 						strcasecmp (strtype, "ucl") == 0) {
801 					format = UCL_EMIT_CONFIG;
802 				}
803 			}
804 		}
805 
806 		return lua_ucl_to_string (L, obj, format);
807 	}
808 	else {
809 		lua_pushnil (L);
810 	}
811 
812 	return 1;
813 }
814 
815 /***
816  * @method object:validate(schema[, path[, ext_refs]])
817  * Validates the given ucl object using schema object represented as another
818  * opaque ucl object. You can also specify path in the form `#/path/def` to
819  * specify the specific schema element to perform validation.
820  *
821  * @param {ucl.object} schema schema object
822  * @param {string} path optional path for validation procedure
823  * @return {result,err} two values: boolean result and the corresponding
824  * error, if `ext_refs` are also specified, then they are returned as opaque
825  * ucl object as {result,err,ext_refs}
826  */
827 static int
828 lua_ucl_object_validate (lua_State *L)
829 {
830 	ucl_object_t *obj, *schema, *ext_refs = NULL;
831 	const ucl_object_t *schema_elt;
832 	bool res = false;
833 	struct ucl_schema_error err;
834 	const char *path = NULL;
835 
836 	obj = lua_ucl_object_get (L, 1);
837 	schema = lua_ucl_object_get (L, 2);
838 
839 	if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) {
840 		if (lua_gettop (L) > 2) {
841 			if (lua_type (L, 3) == LUA_TSTRING) {
842 				path = lua_tostring (L, 3);
843 				if (path[0] == '#') {
844 					path++;
845 				}
846 			}
847 			else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) ==
848 						LUA_TTABLE) {
849 				/* External refs */
850 				ext_refs = lua_ucl_object_get (L, 3);
851 			}
852 
853 			if (lua_gettop (L) > 3) {
854 				if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) ==
855 						LUA_TTABLE) {
856 					/* External refs */
857 					ext_refs = lua_ucl_object_get (L, 4);
858 				}
859 			}
860 		}
861 
862 		if (path) {
863 			schema_elt = ucl_object_lookup_path_char (schema, path, '/');
864 		}
865 		else {
866 			/* Use the top object */
867 			schema_elt = schema;
868 		}
869 
870 		if (schema_elt) {
871 			res = ucl_object_validate_root_ext (schema_elt, obj, schema,
872 					ext_refs, &err);
873 
874 			if (res) {
875 				lua_pushboolean (L, res);
876 				lua_pushnil (L);
877 
878 				if (ext_refs) {
879 					lua_ucl_push_opaque (L, ext_refs);
880 				}
881 			}
882 			else {
883 				lua_pushboolean (L, res);
884 				lua_pushfstring (L, "validation error: %s", err.msg);
885 
886 				if (ext_refs) {
887 					lua_ucl_push_opaque (L, ext_refs);
888 				}
889 			}
890 		}
891 		else {
892 			lua_pushboolean (L, res);
893 
894 			lua_pushfstring (L, "cannot find the requested path: %s", path);
895 
896 			if (ext_refs) {
897 				lua_ucl_push_opaque (L, ext_refs);
898 			}
899 		}
900 	}
901 	else {
902 		lua_pushboolean (L, res);
903 		lua_pushstring (L, "invalid object or schema");
904 	}
905 
906 	if (ext_refs) {
907 		return 3;
908 	}
909 
910 	return 2;
911 }
912 
913 static int
914 lua_ucl_object_gc (lua_State *L)
915 {
916 	ucl_object_t *obj;
917 
918 	obj = lua_ucl_object_get (L, 1);
919 
920 	ucl_object_unref (obj);
921 
922 	return 0;
923 }
924 
925 static void
926 lua_ucl_parser_mt (lua_State *L)
927 {
928 	luaL_newmetatable (L, PARSER_META);
929 
930 	lua_pushvalue(L, -1);
931 	lua_setfield(L, -2, "__index");
932 
933 	lua_pushcfunction (L, lua_ucl_parser_gc);
934 	lua_setfield (L, -2, "__gc");
935 
936 	lua_pushcfunction (L, lua_ucl_parser_parse_file);
937 	lua_setfield (L, -2, "parse_file");
938 
939 	lua_pushcfunction (L, lua_ucl_parser_parse_string);
940 	lua_setfield (L, -2, "parse_string");
941 
942 	lua_pushcfunction (L, lua_ucl_parser_get_object);
943 	lua_setfield (L, -2, "get_object");
944 
945 	lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped);
946 	lua_setfield (L, -2, "get_object_wrapped");
947 
948 	lua_pushcfunction (L, lua_ucl_parser_validate);
949 	lua_setfield (L, -2, "validate");
950 
951 	lua_pop (L, 1);
952 }
953 
954 static void
955 lua_ucl_object_mt (lua_State *L)
956 {
957 	luaL_newmetatable (L, OBJECT_META);
958 
959 	lua_pushvalue(L, -1);
960 	lua_setfield(L, -2, "__index");
961 
962 	lua_pushcfunction (L, lua_ucl_object_gc);
963 	lua_setfield (L, -2, "__gc");
964 
965 	lua_pushcfunction (L, lua_ucl_object_tostring);
966 	lua_setfield (L, -2, "__tostring");
967 
968 	lua_pushcfunction (L, lua_ucl_object_tostring);
969 	lua_setfield (L, -2, "tostring");
970 
971 	lua_pushcfunction (L, lua_ucl_object_unwrap);
972 	lua_setfield (L, -2, "unwrap");
973 
974 	lua_pushcfunction (L, lua_ucl_object_unwrap);
975 	lua_setfield (L, -2, "tolua");
976 
977 	lua_pushcfunction (L, lua_ucl_object_validate);
978 	lua_setfield (L, -2, "validate");
979 
980 	lua_pushstring (L, OBJECT_META);
981 	lua_setfield (L, -2, "class");
982 
983 	lua_pop (L, 1);
984 }
985 
986 static int
987 lua_ucl_to_json (lua_State *L)
988 {
989 	ucl_object_t *obj;
990 	int format = UCL_EMIT_JSON;
991 
992 	if (lua_gettop (L) > 1) {
993 		if (lua_toboolean (L, 2)) {
994 			format = UCL_EMIT_JSON_COMPACT;
995 		}
996 	}
997 
998 	obj = ucl_object_lua_import (L, 1);
999 	if (obj != NULL) {
1000 		lua_ucl_to_string (L, obj, format);
1001 		ucl_object_unref (obj);
1002 	}
1003 	else {
1004 		lua_pushnil (L);
1005 	}
1006 
1007 	return 1;
1008 }
1009 
1010 static int
1011 lua_ucl_to_config (lua_State *L)
1012 {
1013 	ucl_object_t *obj;
1014 
1015 	obj = ucl_object_lua_import (L, 1);
1016 	if (obj != NULL) {
1017 		lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG);
1018 		ucl_object_unref (obj);
1019 	}
1020 	else {
1021 		lua_pushnil (L);
1022 	}
1023 
1024 	return 1;
1025 }
1026 
1027 /***
1028  * @function ucl.to_format(var, format)
1029  * Converts lua variable `var` to the specified `format`. Formats supported are:
1030  *
1031  * - `json` - fine printed json
1032  * - `json-compact` - compacted json
1033  * - `config` - fine printed configuration
1034  * - `ucl` - same as `config`
1035  * - `yaml` - embedded yaml
1036  *
1037  * If `var` contains function, they are called during output formatting and if
1038  * they return string value, then this value is used for ouptut.
1039  * @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
1040  * @param {string} format any available format
1041  * @return {string} string representation of `var` in the specific `format`.
1042  * @example
1043 local table = {
1044   str = 'value',
1045   num = 100500,
1046   null = ucl.null,
1047   func = function ()
1048     return 'huh'
1049   end
1050 }
1051 
1052 print(ucl.to_format(table, 'ucl'))
1053 -- Output:
1054 --[[
1055 num = 100500;
1056 str = "value";
1057 null = null;
1058 func = "huh";
1059 --]]
1060  */
1061 static int
1062 lua_ucl_to_format (lua_State *L)
1063 {
1064 	ucl_object_t *obj;
1065 	int format = UCL_EMIT_JSON;
1066 
1067 	if (lua_gettop (L) > 1) {
1068 		if (lua_type (L, 2) == LUA_TNUMBER) {
1069 			format = lua_tonumber (L, 2);
1070 			if (format < 0 || format >= UCL_EMIT_YAML) {
1071 				lua_pushnil (L);
1072 				return 1;
1073 			}
1074 		}
1075 		else if (lua_type (L, 2) == LUA_TSTRING) {
1076 			const char *strtype = lua_tostring (L, 2);
1077 
1078 			if (strcasecmp (strtype, "json") == 0) {
1079 				format = UCL_EMIT_JSON;
1080 			}
1081 			else if (strcasecmp (strtype, "json-compact") == 0) {
1082 				format = UCL_EMIT_JSON_COMPACT;
1083 			}
1084 			else if (strcasecmp (strtype, "yaml") == 0) {
1085 				format = UCL_EMIT_YAML;
1086 			}
1087 			else if (strcasecmp (strtype, "config") == 0 ||
1088 				strcasecmp (strtype, "ucl") == 0) {
1089 				format = UCL_EMIT_CONFIG;
1090 			}
1091 		}
1092 	}
1093 
1094 	obj = ucl_object_lua_import (L, 1);
1095 	if (obj != NULL) {
1096 		lua_ucl_to_string (L, obj, format);
1097 		ucl_object_unref (obj);
1098 	}
1099 	else {
1100 		lua_pushnil (L);
1101 	}
1102 
1103 	return 1;
1104 }
1105 
1106 static int
1107 lua_ucl_null_tostring (lua_State* L)
1108 {
1109 	lua_pushstring (L, "null");
1110 	return 1;
1111 }
1112 
1113 static void
1114 lua_ucl_null_mt (lua_State *L)
1115 {
1116 	luaL_newmetatable (L, NULL_META);
1117 
1118 	lua_pushcfunction (L, lua_ucl_null_tostring);
1119 	lua_setfield (L, -2, "__tostring");
1120 
1121 	lua_pop (L, 1);
1122 }
1123 
1124 int
1125 luaopen_ucl (lua_State *L)
1126 {
1127 	lua_ucl_parser_mt (L);
1128 	lua_ucl_null_mt (L);
1129 	lua_ucl_object_mt (L);
1130 
1131 	/* Create the refs weak table: */
1132 	lua_createtable (L, 0, 2);
1133 	lua_pushliteral (L, "v"); /* tbl, "v" */
1134 	lua_setfield (L, -2, "__mode");
1135 	lua_pushvalue (L, -1); /* tbl, tbl */
1136 	lua_setmetatable (L, -2); /* tbl */
1137 	lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs");
1138 
1139 	lua_newtable (L);
1140 
1141 	lua_pushcfunction (L, lua_ucl_parser_init);
1142 	lua_setfield (L, -2, "parser");
1143 
1144 	lua_pushcfunction (L, lua_ucl_to_json);
1145 	lua_setfield (L, -2, "to_json");
1146 
1147 	lua_pushcfunction (L, lua_ucl_to_config);
1148 	lua_setfield (L, -2, "to_config");
1149 
1150 	lua_pushcfunction (L, lua_ucl_to_format);
1151 	lua_setfield (L, -2, "to_format");
1152 
1153 	ucl_null = lua_newuserdata (L, 0);
1154 	luaL_getmetatable (L, NULL_META);
1155 	lua_setmetatable (L, -2);
1156 
1157 	lua_pushvalue (L, -1);
1158 	lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null");
1159 
1160 	lua_setfield (L, -2, "null");
1161 
1162 	return 1;
1163 }
1164 
1165 struct ucl_lua_funcdata*
1166 ucl_object_toclosure (const ucl_object_t *obj)
1167 {
1168 	if (obj == NULL || obj->type != UCL_USERDATA) {
1169 		return NULL;
1170 	}
1171 
1172 	return (struct ucl_lua_funcdata*)obj->value.ud;
1173 }
1174