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
33/***
34 * @module ucl
35 * This lua module allows to parse objects from strings and to store data into
36 * ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
37 * @example
38local ucl = require("ucl")
39
40local parser = ucl.parser()
41local res,err = parser:parse_string('{key=value}')
42
43if not res then
44	print('parser error: ' .. err)
45else
46	local obj = parser:get_object()
47	local got = ucl.to_format(obj, 'json')
48endif
49
50local table = {
51  str = 'value',
52  num = 100500,
53  null = ucl.null,
54  func = function ()
55    return 'huh'
56  end
57}
58
59print(ucl.to_format(table, 'ucl'))
60-- Output:
61--[[
62num = 100500;
63str = "value";
64null = null;
65func = "huh";
66--]]
67 */
68
69#define PARSER_META "ucl.parser.meta"
70#define EMITTER_META "ucl.emitter.meta"
71#define NULL_META "ucl.null.meta"
72#define OBJECT_META "ucl.object.meta"
73#define UCL_OBJECT_TYPE_META "ucl.type.object"
74#define UCL_ARRAY_TYPE_META "ucl.type.array"
75#define UCL_IMPL_ARRAY_TYPE_META "ucl.type.impl_array"
76
77static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags);
78static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, int flags);
79static int ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags);
80static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags);
81static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags);
82
83static void *ucl_null;
84
85
86enum lua_ucl_push_flags {
87	LUA_UCL_DEFAULT_FLAGS = 0,
88	LUA_UCL_ALLOW_ARRAY = (1u << 0u),
89	LUA_UCL_CONVERT_NIL = (1u << 1u),
90};
91
92/**
93 * Push a single element of an object to lua
94 * @param L
95 * @param key
96 * @param obj
97 */
98static void
99ucl_object_lua_push_element (lua_State *L, const char *key,
100		const ucl_object_t *obj, int flags)
101{
102	lua_pushstring (L, key);
103	ucl_object_push_lua_common (L, obj, flags|LUA_UCL_ALLOW_ARRAY);
104	lua_settable (L, -3);
105}
106
107static void
108lua_ucl_userdata_dtor (void *ud)
109{
110	struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
111
112	luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx);
113	if (fd->ret != NULL) {
114		free (fd->ret);
115	}
116	free (fd);
117}
118
119static const char *
120lua_ucl_userdata_emitter (void *ud)
121{
122	struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
123	const char *out = "";
124
125	lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx);
126
127	lua_pcall (fd->L, 0, 1, 0);
128	out = lua_tostring (fd->L, -1);
129
130	if (out != NULL) {
131		/* We need to store temporary string in a more appropriate place */
132		if (fd->ret) {
133			free (fd->ret);
134		}
135		fd->ret = strdup (out);
136	}
137
138	lua_settop (fd->L, 0);
139
140	return fd->ret;
141}
142
143/**
144 * Push a single object to lua
145 * @param L
146 * @param obj
147 * @return
148 */
149static int
150ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
151		int flags)
152{
153	const ucl_object_t *cur;
154	ucl_object_iter_t it = NULL;
155
156	if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) {
157		/* Actually we need to push this as an array */
158		return ucl_object_lua_push_array (L, obj, flags);
159	}
160
161	lua_createtable (L, 0, obj->len);
162	it = NULL;
163
164	while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
165		ucl_object_lua_push_element (L, ucl_object_key (cur), cur, flags);
166	}
167
168	luaL_getmetatable (L, UCL_OBJECT_TYPE_META);
169	lua_setmetatable (L, -2);
170
171	return 1;
172}
173
174/**
175 * Push an array to lua as table indexed by integers
176 * @param L
177 * @param obj
178 * @return
179 */
180static int
181ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags)
182{
183	const ucl_object_t *cur;
184	ucl_object_iter_t it;
185	int i = 1, nelt = 0;
186
187	if (obj->type == UCL_ARRAY) {
188		nelt = obj->len;
189		it = ucl_object_iterate_new (obj);
190		lua_createtable (L, nelt, 0);
191
192		while ((cur = ucl_object_iterate_safe (it, true))) {
193			ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY));
194			lua_rawseti (L, -2, i);
195			i ++;
196		}
197
198		luaL_getmetatable (L, UCL_ARRAY_TYPE_META);
199		lua_setmetatable (L, -2);
200
201		ucl_object_iterate_free (it);
202	}
203	else {
204		/* Optimize allocation by preallocation of table */
205		LL_FOREACH (obj, cur) {
206			nelt ++;
207		}
208
209		lua_createtable (L, nelt, 0);
210
211		LL_FOREACH (obj, cur) {
212			ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY));
213			lua_rawseti (L, -2, i);
214			i ++;
215		}
216
217		luaL_getmetatable (L, UCL_IMPL_ARRAY_TYPE_META);
218		lua_setmetatable (L, -2);
219	}
220
221	return 1;
222}
223
224/**
225 * Push a simple object to lua depending on its actual type
226 */
227static int
228ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
229		int flags)
230{
231	struct ucl_lua_funcdata *fd;
232
233	if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) {
234		/* Actually we need to push this as an array */
235		return ucl_object_lua_push_array (L, obj, flags);
236	}
237
238	switch (obj->type) {
239	case UCL_BOOLEAN:
240		lua_pushboolean (L, ucl_obj_toboolean (obj));
241		break;
242	case UCL_STRING:
243		lua_pushstring (L, ucl_obj_tostring (obj));
244		break;
245	case UCL_INT:
246#if LUA_VERSION_NUM >= 501
247		lua_pushinteger (L, ucl_obj_toint (obj));
248#else
249		lua_pushnumber (L, ucl_obj_toint (obj));
250#endif
251		break;
252	case UCL_FLOAT:
253	case UCL_TIME:
254		lua_pushnumber (L, ucl_obj_todouble (obj));
255		break;
256	case UCL_NULL:
257		if (flags & LUA_UCL_CONVERT_NIL) {
258			lua_pushboolean (L, false);
259		}
260		else {
261			lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
262		}
263		break;
264	case UCL_USERDATA:
265		fd = (struct ucl_lua_funcdata *)obj->value.ud;
266		lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx);
267		break;
268	default:
269		lua_pushnil (L);
270		break;
271	}
272
273	return 1;
274}
275
276static int
277ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags)
278{
279	switch (obj->type) {
280	case UCL_OBJECT:
281		return ucl_object_lua_push_object (L, obj, flags);
282	case UCL_ARRAY:
283		return ucl_object_lua_push_array (L, obj, flags);
284	default:
285		return ucl_object_lua_push_scalar (L, obj, flags);
286	}
287}
288
289/***
290 * @function ucl_object_push_lua(L, obj, allow_array)
291 * This is a `C` function to push `UCL` object as lua variable. This function
292 * converts `obj` to lua representation using the following conversions:
293 *
294 * - *scalar* values are directly presented by lua objects
295 * - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
296 * this can be used to pass functions from lua to c and vice-versa
297 * - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations
298 * - *objects* are converted to lua tables with string indicies
299 * @param {lua_State} L lua state pointer
300 * @param {ucl_object_t} obj object to push
301 * @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays)
302 * @return {int} `1` if an object is pushed to lua
303 */
304int
305ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
306{
307	return ucl_object_push_lua_common (L, obj,
308			allow_array ? LUA_UCL_ALLOW_ARRAY : LUA_UCL_DEFAULT_FLAGS);
309}
310
311int
312ucl_object_push_lua_filter_nil (lua_State *L, const ucl_object_t *obj, bool allow_array)
313{
314	return ucl_object_push_lua_common (L, obj,
315			allow_array ? (LUA_UCL_ALLOW_ARRAY|LUA_UCL_CONVERT_NIL) :
316			(LUA_UCL_DEFAULT_FLAGS|LUA_UCL_CONVERT_NIL));
317}
318
319/**
320 * Parse lua table into object top
321 * @param L
322 * @param top
323 * @param idx
324 */
325static ucl_object_t *
326ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags)
327{
328	ucl_object_t *obj, *top = NULL, *cur;
329	size_t keylen;
330	const char *k;
331	bool is_array = true, is_implicit = false, found_mt = false;
332	size_t max = 0, nelts = 0;
333
334	if (idx < 0) {
335		/* For negative indicies we want to invert them */
336		idx = lua_gettop (L) + idx + 1;
337	}
338
339	/* First, we check from metatable */
340	if (luaL_getmetafield (L, idx, "class") != 0) {
341
342		if (lua_type (L, -1) == LUA_TSTRING) {
343			const char *classname = lua_tostring (L, -1);
344
345			if (strcmp (classname, UCL_OBJECT_TYPE_META) == 0) {
346				is_array = false;
347				found_mt = true;
348			} else if (strcmp (classname, UCL_ARRAY_TYPE_META) == 0) {
349				is_array = true;
350				found_mt = true;
351#if LUA_VERSION_NUM >= 502
352				max = lua_rawlen (L, idx);
353#else
354				max = lua_objlen (L, idx);
355#endif
356				nelts = max;
357			} else if (strcmp (classname, UCL_IMPL_ARRAY_TYPE_META) == 0) {
358				is_array = true;
359				is_implicit = true;
360				found_mt = true;
361#if LUA_VERSION_NUM >= 502
362				max = lua_rawlen (L, idx);
363#else
364				max = lua_objlen (L, idx);
365#endif
366				nelts = max;
367			}
368		}
369
370		lua_pop (L, 1);
371	}
372
373	if (!found_mt) {
374		/* Check for array (it is all inefficient) */
375		lua_pushnil (L);
376
377		while (lua_next (L, idx) != 0) {
378			lua_pushvalue (L, -2);
379
380			if (lua_type (L, -1) == LUA_TNUMBER) {
381				double num = lua_tonumber (L, -1);
382				if (num == (int) num) {
383					if (num > max) {
384						max = num;
385					}
386				}
387				else {
388					/* Keys are not integer */
389					is_array = false;
390				}
391			}
392			else {
393				/* Keys are not numeric */
394				is_array = false;
395			}
396
397			lua_pop (L, 2);
398			nelts ++;
399		}
400	}
401
402	/* Table iterate */
403	if (is_array) {
404		int i;
405
406		if (!is_implicit) {
407			top = ucl_object_typed_new (UCL_ARRAY);
408			ucl_object_reserve (top, nelts);
409		}
410		else {
411			top = NULL;
412		}
413
414		for (i = 1; i <= max; i ++) {
415			lua_pushinteger (L, i);
416			lua_gettable (L, idx);
417
418			obj = ucl_object_lua_fromelt (L, lua_gettop (L), flags);
419
420			if (obj != NULL) {
421				if (is_implicit) {
422					DL_APPEND (top, obj);
423				}
424				else {
425					ucl_array_append (top, obj);
426				}
427			}
428			lua_pop (L, 1);
429		}
430	}
431	else {
432		lua_pushnil (L);
433		top = ucl_object_typed_new (UCL_OBJECT);
434		ucl_object_reserve (top, nelts);
435
436		while (lua_next (L, idx) != 0) {
437			/* copy key to avoid modifications */
438			lua_pushvalue (L, -2);
439			k = lua_tolstring (L, -1, &keylen);
440			obj = ucl_object_lua_fromelt (L, lua_gettop (L) - 1, flags);
441
442			if (obj != NULL) {
443				ucl_object_insert_key (top, obj, k, keylen, true);
444
445				DL_FOREACH (obj, cur) {
446					if (cur->keylen == 0) {
447						cur->keylen = obj->keylen;
448						cur->key = obj->key;
449					}
450				}
451			}
452			lua_pop (L, 2);
453		}
454	}
455
456	return top;
457}
458
459/**
460 * Get a single element from lua to object obj
461 * @param L
462 * @param obj
463 * @param idx
464 */
465static ucl_object_t *
466ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags)
467{
468	int type;
469	double num;
470	ucl_object_t *obj = NULL;
471	struct ucl_lua_funcdata *fd;
472	const char *str;
473	size_t sz;
474
475	type = lua_type (L, idx);
476
477	switch (type) {
478	case LUA_TSTRING:
479		str = lua_tolstring (L, idx, &sz);
480
481		if (str) {
482			obj = ucl_object_fromstring_common (str, sz, flags);
483		}
484		else {
485			obj = ucl_object_typed_new (UCL_NULL);
486		}
487		break;
488	case LUA_TNUMBER:
489		num = lua_tonumber (L, idx);
490		if (num == (int64_t)num) {
491			obj = ucl_object_fromint (num);
492		}
493		else {
494			obj = ucl_object_fromdouble (num);
495		}
496		break;
497	case LUA_TBOOLEAN:
498		obj = ucl_object_frombool (lua_toboolean (L, idx));
499		break;
500	case LUA_TUSERDATA:
501		if (lua_topointer (L, idx) == ucl_null) {
502			obj = ucl_object_typed_new (UCL_NULL);
503		}
504		break;
505	case LUA_TTABLE:
506	case LUA_TFUNCTION:
507	case LUA_TTHREAD:
508		if (luaL_getmetafield (L, idx, "__gen_ucl")) {
509			if (lua_isfunction (L, -1)) {
510				lua_settop (L, 3); /* gen, obj, func */
511				lua_insert (L, 1); /* func, gen, obj */
512				lua_insert (L, 2); /* func, obj, gen */
513				lua_call(L, 2, 1);
514				obj = ucl_object_lua_fromelt (L, 1, flags);
515			}
516			lua_pop (L, 2);
517		}
518		else {
519			if (type == LUA_TTABLE) {
520				obj = ucl_object_lua_fromtable (L, idx, flags);
521			}
522			else if (type == LUA_TFUNCTION) {
523				fd = malloc (sizeof (*fd));
524				if (fd != NULL) {
525					lua_pushvalue (L, idx);
526					fd->L = L;
527					fd->ret = NULL;
528					fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
529
530					obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
531							lua_ucl_userdata_emitter, (void *)fd);
532				}
533			}
534		}
535		break;
536	}
537
538	return obj;
539}
540
541/**
542 * @function ucl_object_lua_import(L, idx)
543 * Extracts ucl object from lua variable at `idx` position,
544 * @see ucl_object_push_lua for conversion definitions
545 * @param {lua_state} L lua state machine pointer
546 * @param {int} idx index where the source variable is placed
547 * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
548 * this object thus needs to be unref'ed after usage.
549 */
550ucl_object_t *
551ucl_object_lua_import (lua_State *L, int idx)
552{
553	ucl_object_t *obj;
554	int t;
555
556	t = lua_type (L, idx);
557	switch (t) {
558	case LUA_TTABLE:
559		obj = ucl_object_lua_fromtable (L, idx, 0);
560		break;
561	default:
562		obj = ucl_object_lua_fromelt (L, idx, 0);
563		break;
564	}
565
566	return obj;
567}
568
569/**
570 * @function ucl_object_lua_import_escape(L, idx)
571 * Extracts ucl object from lua variable at `idx` position escaping JSON strings
572 * @see ucl_object_push_lua for conversion definitions
573 * @param {lua_state} L lua state machine pointer
574 * @param {int} idx index where the source variable is placed
575 * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
576 * this object thus needs to be unref'ed after usage.
577 */
578ucl_object_t *
579ucl_object_lua_import_escape (lua_State *L, int idx)
580{
581	ucl_object_t *obj;
582	int t;
583
584	t = lua_type (L, idx);
585	switch (t) {
586	case LUA_TTABLE:
587		obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW);
588		break;
589	default:
590		obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW);
591		break;
592	}
593
594	return obj;
595}
596
597static int
598lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
599{
600	unsigned char *result;
601
602	result = ucl_object_emit (obj, type);
603
604	if (result != NULL) {
605		lua_pushstring (L, (const char *)result);
606		free (result);
607	}
608	else {
609		lua_pushnil (L);
610	}
611
612	return 1;
613}
614
615static int
616lua_ucl_parser_init (lua_State *L)
617{
618	struct ucl_parser *parser, **pparser;
619	int flags = UCL_PARSER_NO_FILEVARS;
620
621	if (lua_gettop (L) >= 1) {
622		flags = lua_tonumber (L, 1);
623	}
624
625	parser = ucl_parser_new (flags);
626	if (parser == NULL) {
627		lua_pushnil (L);
628		return 1;
629	}
630
631	pparser = lua_newuserdata (L, sizeof (parser));
632	*pparser = parser;
633	luaL_getmetatable (L, PARSER_META);
634	lua_setmetatable (L, -2);
635
636	return 1;
637}
638
639static struct ucl_parser *
640lua_ucl_parser_get (lua_State *L, int index)
641{
642	return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
643}
644
645static ucl_object_t *
646lua_ucl_object_get (lua_State *L, int index)
647{
648	return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META));
649}
650
651static void
652lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj)
653{
654	ucl_object_t **pobj;
655
656	pobj = lua_newuserdata (L, sizeof (*pobj));
657	*pobj = obj;
658	luaL_getmetatable (L, OBJECT_META);
659	lua_setmetatable (L, -2);
660}
661
662static inline enum ucl_parse_type
663lua_ucl_str_to_parse_type (const char *str)
664{
665	enum ucl_parse_type type = UCL_PARSE_UCL;
666
667	if (str != NULL) {
668		if (strcasecmp (str, "msgpack") == 0) {
669			type = UCL_PARSE_MSGPACK;
670		}
671		else if (strcasecmp (str, "sexp") == 0 ||
672				strcasecmp (str, "csexp") == 0) {
673			type = UCL_PARSE_CSEXP;
674		}
675		else if (strcasecmp (str, "auto") == 0) {
676			type = UCL_PARSE_AUTO;
677		}
678	}
679
680	return type;
681}
682
683/***
684 * @method parser:parse_file(name)
685 * Parse UCL object from file.
686 * @param {string} name filename to parse
687 * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
688@example
689local parser = ucl.parser()
690local res,err = parser:parse_file('/some/file.conf')
691
692if not res then
693	print('parser error: ' .. err)
694else
695	-- Do something with object
696end
697 */
698static int
699lua_ucl_parser_parse_file (lua_State *L)
700{
701	struct ucl_parser *parser;
702	const char *file;
703	int ret = 2;
704
705	parser = lua_ucl_parser_get (L, 1);
706	file = luaL_checkstring (L, 2);
707
708	if (parser != NULL && file != NULL) {
709		if (ucl_parser_add_file (parser, file)) {
710			lua_pushboolean (L, true);
711			ret = 1;
712		}
713		else {
714			lua_pushboolean (L, false);
715			lua_pushstring (L, ucl_parser_get_error (parser));
716		}
717	}
718	else {
719		lua_pushboolean (L, false);
720		lua_pushstring (L, "invalid arguments");
721	}
722
723	return ret;
724}
725
726/***
727 * @method parser:register_variable(name, value)
728 * Register parser variable
729 * @param {string} name name of variable
730 * @param {string} value value of variable
731 * @return {bool} success
732@example
733local parser = ucl.parser()
734local res = parser:register_variable('CONFDIR', '/etc/foo')
735 */
736static int
737lua_ucl_parser_register_variable (lua_State *L)
738{
739	struct ucl_parser *parser;
740	const char *name, *value;
741	int ret = 2;
742
743	parser = lua_ucl_parser_get (L, 1);
744	name = luaL_checkstring (L, 2);
745	value = luaL_checkstring (L, 3);
746
747	if (parser != NULL && name != NULL && value != NULL) {
748		ucl_parser_register_variable (parser, name, value);
749		lua_pushboolean (L, true);
750		ret = 1;
751	}
752	else {
753		return luaL_error (L, "invalid arguments");
754	}
755
756	return ret;
757}
758
759/***
760 * @method parser:register_variables(vars)
761 * Register parser variables
762 * @param {table} vars names/values of variables
763 * @return {bool} success
764@example
765local parser = ucl.parser()
766local res = parser:register_variables({CONFDIR = '/etc/foo', VARDIR = '/var'})
767 */
768static int
769lua_ucl_parser_register_variables (lua_State *L)
770{
771	struct ucl_parser *parser;
772	const char *name, *value;
773	int ret = 2;
774
775	parser = lua_ucl_parser_get (L, 1);
776
777	if (parser != NULL && lua_type (L, 2) == LUA_TTABLE) {
778		for (lua_pushnil (L); lua_next (L, 2); lua_pop (L, 1)) {
779			lua_pushvalue (L, -2);
780			name = luaL_checkstring (L, -1);
781			value = luaL_checkstring (L, -2);
782			ucl_parser_register_variable (parser, name, value);
783			lua_pop (L, 1);
784		}
785
786		lua_pushboolean (L, true);
787		ret = 1;
788	}
789	else {
790		return luaL_error (L, "invalid arguments");
791	}
792
793	return ret;
794}
795
796/***
797 * @method parser:parse_string(input)
798 * Parse UCL object from file.
799 * @param {string} input string to parse
800 * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
801 */
802static int
803lua_ucl_parser_parse_string (lua_State *L)
804{
805	struct ucl_parser *parser;
806	const char *string;
807	size_t llen;
808	enum ucl_parse_type type = UCL_PARSE_UCL;
809	int ret = 2;
810
811	parser = lua_ucl_parser_get (L, 1);
812	string = luaL_checklstring (L, 2, &llen);
813
814	if (lua_type (L, 3) == LUA_TSTRING) {
815		type = lua_ucl_str_to_parse_type (lua_tostring (L, 3));
816	}
817
818	if (parser != NULL && string != NULL) {
819		if (ucl_parser_add_chunk_full (parser, (const unsigned char *)string,
820				llen, 0, UCL_DUPLICATE_APPEND, type)) {
821			lua_pushboolean (L, true);
822			ret = 1;
823		}
824		else {
825			lua_pushboolean (L, false);
826			lua_pushstring (L, ucl_parser_get_error (parser));
827		}
828	}
829	else {
830		lua_pushboolean (L, false);
831		lua_pushstring (L, "invalid arguments");
832	}
833
834	return ret;
835}
836
837struct _rspamd_lua_text {
838	const char *start;
839	unsigned int len;
840	unsigned int flags;
841};
842
843/***
844 * @method parser:parse_text(input)
845 * Parse UCL object from text object (Rspamd specific).
846 * @param {rspamd_text} input text to parse
847 * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
848 */
849static int
850lua_ucl_parser_parse_text (lua_State *L)
851{
852	struct ucl_parser *parser;
853	struct _rspamd_lua_text *t;
854	enum ucl_parse_type type = UCL_PARSE_UCL;
855	int ret = 2;
856
857	parser = lua_ucl_parser_get (L, 1);
858	t = lua_touserdata (L, 2);
859
860	if (lua_type (L, 3) == LUA_TSTRING) {
861		type = lua_ucl_str_to_parse_type (lua_tostring (L, 3));
862	}
863
864	if (parser != NULL && t != NULL) {
865		if (ucl_parser_add_chunk_full (parser, (const unsigned char *)t->start,
866				t->len, 0, UCL_DUPLICATE_APPEND, type)) {
867			lua_pushboolean (L, true);
868			ret = 1;
869		}
870		else {
871			lua_pushboolean (L, false);
872			lua_pushstring (L, ucl_parser_get_error (parser));
873		}
874	}
875	else {
876		lua_pushboolean (L, false);
877		lua_pushstring (L, "invalid arguments");
878	}
879
880	return ret;
881}
882
883/***
884 * @method parser:get_object()
885 * Get top object from parser and export it to lua representation.
886 * @return {variant or nil} ucl object as lua native variable
887 */
888static int
889lua_ucl_parser_get_object (lua_State *L)
890{
891	struct ucl_parser *parser;
892	ucl_object_t *obj;
893	int ret = 1;
894
895	parser = lua_ucl_parser_get (L, 1);
896	obj = ucl_parser_get_object (parser);
897
898	if (obj != NULL) {
899		ret = ucl_object_push_lua (L, obj, false);
900		/* no need to keep reference */
901		ucl_object_unref (obj);
902	}
903	else {
904		lua_pushnil (L);
905	}
906
907	return ret;
908}
909
910/***
911 * @method parser:get_object_wrapped()
912 * Get top object from parser and export it to userdata object without
913 * unwrapping to lua.
914 * @return {ucl.object or nil} ucl object wrapped variable
915 */
916static int
917lua_ucl_parser_get_object_wrapped (lua_State *L)
918{
919	struct ucl_parser *parser;
920	ucl_object_t *obj;
921	int ret = 1;
922
923	parser = lua_ucl_parser_get (L, 1);
924	obj = ucl_parser_get_object (parser);
925
926	if (obj != NULL) {
927		lua_ucl_push_opaque (L, obj);
928	}
929	else {
930		lua_pushnil (L);
931	}
932
933	return ret;
934}
935
936/***
937 * @method parser:validate(schema)
938 * Validates the top object in the parser against schema. Schema might be
939 * another object or a string that represents file to load schema from.
940 *
941 * @param {string/table} schema input schema
942 * @return {result,err} two values: boolean result and the corresponding error
943 *
944 */
945static int
946lua_ucl_parser_validate (lua_State *L)
947{
948	struct ucl_parser *parser, *schema_parser;
949	ucl_object_t *schema;
950	const char *schema_file;
951	struct ucl_schema_error err;
952
953	parser = lua_ucl_parser_get (L, 1);
954
955	if (parser && parser->top_obj) {
956		if (lua_type (L, 2) == LUA_TTABLE) {
957			schema = ucl_object_lua_import (L, 2);
958
959			if (schema == NULL) {
960				lua_pushboolean (L, false);
961				lua_pushstring (L, "cannot load schema from lua table");
962
963				return 2;
964			}
965		}
966		else if (lua_type (L, 2) == LUA_TSTRING) {
967			schema_parser = ucl_parser_new (0);
968			schema_file = luaL_checkstring (L, 2);
969
970			if (!ucl_parser_add_file (schema_parser, schema_file)) {
971				lua_pushboolean (L, false);
972				lua_pushfstring (L, "cannot parse schema file \"%s\": "
973						"%s", schema_file, ucl_parser_get_error (parser));
974				ucl_parser_free (schema_parser);
975
976				return 2;
977			}
978
979			schema = ucl_parser_get_object (schema_parser);
980			ucl_parser_free (schema_parser);
981		}
982		else {
983			lua_pushboolean (L, false);
984			lua_pushstring (L, "invalid schema argument");
985
986			return 2;
987		}
988
989		if (!ucl_object_validate (schema, parser->top_obj, &err)) {
990			lua_pushboolean (L, false);
991			lua_pushfstring (L, "validation error: "
992					"%s", err.msg);
993		}
994		else {
995			lua_pushboolean (L, true);
996			lua_pushnil (L);
997		}
998
999		ucl_object_unref (schema);
1000	}
1001	else {
1002		lua_pushboolean (L, false);
1003		lua_pushstring (L, "invalid parser or empty top object");
1004	}
1005
1006	return 2;
1007}
1008
1009static int
1010lua_ucl_parser_gc (lua_State *L)
1011{
1012	struct ucl_parser *parser;
1013
1014	parser = lua_ucl_parser_get (L, 1);
1015	ucl_parser_free (parser);
1016
1017	return 0;
1018}
1019
1020/***
1021 * @method object:unwrap()
1022 * Unwraps opaque ucl object to the native lua object (performing copying)
1023 * @return {variant} any lua object
1024 */
1025static int
1026lua_ucl_object_unwrap (lua_State *L)
1027{
1028	ucl_object_t *obj;
1029
1030	obj = lua_ucl_object_get (L, 1);
1031
1032	if (obj) {
1033		ucl_object_push_lua (L, obj, true);
1034	}
1035	else {
1036		lua_pushnil (L);
1037	}
1038
1039	return 1;
1040}
1041
1042static inline enum ucl_emitter
1043lua_ucl_str_to_emit_type (const char *strtype)
1044{
1045	enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
1046
1047	if (strcasecmp (strtype, "json") == 0) {
1048		format = UCL_EMIT_JSON;
1049	}
1050	else if (strcasecmp (strtype, "json-compact") == 0) {
1051		format = UCL_EMIT_JSON_COMPACT;
1052	}
1053	else if (strcasecmp (strtype, "yaml") == 0) {
1054		format = UCL_EMIT_YAML;
1055	}
1056	else if (strcasecmp (strtype, "config") == 0 ||
1057			strcasecmp (strtype, "ucl") == 0) {
1058		format = UCL_EMIT_CONFIG;
1059	}
1060
1061	return format;
1062}
1063
1064/***
1065 * @method object:tostring(type)
1066 * Unwraps opaque ucl object to string (json by default). Optionally you can
1067 * specify output format:
1068 *
1069 * - `json` - fine printed json
1070 * - `json-compact` - compacted json
1071 * - `config` - fine printed configuration
1072 * - `ucl` - same as `config`
1073 * - `yaml` - embedded yaml
1074 * @param {string} type optional
1075 * @return {string} string representation of the opaque ucl object
1076 */
1077static int
1078lua_ucl_object_tostring (lua_State *L)
1079{
1080	ucl_object_t *obj;
1081	enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
1082
1083	obj = lua_ucl_object_get (L, 1);
1084
1085	if (obj) {
1086		if (lua_gettop (L) > 1) {
1087			if (lua_type (L, 2) == LUA_TSTRING) {
1088				const char *strtype = lua_tostring (L, 2);
1089
1090				format = lua_ucl_str_to_emit_type (strtype);
1091			}
1092		}
1093
1094		return lua_ucl_to_string (L, obj, format);
1095	}
1096	else {
1097		lua_pushnil (L);
1098	}
1099
1100	return 1;
1101}
1102
1103/***
1104 * @method object:validate(schema[, path[, ext_refs]])
1105 * Validates the given ucl object using schema object represented as another
1106 * opaque ucl object. You can also specify path in the form `#/path/def` to
1107 * specify the specific schema element to perform validation.
1108 *
1109 * @param {ucl.object} schema schema object
1110 * @param {string} path optional path for validation procedure
1111 * @return {result,err} two values: boolean result and the corresponding
1112 * error, if `ext_refs` are also specified, then they are returned as opaque
1113 * ucl object as {result,err,ext_refs}
1114 */
1115static int
1116lua_ucl_object_validate (lua_State *L)
1117{
1118	ucl_object_t *obj, *schema, *ext_refs = NULL;
1119	const ucl_object_t *schema_elt;
1120	bool res = false;
1121	struct ucl_schema_error err;
1122	const char *path = NULL;
1123
1124	obj = lua_ucl_object_get (L, 1);
1125	schema = lua_ucl_object_get (L, 2);
1126
1127	if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) {
1128		if (lua_gettop (L) > 2) {
1129			if (lua_type (L, 3) == LUA_TSTRING) {
1130				path = lua_tostring (L, 3);
1131				if (path[0] == '#') {
1132					path++;
1133				}
1134			}
1135			else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) ==
1136						LUA_TTABLE) {
1137				/* External refs */
1138				ext_refs = lua_ucl_object_get (L, 3);
1139			}
1140
1141			if (lua_gettop (L) > 3) {
1142				if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) ==
1143						LUA_TTABLE) {
1144					/* External refs */
1145					ext_refs = lua_ucl_object_get (L, 4);
1146				}
1147			}
1148		}
1149
1150		if (path) {
1151			schema_elt = ucl_object_lookup_path_char (schema, path, '/');
1152		}
1153		else {
1154			/* Use the top object */
1155			schema_elt = schema;
1156		}
1157
1158		if (schema_elt) {
1159			res = ucl_object_validate_root_ext (schema_elt, obj, schema,
1160					ext_refs, &err);
1161
1162			if (res) {
1163				lua_pushboolean (L, res);
1164				lua_pushnil (L);
1165
1166				if (ext_refs) {
1167					lua_ucl_push_opaque (L, ext_refs);
1168				}
1169			}
1170			else {
1171				lua_pushboolean (L, res);
1172				lua_pushfstring (L, "validation error: %s", err.msg);
1173
1174				if (ext_refs) {
1175					lua_ucl_push_opaque (L, ext_refs);
1176				}
1177			}
1178		}
1179		else {
1180			lua_pushboolean (L, res);
1181
1182			lua_pushfstring (L, "cannot find the requested path: %s", path);
1183
1184			if (ext_refs) {
1185				lua_ucl_push_opaque (L, ext_refs);
1186			}
1187		}
1188	}
1189	else {
1190		lua_pushboolean (L, res);
1191		lua_pushstring (L, "invalid object or schema");
1192	}
1193
1194	if (ext_refs) {
1195		return 3;
1196	}
1197
1198	return 2;
1199}
1200
1201static int
1202lua_ucl_object_gc (lua_State *L)
1203{
1204	ucl_object_t *obj;
1205
1206	obj = lua_ucl_object_get (L, 1);
1207
1208	ucl_object_unref (obj);
1209
1210	return 0;
1211}
1212
1213static void
1214lua_ucl_parser_mt (lua_State *L)
1215{
1216	luaL_newmetatable (L, PARSER_META);
1217
1218	lua_pushvalue(L, -1);
1219	lua_setfield(L, -2, "__index");
1220
1221	lua_pushcfunction (L, lua_ucl_parser_gc);
1222	lua_setfield (L, -2, "__gc");
1223
1224	lua_pushcfunction (L, lua_ucl_parser_parse_file);
1225	lua_setfield (L, -2, "parse_file");
1226
1227	lua_pushcfunction (L, lua_ucl_parser_parse_string);
1228	lua_setfield (L, -2, "parse_string");
1229
1230	lua_pushcfunction (L, lua_ucl_parser_parse_text);
1231	lua_setfield (L, -2, "parse_text");
1232
1233	lua_pushcfunction (L, lua_ucl_parser_register_variable);
1234	lua_setfield (L, -2, "register_variable");
1235
1236	lua_pushcfunction (L, lua_ucl_parser_register_variables);
1237	lua_setfield (L, -2, "register_variables");
1238
1239	lua_pushcfunction (L, lua_ucl_parser_get_object);
1240	lua_setfield (L, -2, "get_object");
1241
1242	lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped);
1243	lua_setfield (L, -2, "get_object_wrapped");
1244
1245	lua_pushcfunction (L, lua_ucl_parser_validate);
1246	lua_setfield (L, -2, "validate");
1247
1248	lua_pop (L, 1);
1249}
1250
1251static void
1252lua_ucl_object_mt (lua_State *L)
1253{
1254	luaL_newmetatable (L, OBJECT_META);
1255
1256	lua_pushvalue(L, -1);
1257	lua_setfield(L, -2, "__index");
1258
1259	lua_pushcfunction (L, lua_ucl_object_gc);
1260	lua_setfield (L, -2, "__gc");
1261
1262	lua_pushcfunction (L, lua_ucl_object_tostring);
1263	lua_setfield (L, -2, "__tostring");
1264
1265	lua_pushcfunction (L, lua_ucl_object_tostring);
1266	lua_setfield (L, -2, "tostring");
1267
1268	lua_pushcfunction (L, lua_ucl_object_unwrap);
1269	lua_setfield (L, -2, "unwrap");
1270
1271	lua_pushcfunction (L, lua_ucl_object_unwrap);
1272	lua_setfield (L, -2, "tolua");
1273
1274	lua_pushcfunction (L, lua_ucl_object_validate);
1275	lua_setfield (L, -2, "validate");
1276
1277	lua_pushstring (L, OBJECT_META);
1278	lua_setfield (L, -2, "class");
1279
1280	lua_pop (L, 1);
1281}
1282
1283static void
1284lua_ucl_types_mt (lua_State *L)
1285{
1286	luaL_newmetatable (L, UCL_OBJECT_TYPE_META);
1287
1288	lua_pushcfunction (L, lua_ucl_object_tostring);
1289	lua_setfield (L, -2, "__tostring");
1290
1291	lua_pushcfunction (L, lua_ucl_object_tostring);
1292	lua_setfield (L, -2, "tostring");
1293
1294	lua_pushstring (L, UCL_OBJECT_TYPE_META);
1295	lua_setfield (L, -2, "class");
1296
1297	lua_pop (L, 1);
1298
1299	luaL_newmetatable (L, UCL_ARRAY_TYPE_META);
1300
1301	lua_pushcfunction (L, lua_ucl_object_tostring);
1302	lua_setfield (L, -2, "__tostring");
1303
1304	lua_pushcfunction (L, lua_ucl_object_tostring);
1305	lua_setfield (L, -2, "tostring");
1306
1307	lua_pushstring (L, UCL_ARRAY_TYPE_META);
1308	lua_setfield (L, -2, "class");
1309
1310	lua_pop (L, 1);
1311
1312	luaL_newmetatable (L, UCL_IMPL_ARRAY_TYPE_META);
1313
1314	lua_pushcfunction (L, lua_ucl_object_tostring);
1315	lua_setfield (L, -2, "__tostring");
1316
1317	lua_pushcfunction (L, lua_ucl_object_tostring);
1318	lua_setfield (L, -2, "tostring");
1319
1320	lua_pushstring (L, UCL_IMPL_ARRAY_TYPE_META);
1321	lua_setfield (L, -2, "class");
1322
1323	lua_pop (L, 1);
1324}
1325
1326static int
1327lua_ucl_to_json (lua_State *L)
1328{
1329	ucl_object_t *obj;
1330	int format = UCL_EMIT_JSON;
1331
1332	if (lua_gettop (L) > 1) {
1333		if (lua_toboolean (L, 2)) {
1334			format = UCL_EMIT_JSON_COMPACT;
1335		}
1336	}
1337
1338	obj = ucl_object_lua_import (L, 1);
1339	if (obj != NULL) {
1340		lua_ucl_to_string (L, obj, format);
1341		ucl_object_unref (obj);
1342	}
1343	else {
1344		lua_pushnil (L);
1345	}
1346
1347	return 1;
1348}
1349
1350static int
1351lua_ucl_to_config (lua_State *L)
1352{
1353	ucl_object_t *obj;
1354
1355	obj = ucl_object_lua_import (L, 1);
1356	if (obj != NULL) {
1357		lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG);
1358		ucl_object_unref (obj);
1359	}
1360	else {
1361		lua_pushnil (L);
1362	}
1363
1364	return 1;
1365}
1366
1367/***
1368 * @function ucl.to_format(var, format)
1369 * Converts lua variable `var` to the specified `format`. Formats supported are:
1370 *
1371 * - `json` - fine printed json
1372 * - `json-compact` - compacted json
1373 * - `config` - fine printed configuration
1374 * - `ucl` - same as `config`
1375 * - `yaml` - embedded yaml
1376 *
1377 * If `var` contains function, they are called during output formatting and if
1378 * they return string value, then this value is used for output.
1379 * @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
1380 * @param {string} format any available format
1381 * @return {string} string representation of `var` in the specific `format`.
1382 * @example
1383local table = {
1384  str = 'value',
1385  num = 100500,
1386  null = ucl.null,
1387  func = function ()
1388    return 'huh'
1389  end
1390}
1391
1392print(ucl.to_format(table, 'ucl'))
1393-- Output:
1394--[[
1395num = 100500;
1396str = "value";
1397null = null;
1398func = "huh";
1399--]]
1400 */
1401static int
1402lua_ucl_to_format (lua_State *L)
1403{
1404	ucl_object_t *obj;
1405	int format = UCL_EMIT_JSON;
1406	bool sort = false;
1407
1408	if (lua_gettop (L) > 1) {
1409		if (lua_type (L, 2) == LUA_TNUMBER) {
1410			format = lua_tonumber (L, 2);
1411			if (format < 0 || format >= UCL_EMIT_YAML) {
1412				lua_pushnil (L);
1413				return 1;
1414			}
1415		}
1416		else if (lua_type (L, 2) == LUA_TSTRING) {
1417			const char *strtype = lua_tostring (L, 2);
1418
1419			if (strcasecmp (strtype, "json") == 0) {
1420				format = UCL_EMIT_JSON;
1421			}
1422			else if (strcasecmp (strtype, "json-compact") == 0) {
1423				format = UCL_EMIT_JSON_COMPACT;
1424			}
1425			else if (strcasecmp (strtype, "yaml") == 0) {
1426				format = UCL_EMIT_YAML;
1427			}
1428			else if (strcasecmp (strtype, "config") == 0 ||
1429				strcasecmp (strtype, "ucl") == 0) {
1430				format = UCL_EMIT_CONFIG;
1431			}
1432			else if (strcasecmp (strtype, "msgpack") == 0) {
1433				format = UCL_EMIT_MSGPACK;
1434			}
1435		}
1436
1437		if (lua_isboolean (L, 3)) {
1438			sort = lua_toboolean (L, 3);
1439		}
1440	}
1441
1442	obj = ucl_object_lua_import (L, 1);
1443
1444	if (obj != NULL) {
1445
1446		if (sort) {
1447			if (ucl_object_type (obj) == UCL_OBJECT) {
1448				ucl_object_sort_keys (obj, UCL_SORT_KEYS_RECURSIVE);
1449			}
1450		}
1451
1452		lua_ucl_to_string (L, obj, format);
1453		ucl_object_unref (obj);
1454	}
1455	else {
1456		lua_pushnil (L);
1457	}
1458
1459	return 1;
1460}
1461
1462static int
1463lua_ucl_null_tostring (lua_State* L)
1464{
1465	lua_pushstring (L, "null");
1466	return 1;
1467}
1468
1469static void
1470lua_ucl_null_mt (lua_State *L)
1471{
1472	luaL_newmetatable (L, NULL_META);
1473
1474	lua_pushcfunction (L, lua_ucl_null_tostring);
1475	lua_setfield (L, -2, "__tostring");
1476
1477	lua_pop (L, 1);
1478}
1479
1480int
1481luaopen_ucl (lua_State *L)
1482{
1483	lua_ucl_parser_mt (L);
1484	lua_ucl_null_mt (L);
1485	lua_ucl_object_mt (L);
1486	lua_ucl_types_mt (L);
1487
1488	/* Create the refs weak table: */
1489	lua_createtable (L, 0, 2);
1490	lua_pushliteral (L, "v"); /* tbl, "v" */
1491	lua_setfield (L, -2, "__mode");
1492	lua_pushvalue (L, -1); /* tbl, tbl */
1493	lua_setmetatable (L, -2); /* tbl */
1494	lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs");
1495
1496	lua_newtable (L);
1497
1498	lua_pushcfunction (L, lua_ucl_parser_init);
1499	lua_setfield (L, -2, "parser");
1500
1501	lua_pushcfunction (L, lua_ucl_to_json);
1502	lua_setfield (L, -2, "to_json");
1503
1504	lua_pushcfunction (L, lua_ucl_to_config);
1505	lua_setfield (L, -2, "to_config");
1506
1507	lua_pushcfunction (L, lua_ucl_to_format);
1508	lua_setfield (L, -2, "to_format");
1509
1510	ucl_null = lua_newuserdata (L, 0);
1511	luaL_getmetatable (L, NULL_META);
1512	lua_setmetatable (L, -2);
1513
1514	lua_pushvalue (L, -1);
1515	lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null");
1516
1517	lua_setfield (L, -2, "null");
1518
1519	return 1;
1520}
1521
1522struct ucl_lua_funcdata*
1523ucl_object_toclosure (const ucl_object_t *obj)
1524{
1525	if (obj == NULL || obj->type != UCL_USERDATA) {
1526		return NULL;
1527	}
1528
1529	return (struct ucl_lua_funcdata*)obj->value.ud;
1530}
1531