lua-bindings: allow arbitrary (json-mappable) plugin arguments.
authorKrisztian Litkey <kli@iki.fi>
Wed, 27 Feb 2013 22:13:58 +0000 (00:13 +0200)
committerKrisztian Litkey <kli@iki.fi>
Wed, 27 Feb 2013 22:21:43 +0000 (00:21 +0200)
src/core/lua-bindings/lua-plugin.c

index 58d55aa..4ea9f7a 100644 (file)
@@ -27,6 +27,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <errno.h>
+
 #include <lualib.h>
 #include <lauxlib.h>
 
@@ -36,6 +38,9 @@
 #include <murphy/core/lua-bindings/murphy.h>
 
 
+static int stringify_table(lua_State *L, int index,
+                           char **bufp, int *sizep, int *offsp);
+
 static int plugin_exists(lua_State *L)
 {
     mrp_context_t *ctx;
@@ -54,6 +59,201 @@ static int plugin_exists(lua_State *L)
 }
 
 
+static int ensure_buffer(char **bufp, int *sizep, int *offsp, int len)
+{
+    int spc, size, diff;
+
+    spc = *sizep - *offsp;
+    if (spc < len) {
+        diff = len - spc;
+
+        if (diff > *sizep)
+            size = *sizep + diff;
+        else
+            size = *sizep * 2;
+
+        if (!mrp_realloc(*bufp, size))
+            return -1;
+        else
+            *sizep = size;
+    }
+
+    return 0;
+}
+
+
+static int push_buffer(char **bufp, int *sizep, int *offsp,
+                       const char *str, int len)
+{
+    if (len <= 0)
+        len = strlen(str);
+
+    if (ensure_buffer(bufp, sizep, offsp, len + 1) == 0) {
+        strcpy(*bufp + *offsp, str);
+        *offsp += len;
+
+        return 0;
+    }
+    else
+        return -1;
+}
+
+
+static int stringify_string(lua_State *L, int index,
+                            char **bufp, int *sizep, int *offsp)
+{
+    const char *str;
+    size_t      size;
+    int         len;
+
+    str = lua_tolstring(L, index, &size);
+    len = (int)size;
+
+    if (ensure_buffer(bufp, sizep, offsp, len + 3) < 0)
+        return -1;
+
+    snprintf(*bufp + *offsp, len + 3, "'%s'", str);
+    *offsp += len + 2;
+
+    return 0;
+}
+
+
+static int stringify_number(lua_State *L, int index,
+                                char **bufp, int *sizep, int *offsp)
+{
+    char   num[64];
+    double d;
+    int    i, len;
+
+    if ((d = lua_tonumber(L, index)) == 1.0 * (i = lua_tointeger(L, index)))
+        len = snprintf(num, sizeof(num), "%d", i);
+    else
+        len = snprintf(num, sizeof(num), "%f", d);
+
+    return push_buffer(bufp, sizep, offsp, num, len);
+}
+
+
+static int stringify_boolean(lua_State *L, int index,
+                                 char **bufp, int *sizep, int *offsp)
+{
+    const char *bln;
+    int         len;
+
+    if (lua_toboolean(L, index)) {
+        bln = "true";
+        len = 4;
+    }
+    else {
+        bln = "false";
+        len = 5;
+    }
+
+    return push_buffer(bufp, sizep, offsp, bln, len);
+}
+
+
+static int stringify_object(lua_State *L, int index,
+                            char **bufp, int *sizep, int *offsp)
+{
+    switch (lua_type(L, index)) {
+    case LUA_TSTRING:  return stringify_string(L, index, bufp, sizep, offsp);
+    case LUA_TNUMBER:  return stringify_number(L, index, bufp, sizep, offsp);
+    case LUA_TBOOLEAN: return stringify_boolean(L, index, bufp, sizep, offsp);
+    case LUA_TTABLE:   return stringify_table(L, index, bufp, sizep, offsp);
+    default:
+        errno = EINVAL;
+    }
+
+    return -1;
+}
+
+
+static int stringify_table(lua_State *L, int index,
+                           char **bufp, int *sizep, int *offsp)
+{
+    const char *sep, *p;
+    char        key[256];
+    int         arr;
+    int         i, d, idx, len;
+
+    arr = TRUE;
+
+    lua_pushnil(L);
+
+    /*
+     * check what the table should be converted to (array or dictionary)
+     */
+    idx = -1;
+    while (arr && lua_next(L, index - 1)) {
+        switch (lua_type(L, -2)) {
+        case LUA_TBOOLEAN:
+        case LUA_TTABLE:
+        default:
+        invalid:
+            lua_pop(L, 3);
+            return -1;
+
+        case LUA_TNUMBER:
+            d = lua_tonumber(L, -2);
+            i = lua_tointeger(L, -2);
+
+            if (d != 1.0 * i || (idx >= 0 && i != idx + 1))
+                goto invalid;
+            else
+                idx = i;
+            break;
+
+        case LUA_TSTRING:
+            lua_pop(L, 1);
+            arr = FALSE;
+            break;
+        }
+
+        lua_pop(L, 1);
+    }
+
+
+    /*
+     * convert either to an array or a dictionary
+     */
+
+    if (push_buffer(bufp, sizep, offsp, arr ? "[" : "{", 1))
+        return -1;
+
+    sep = "";
+    lua_pushnil(L);
+    while (lua_next(L, index - 1)) {
+        if (!arr) {
+            len = snprintf(key, sizeof(key), "%s'%s':", sep,
+                           lua_tostring(L, -2));
+            p = key;
+        }
+        else {
+            p   = (char *)sep;
+            len = strlen(sep);
+        }
+
+        if (push_buffer(bufp, sizep, offsp, p, len) < 0)
+            return -1;
+
+        if (stringify_object(L, -1, bufp, sizep, offsp) < 0) {
+            lua_pop(L, 3);
+            return -1;
+        }
+
+        lua_pop(L, 1);
+        sep = ",";
+    }
+
+    if (push_buffer(bufp, sizep, offsp, arr ? "]" : "}", 1) < 0)
+        return -1;
+    else
+        return 0;
+}
+
+
 static int load(lua_State *L, int may_fail)
 {
     mrp_context_t    *ctx;
@@ -62,6 +262,8 @@ static int load(lua_State *L, int may_fail)
     const char       *instance, *argerr;
     mrp_plugin_arg_t  args[256];
     int               narg, n, type, t, success;
+    char             *json;
+    int               size, offs;
 
     ctx = mrp_lua_check_murphy_context(L, 1);
     n   = lua_gettop(L);
@@ -130,6 +332,17 @@ static int load(lua_State *L, int may_fail)
                 args[narg].str = mrp_strdup(lua_toboolean(L, -1) ?
                                             "true" : "false");
                 break;
+            case LUA_TTABLE:
+                json = NULL;
+                size = 0;
+                offs = 0;
+                if (stringify_table(L, -1, &json, &size, &offs) == 0)
+                    args[narg].str = json;
+                else {
+                    argerr = "failed to json-stringify Lua table";
+                    goto arg_error;
+                }
+                break;
             default:
                 argerr = "invalid argument table value";
                 goto arg_error;