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