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_object_iterate (obj, &it, true) != NULL) { 153 nelt ++; 154 } 155 156 lua_createtable (L, 0, nelt); 157 it = NULL; 158 159 while ((cur = ucl_object_iterate (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 ucl_object_iterate_free (it); 191 } 192 else { 193 /* Optimize allocation by preallocation of table */ 194 LL_FOREACH (obj, cur) { 195 nelt ++; 196 } 197 198 lua_createtable (L, nelt, 0); 199 200 LL_FOREACH (obj, cur) { 201 ucl_object_push_lua (L, cur, false); 202 lua_rawseti (L, -2, i); 203 i ++; 204 } 205 } 206 207 return 1; 208 } 209 210 /** 211 * Push a simple object to lua depending on its actual type 212 */ 213 static int 214 ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, 215 bool allow_array) 216 { 217 struct ucl_lua_funcdata *fd; 218 219 if (allow_array && obj->next != NULL) { 220 /* Actually we need to push this as an array */ 221 return ucl_object_lua_push_array (L, obj); 222 } 223 224 switch (obj->type) { 225 case UCL_BOOLEAN: 226 lua_pushboolean (L, ucl_obj_toboolean (obj)); 227 break; 228 case UCL_STRING: 229 lua_pushstring (L, ucl_obj_tostring (obj)); 230 break; 231 case UCL_INT: 232 #if LUA_VERSION_NUM >= 501 233 lua_pushinteger (L, ucl_obj_toint (obj)); 234 #else 235 lua_pushnumber (L, ucl_obj_toint (obj)); 236 #endif 237 break; 238 case UCL_FLOAT: 239 case UCL_TIME: 240 lua_pushnumber (L, ucl_obj_todouble (obj)); 241 break; 242 case UCL_NULL: 243 lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null"); 244 break; 245 case UCL_USERDATA: 246 fd = (struct ucl_lua_funcdata *)obj->value.ud; 247 lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx); 248 break; 249 default: 250 lua_pushnil (L); 251 break; 252 } 253 254 return 1; 255 } 256 257 /*** 258 * @function ucl_object_push_lua(L, obj, allow_array) 259 * This is a `C` function to push `UCL` object as lua variable. This function 260 * converts `obj` to lua representation using the following conversions: 261 * 262 * - *scalar* values are directly presented by lua objects 263 * - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`, 264 * this can be used to pass functions from lua to c and vice-versa 265 * - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations 266 * - *objects* are converted to lua tables with string indicies 267 * @param {lua_State} L lua state pointer 268 * @param {ucl_object_t} obj object to push 269 * @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays) 270 * @return {int} `1` if an object is pushed to lua 271 */ 272 int 273 ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array) 274 { 275 switch (obj->type) { 276 case UCL_OBJECT: 277 return ucl_object_lua_push_object (L, obj, allow_array); 278 case UCL_ARRAY: 279 return ucl_object_lua_push_array (L, obj); 280 default: 281 return ucl_object_lua_push_scalar (L, obj, allow_array); 282 } 283 } 284 285 /** 286 * Parse lua table into object top 287 * @param L 288 * @param top 289 * @param idx 290 */ 291 static ucl_object_t * 292 ucl_object_lua_fromtable (lua_State *L, int idx) 293 { 294 ucl_object_t *obj, *top = NULL; 295 size_t keylen; 296 const char *k; 297 bool is_array = true; 298 int max = INT_MIN; 299 300 if (idx < 0) { 301 /* For negative indicies we want to invert them */ 302 idx = lua_gettop (L) + idx + 1; 303 } 304 /* Check for array */ 305 lua_pushnil (L); 306 while (lua_next (L, idx) != 0) { 307 if (lua_type (L, -2) == LUA_TNUMBER) { 308 double num = lua_tonumber (L, -2); 309 if (num == (int)num) { 310 if (num > max) { 311 max = num; 312 } 313 } 314 else { 315 /* Keys are not integer */ 316 lua_pop (L, 2); 317 is_array = false; 318 break; 319 } 320 } 321 else { 322 /* Keys are not numeric */ 323 lua_pop (L, 2); 324 is_array = false; 325 break; 326 } 327 lua_pop (L, 1); 328 } 329 330 /* Table iterate */ 331 if (is_array) { 332 int i; 333 334 top = ucl_object_typed_new (UCL_ARRAY); 335 for (i = 1; i <= max; i ++) { 336 lua_pushinteger (L, i); 337 lua_gettable (L, idx); 338 obj = ucl_object_lua_fromelt (L, lua_gettop (L)); 339 if (obj != NULL) { 340 ucl_array_append (top, obj); 341 } 342 lua_pop (L, 1); 343 } 344 } 345 else { 346 lua_pushnil (L); 347 top = ucl_object_typed_new (UCL_OBJECT); 348 while (lua_next (L, idx) != 0) { 349 /* copy key to avoid modifications */ 350 k = lua_tolstring (L, -2, &keylen); 351 obj = ucl_object_lua_fromelt (L, lua_gettop (L)); 352 353 if (obj != NULL) { 354 ucl_object_insert_key (top, obj, k, keylen, true); 355 } 356 lua_pop (L, 1); 357 } 358 } 359 360 return top; 361 } 362 363 /** 364 * Get a single element from lua to object obj 365 * @param L 366 * @param obj 367 * @param idx 368 */ 369 static ucl_object_t * 370 ucl_object_lua_fromelt (lua_State *L, int idx) 371 { 372 int type; 373 double num; 374 ucl_object_t *obj = NULL; 375 struct ucl_lua_funcdata *fd; 376 377 type = lua_type (L, idx); 378 379 switch (type) { 380 case LUA_TSTRING: 381 obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0); 382 break; 383 case LUA_TNUMBER: 384 num = lua_tonumber (L, idx); 385 if (num == (int64_t)num) { 386 obj = ucl_object_fromint (num); 387 } 388 else { 389 obj = ucl_object_fromdouble (num); 390 } 391 break; 392 case LUA_TBOOLEAN: 393 obj = ucl_object_frombool (lua_toboolean (L, idx)); 394 break; 395 case LUA_TUSERDATA: 396 if (lua_topointer (L, idx) == ucl_null) { 397 obj = ucl_object_typed_new (UCL_NULL); 398 } 399 break; 400 case LUA_TTABLE: 401 case LUA_TFUNCTION: 402 case LUA_TTHREAD: 403 if (luaL_getmetafield (L, idx, "__gen_ucl")) { 404 if (lua_isfunction (L, -1)) { 405 lua_settop (L, 3); /* gen, obj, func */ 406 lua_insert (L, 1); /* func, gen, obj */ 407 lua_insert (L, 2); /* func, obj, gen */ 408 lua_call(L, 2, 1); 409 obj = ucl_object_lua_fromelt (L, 1); 410 } 411 lua_pop (L, 2); 412 } 413 else { 414 if (type == LUA_TTABLE) { 415 obj = ucl_object_lua_fromtable (L, idx); 416 } 417 else if (type == LUA_TFUNCTION) { 418 fd = malloc (sizeof (*fd)); 419 if (fd != NULL) { 420 lua_pushvalue (L, idx); 421 fd->L = L; 422 fd->ret = NULL; 423 fd->idx = luaL_ref (L, LUA_REGISTRYINDEX); 424 425 obj = ucl_object_new_userdata (lua_ucl_userdata_dtor, 426 lua_ucl_userdata_emitter, (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 = UCL_PARSER_NO_FILEVARS; 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 static void 518 lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj) 519 { 520 ucl_object_t **pobj; 521 522 pobj = lua_newuserdata (L, sizeof (*pobj)); 523 *pobj = obj; 524 luaL_getmetatable (L, OBJECT_META); 525 lua_setmetatable (L, -2); 526 } 527 528 static inline enum ucl_parse_type 529 lua_ucl_str_to_parse_type (const char *str) 530 { 531 enum ucl_parse_type type = UCL_PARSE_UCL; 532 533 if (str != NULL) { 534 if (strcasecmp (str, "msgpack") == 0) { 535 type = UCL_PARSE_MSGPACK; 536 } 537 else if (strcasecmp (str, "sexp") == 0 || 538 strcasecmp (str, "csexp") == 0) { 539 type = UCL_PARSE_CSEXP; 540 } 541 else if (strcasecmp (str, "auto") == 0) { 542 type = UCL_PARSE_AUTO; 543 } 544 } 545 546 return type; 547 } 548 549 /*** 550 * @method parser:parse_file(name) 551 * Parse UCL object from file. 552 * @param {string} name filename to parse 553 * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned 554 @example 555 local parser = ucl.parser() 556 local res,err = parser:parse_file('/some/file.conf') 557 558 if not res then 559 print('parser error: ' .. err) 560 else 561 -- Do something with object 562 end 563 */ 564 static int 565 lua_ucl_parser_parse_file (lua_State *L) 566 { 567 struct ucl_parser *parser; 568 const char *file; 569 int ret = 2; 570 571 parser = lua_ucl_parser_get (L, 1); 572 file = luaL_checkstring (L, 2); 573 574 if (parser != NULL && file != NULL) { 575 if (ucl_parser_add_file (parser, file)) { 576 lua_pushboolean (L, true); 577 ret = 1; 578 } 579 else { 580 lua_pushboolean (L, false); 581 lua_pushstring (L, ucl_parser_get_error (parser)); 582 } 583 } 584 else { 585 lua_pushboolean (L, false); 586 lua_pushstring (L, "invalid arguments"); 587 } 588 589 return ret; 590 } 591 592 /*** 593 * @method parser:parse_string(input) 594 * Parse UCL object from file. 595 * @param {string} input string to parse 596 * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned 597 */ 598 static int 599 lua_ucl_parser_parse_string (lua_State *L) 600 { 601 struct ucl_parser *parser; 602 const char *string; 603 size_t llen; 604 enum ucl_parse_type type = UCL_PARSE_UCL; 605 int ret = 2; 606 607 parser = lua_ucl_parser_get (L, 1); 608 string = luaL_checklstring (L, 2, &llen); 609 610 if (lua_type (L, 3) == LUA_TSTRING) { 611 type = lua_ucl_str_to_parse_type (lua_tostring (L, 3)); 612 } 613 614 if (parser != NULL && string != NULL) { 615 if (ucl_parser_add_chunk_full (parser, (const unsigned char *)string, 616 llen, 0, UCL_DUPLICATE_APPEND, type)) { 617 lua_pushboolean (L, true); 618 ret = 1; 619 } 620 else { 621 lua_pushboolean (L, false); 622 lua_pushstring (L, ucl_parser_get_error (parser)); 623 } 624 } 625 else { 626 lua_pushboolean (L, false); 627 lua_pushstring (L, "invalid arguments"); 628 } 629 630 return ret; 631 } 632 633 /*** 634 * @method parser:get_object() 635 * Get top object from parser and export it to lua representation. 636 * @return {variant or nil} ucl object as lua native variable 637 */ 638 static int 639 lua_ucl_parser_get_object (lua_State *L) 640 { 641 struct ucl_parser *parser; 642 ucl_object_t *obj; 643 int ret = 1; 644 645 parser = lua_ucl_parser_get (L, 1); 646 obj = ucl_parser_get_object (parser); 647 648 if (obj != NULL) { 649 ret = ucl_object_push_lua (L, obj, false); 650 /* no need to keep reference */ 651 ucl_object_unref (obj); 652 } 653 else { 654 lua_pushnil (L); 655 } 656 657 return ret; 658 } 659 660 /*** 661 * @method parser:get_object_wrapped() 662 * Get top object from parser and export it to userdata object without 663 * unwrapping to lua. 664 * @return {ucl.object or nil} ucl object wrapped variable 665 */ 666 static int 667 lua_ucl_parser_get_object_wrapped (lua_State *L) 668 { 669 struct ucl_parser *parser; 670 ucl_object_t *obj; 671 int ret = 1; 672 673 parser = lua_ucl_parser_get (L, 1); 674 obj = ucl_parser_get_object (parser); 675 676 if (obj != NULL) { 677 lua_ucl_push_opaque (L, obj); 678 } 679 else { 680 lua_pushnil (L); 681 } 682 683 return ret; 684 } 685 686 /*** 687 * @method parser:validate(schema) 688 * Validates the top object in the parser against schema. Schema might be 689 * another object or a string that represents file to load schema from. 690 * 691 * @param {string/table} schema input schema 692 * @return {result,err} two values: boolean result and the corresponding error 693 * 694 */ 695 static int 696 lua_ucl_parser_validate (lua_State *L) 697 { 698 struct ucl_parser *parser, *schema_parser; 699 ucl_object_t *schema; 700 const char *schema_file; 701 struct ucl_schema_error err; 702 703 parser = lua_ucl_parser_get (L, 1); 704 705 if (parser && parser->top_obj) { 706 if (lua_type (L, 2) == LUA_TTABLE) { 707 schema = ucl_object_lua_import (L, 2); 708 709 if (schema == NULL) { 710 lua_pushboolean (L, false); 711 lua_pushstring (L, "cannot load schema from lua table"); 712 713 return 2; 714 } 715 } 716 else if (lua_type (L, 2) == LUA_TSTRING) { 717 schema_parser = ucl_parser_new (0); 718 schema_file = luaL_checkstring (L, 2); 719 720 if (!ucl_parser_add_file (schema_parser, schema_file)) { 721 lua_pushboolean (L, false); 722 lua_pushfstring (L, "cannot parse schema file \"%s\": " 723 "%s", schema_file, ucl_parser_get_error (parser)); 724 ucl_parser_free (schema_parser); 725 726 return 2; 727 } 728 729 schema = ucl_parser_get_object (schema_parser); 730 ucl_parser_free (schema_parser); 731 } 732 else { 733 lua_pushboolean (L, false); 734 lua_pushstring (L, "invalid schema argument"); 735 736 return 2; 737 } 738 739 if (!ucl_object_validate (schema, parser->top_obj, &err)) { 740 lua_pushboolean (L, false); 741 lua_pushfstring (L, "validation error: " 742 "%s", err.msg); 743 } 744 else { 745 lua_pushboolean (L, true); 746 lua_pushnil (L); 747 } 748 749 ucl_object_unref (schema); 750 } 751 else { 752 lua_pushboolean (L, false); 753 lua_pushstring (L, "invalid parser or empty top object"); 754 } 755 756 return 2; 757 } 758 759 static int 760 lua_ucl_parser_gc (lua_State *L) 761 { 762 struct ucl_parser *parser; 763 764 parser = lua_ucl_parser_get (L, 1); 765 ucl_parser_free (parser); 766 767 return 0; 768 } 769 770 /*** 771 * @method object:unwrap() 772 * Unwraps opaque ucl object to the native lua object (performing copying) 773 * @return {variant} any lua object 774 */ 775 static int 776 lua_ucl_object_unwrap (lua_State *L) 777 { 778 ucl_object_t *obj; 779 780 obj = lua_ucl_object_get (L, 1); 781 782 if (obj) { 783 ucl_object_push_lua (L, obj, true); 784 } 785 else { 786 lua_pushnil (L); 787 } 788 789 return 1; 790 } 791 792 static inline enum ucl_emitter 793 lua_ucl_str_to_emit_type (const char *strtype) 794 { 795 enum ucl_emitter format = UCL_EMIT_JSON_COMPACT; 796 797 if (strcasecmp (strtype, "json") == 0) { 798 format = UCL_EMIT_JSON; 799 } 800 else if (strcasecmp (strtype, "json-compact") == 0) { 801 format = UCL_EMIT_JSON_COMPACT; 802 } 803 else if (strcasecmp (strtype, "yaml") == 0) { 804 format = UCL_EMIT_YAML; 805 } 806 else if (strcasecmp (strtype, "config") == 0 || 807 strcasecmp (strtype, "ucl") == 0) { 808 format = UCL_EMIT_CONFIG; 809 } 810 811 return format; 812 } 813 814 /*** 815 * @method object:tostring(type) 816 * Unwraps opaque ucl object to string (json by default). Optionally you can 817 * specify output format: 818 * 819 * - `json` - fine printed json 820 * - `json-compact` - compacted json 821 * - `config` - fine printed configuration 822 * - `ucl` - same as `config` 823 * - `yaml` - embedded yaml 824 * @param {string} type optional 825 * @return {string} string representation of the opaque ucl object 826 */ 827 static int 828 lua_ucl_object_tostring (lua_State *L) 829 { 830 ucl_object_t *obj; 831 enum ucl_emitter format = UCL_EMIT_JSON_COMPACT; 832 833 obj = lua_ucl_object_get (L, 1); 834 835 if (obj) { 836 if (lua_gettop (L) > 1) { 837 if (lua_type (L, 2) == LUA_TSTRING) { 838 const char *strtype = lua_tostring (L, 2); 839 840 format = lua_ucl_str_to_emit_type (strtype); 841 } 842 } 843 844 return lua_ucl_to_string (L, obj, format); 845 } 846 else { 847 lua_pushnil (L); 848 } 849 850 return 1; 851 } 852 853 /*** 854 * @method object:validate(schema[, path[, ext_refs]]) 855 * Validates the given ucl object using schema object represented as another 856 * opaque ucl object. You can also specify path in the form `#/path/def` to 857 * specify the specific schema element to perform validation. 858 * 859 * @param {ucl.object} schema schema object 860 * @param {string} path optional path for validation procedure 861 * @return {result,err} two values: boolean result and the corresponding 862 * error, if `ext_refs` are also specified, then they are returned as opaque 863 * ucl object as {result,err,ext_refs} 864 */ 865 static int 866 lua_ucl_object_validate (lua_State *L) 867 { 868 ucl_object_t *obj, *schema, *ext_refs = NULL; 869 const ucl_object_t *schema_elt; 870 bool res = false; 871 struct ucl_schema_error err; 872 const char *path = NULL; 873 874 obj = lua_ucl_object_get (L, 1); 875 schema = lua_ucl_object_get (L, 2); 876 877 if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) { 878 if (lua_gettop (L) > 2) { 879 if (lua_type (L, 3) == LUA_TSTRING) { 880 path = lua_tostring (L, 3); 881 if (path[0] == '#') { 882 path++; 883 } 884 } 885 else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) == 886 LUA_TTABLE) { 887 /* External refs */ 888 ext_refs = lua_ucl_object_get (L, 3); 889 } 890 891 if (lua_gettop (L) > 3) { 892 if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) == 893 LUA_TTABLE) { 894 /* External refs */ 895 ext_refs = lua_ucl_object_get (L, 4); 896 } 897 } 898 } 899 900 if (path) { 901 schema_elt = ucl_object_lookup_path_char (schema, path, '/'); 902 } 903 else { 904 /* Use the top object */ 905 schema_elt = schema; 906 } 907 908 if (schema_elt) { 909 res = ucl_object_validate_root_ext (schema_elt, obj, schema, 910 ext_refs, &err); 911 912 if (res) { 913 lua_pushboolean (L, res); 914 lua_pushnil (L); 915 916 if (ext_refs) { 917 lua_ucl_push_opaque (L, ext_refs); 918 } 919 } 920 else { 921 lua_pushboolean (L, res); 922 lua_pushfstring (L, "validation error: %s", err.msg); 923 924 if (ext_refs) { 925 lua_ucl_push_opaque (L, ext_refs); 926 } 927 } 928 } 929 else { 930 lua_pushboolean (L, res); 931 932 lua_pushfstring (L, "cannot find the requested path: %s", path); 933 934 if (ext_refs) { 935 lua_ucl_push_opaque (L, ext_refs); 936 } 937 } 938 } 939 else { 940 lua_pushboolean (L, res); 941 lua_pushstring (L, "invalid object or schema"); 942 } 943 944 if (ext_refs) { 945 return 3; 946 } 947 948 return 2; 949 } 950 951 static int 952 lua_ucl_object_gc (lua_State *L) 953 { 954 ucl_object_t *obj; 955 956 obj = lua_ucl_object_get (L, 1); 957 958 ucl_object_unref (obj); 959 960 return 0; 961 } 962 963 static void 964 lua_ucl_parser_mt (lua_State *L) 965 { 966 luaL_newmetatable (L, PARSER_META); 967 968 lua_pushvalue(L, -1); 969 lua_setfield(L, -2, "__index"); 970 971 lua_pushcfunction (L, lua_ucl_parser_gc); 972 lua_setfield (L, -2, "__gc"); 973 974 lua_pushcfunction (L, lua_ucl_parser_parse_file); 975 lua_setfield (L, -2, "parse_file"); 976 977 lua_pushcfunction (L, lua_ucl_parser_parse_string); 978 lua_setfield (L, -2, "parse_string"); 979 980 lua_pushcfunction (L, lua_ucl_parser_get_object); 981 lua_setfield (L, -2, "get_object"); 982 983 lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped); 984 lua_setfield (L, -2, "get_object_wrapped"); 985 986 lua_pushcfunction (L, lua_ucl_parser_validate); 987 lua_setfield (L, -2, "validate"); 988 989 lua_pop (L, 1); 990 } 991 992 static void 993 lua_ucl_object_mt (lua_State *L) 994 { 995 luaL_newmetatable (L, OBJECT_META); 996 997 lua_pushvalue(L, -1); 998 lua_setfield(L, -2, "__index"); 999 1000 lua_pushcfunction (L, lua_ucl_object_gc); 1001 lua_setfield (L, -2, "__gc"); 1002 1003 lua_pushcfunction (L, lua_ucl_object_tostring); 1004 lua_setfield (L, -2, "__tostring"); 1005 1006 lua_pushcfunction (L, lua_ucl_object_tostring); 1007 lua_setfield (L, -2, "tostring"); 1008 1009 lua_pushcfunction (L, lua_ucl_object_unwrap); 1010 lua_setfield (L, -2, "unwrap"); 1011 1012 lua_pushcfunction (L, lua_ucl_object_unwrap); 1013 lua_setfield (L, -2, "tolua"); 1014 1015 lua_pushcfunction (L, lua_ucl_object_validate); 1016 lua_setfield (L, -2, "validate"); 1017 1018 lua_pushstring (L, OBJECT_META); 1019 lua_setfield (L, -2, "class"); 1020 1021 lua_pop (L, 1); 1022 } 1023 1024 static int 1025 lua_ucl_to_json (lua_State *L) 1026 { 1027 ucl_object_t *obj; 1028 int format = UCL_EMIT_JSON; 1029 1030 if (lua_gettop (L) > 1) { 1031 if (lua_toboolean (L, 2)) { 1032 format = UCL_EMIT_JSON_COMPACT; 1033 } 1034 } 1035 1036 obj = ucl_object_lua_import (L, 1); 1037 if (obj != NULL) { 1038 lua_ucl_to_string (L, obj, format); 1039 ucl_object_unref (obj); 1040 } 1041 else { 1042 lua_pushnil (L); 1043 } 1044 1045 return 1; 1046 } 1047 1048 static int 1049 lua_ucl_to_config (lua_State *L) 1050 { 1051 ucl_object_t *obj; 1052 1053 obj = ucl_object_lua_import (L, 1); 1054 if (obj != NULL) { 1055 lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG); 1056 ucl_object_unref (obj); 1057 } 1058 else { 1059 lua_pushnil (L); 1060 } 1061 1062 return 1; 1063 } 1064 1065 /*** 1066 * @function ucl.to_format(var, format) 1067 * Converts lua variable `var` to the specified `format`. Formats supported are: 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 * 1075 * If `var` contains function, they are called during output formatting and if 1076 * they return string value, then this value is used for ouptut. 1077 * @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output) 1078 * @param {string} format any available format 1079 * @return {string} string representation of `var` in the specific `format`. 1080 * @example 1081 local table = { 1082 str = 'value', 1083 num = 100500, 1084 null = ucl.null, 1085 func = function () 1086 return 'huh' 1087 end 1088 } 1089 1090 print(ucl.to_format(table, 'ucl')) 1091 -- Output: 1092 --[[ 1093 num = 100500; 1094 str = "value"; 1095 null = null; 1096 func = "huh"; 1097 --]] 1098 */ 1099 static int 1100 lua_ucl_to_format (lua_State *L) 1101 { 1102 ucl_object_t *obj; 1103 int format = UCL_EMIT_JSON; 1104 1105 if (lua_gettop (L) > 1) { 1106 if (lua_type (L, 2) == LUA_TNUMBER) { 1107 format = lua_tonumber (L, 2); 1108 if (format < 0 || format >= UCL_EMIT_YAML) { 1109 lua_pushnil (L); 1110 return 1; 1111 } 1112 } 1113 else if (lua_type (L, 2) == LUA_TSTRING) { 1114 const char *strtype = lua_tostring (L, 2); 1115 1116 if (strcasecmp (strtype, "json") == 0) { 1117 format = UCL_EMIT_JSON; 1118 } 1119 else if (strcasecmp (strtype, "json-compact") == 0) { 1120 format = UCL_EMIT_JSON_COMPACT; 1121 } 1122 else if (strcasecmp (strtype, "yaml") == 0) { 1123 format = UCL_EMIT_YAML; 1124 } 1125 else if (strcasecmp (strtype, "config") == 0 || 1126 strcasecmp (strtype, "ucl") == 0) { 1127 format = UCL_EMIT_CONFIG; 1128 } 1129 else if (strcasecmp (strtype, "msgpack") == 0) { 1130 format = UCL_EMIT_MSGPACK; 1131 } 1132 } 1133 } 1134 1135 obj = ucl_object_lua_import (L, 1); 1136 if (obj != NULL) { 1137 lua_ucl_to_string (L, obj, format); 1138 ucl_object_unref (obj); 1139 } 1140 else { 1141 lua_pushnil (L); 1142 } 1143 1144 return 1; 1145 } 1146 1147 static int 1148 lua_ucl_null_tostring (lua_State* L) 1149 { 1150 lua_pushstring (L, "null"); 1151 return 1; 1152 } 1153 1154 static void 1155 lua_ucl_null_mt (lua_State *L) 1156 { 1157 luaL_newmetatable (L, NULL_META); 1158 1159 lua_pushcfunction (L, lua_ucl_null_tostring); 1160 lua_setfield (L, -2, "__tostring"); 1161 1162 lua_pop (L, 1); 1163 } 1164 1165 int 1166 luaopen_ucl (lua_State *L) 1167 { 1168 lua_ucl_parser_mt (L); 1169 lua_ucl_null_mt (L); 1170 lua_ucl_object_mt (L); 1171 1172 /* Create the refs weak table: */ 1173 lua_createtable (L, 0, 2); 1174 lua_pushliteral (L, "v"); /* tbl, "v" */ 1175 lua_setfield (L, -2, "__mode"); 1176 lua_pushvalue (L, -1); /* tbl, tbl */ 1177 lua_setmetatable (L, -2); /* tbl */ 1178 lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs"); 1179 1180 lua_newtable (L); 1181 1182 lua_pushcfunction (L, lua_ucl_parser_init); 1183 lua_setfield (L, -2, "parser"); 1184 1185 lua_pushcfunction (L, lua_ucl_to_json); 1186 lua_setfield (L, -2, "to_json"); 1187 1188 lua_pushcfunction (L, lua_ucl_to_config); 1189 lua_setfield (L, -2, "to_config"); 1190 1191 lua_pushcfunction (L, lua_ucl_to_format); 1192 lua_setfield (L, -2, "to_format"); 1193 1194 ucl_null = lua_newuserdata (L, 0); 1195 luaL_getmetatable (L, NULL_META); 1196 lua_setmetatable (L, -2); 1197 1198 lua_pushvalue (L, -1); 1199 lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null"); 1200 1201 lua_setfield (L, -2, "null"); 1202 1203 return 1; 1204 } 1205 1206 struct ucl_lua_funcdata* 1207 ucl_object_toclosure (const ucl_object_t *obj) 1208 { 1209 if (obj == NULL || obj->type != UCL_USERDATA) { 1210 return NULL; 1211 } 1212 1213 return (struct ucl_lua_funcdata*)obj->value.ud; 1214 } 1215