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