#include #include #include #include #include #define LUA_KVLIST_VALUE "li KeyValue list (string, liValue*)" static int lua_kvlist_index(lua_State *L) { guint len, i; gboolean nil_key; switch (lua_type(L, 2)) { case LUA_TNUMBER: lua_rawget(L, 1); return 1; case LUA_TSTRING: nil_key = FALSE; break; case LUA_TNIL: nil_key = TRUE; break; default: goto fail; } if (LUA_TTABLE != lua_type(L, 1)) goto fail; len = lua_objlen(L, 1); for (i = len; i >= 1; lua_pop(L, 1), --i) { lua_rawgeti(L, 1, i); if (LUA_TTABLE != lua_type(L, -1)) continue; if (2 != lua_objlen(L, -1)) continue; lua_rawgeti(L, -1, 1); switch (lua_type(L, -1)) { case LUA_TSTRING: if (nil_key) break; if (!lua_equal(L, -1, 2)) break; lua_rawgeti(L, -2, 2); return 1; case LUA_TNIL: if (!nil_key) break; lua_rawgeti(L, -2, 2); return 1; default: break; } lua_pop(L, 1); } fail: lua_pushnil(L); return 1; } static void lua_push_kvlist_metatable(lua_State *L) { if (luaL_newmetatable(L, LUA_KVLIST_VALUE)) { lua_pushcclosure(L, lua_kvlist_index, 0); lua_setfield(L, -2, "__index"); } } static liValue* li_value_from_lua_table(liServer *srv, lua_State *L, int ndx) { liValue *val, *entry; gboolean is_list = FALSE, is_hash = FALSE; int ikey; liValue *kv_key, *kv_pair; val = li_value_new_list(); ndx = li_lua_fixindex(L, ndx); lua_pushnil(L); while (lua_next(L, ndx) != 0) { switch (lua_type(L, -2)) { case LUA_TNUMBER: if (is_hash) goto mixerror; is_list = TRUE; ikey = lua_tointeger(L, -2) - 1; if (ikey < 0) { ERROR(srv, "Invalid key < 0: %i - skipping entry", ikey + 1); lua_pop(L, 1); continue; } entry = li_value_from_lua(srv, L); if (NULL == entry) continue; li_value_list_set(val, ikey, entry); break; case LUA_TSTRING: if (is_list) goto mixerror; is_hash = TRUE; kv_key = li_value_new_string(li_lua_togstring(L, -2)); entry = li_value_from_lua(srv, L); if (NULL == entry) { li_value_free(kv_key); continue; } kv_pair = li_value_new_list(); li_value_list_append(kv_pair, kv_key); li_value_list_append(kv_pair, entry); li_value_list_append(val, kv_pair); break; default: ERROR(srv, "Unexpted key type in table: %s (%i) - skipping entry", lua_typename(L, lua_type(L, -1)), lua_type(L, -1)); lua_pop(L, 1); break; } } return val; mixerror: ERROR(srv, "%s", "Cannot mix list with hash; skipping remaining part of table"); lua_pop(L, 2); return val; } liValue* li_value_from_lua(liServer *srv, lua_State *L) { liValue *val; switch (lua_type(L, -1)) { case LUA_TNIL: lua_pop(L, 1); return NULL; case LUA_TBOOLEAN: val = li_value_new_bool(lua_toboolean(L, -1)); lua_pop(L, 1); return val; case LUA_TNUMBER: val = li_value_new_number(lua_tonumber(L, -1)); lua_pop(L, 1); return val; case LUA_TSTRING: val = li_value_new_string(li_lua_togstring(L, -1)); lua_pop(L, 1); return val; case LUA_TTABLE: val = li_value_from_lua_table(srv, L, -1); lua_pop(L, 1); return val; case LUA_TUSERDATA: { /* check for action */ liAction *a = li_lua_get_action(L, -1); if (a) { li_action_acquire(a); lua_pop(L, 1); return li_value_new_action(srv, a); } } { /* check for condition */ liCondition *c = li_lua_get_condition(L, -1); if (c) { li_condition_acquire(c); lua_pop(L, 1); return li_value_new_condition(srv, c); } } ERROR(srv, "%s", "Unknown lua userdata"); lua_pop(L, 1); return NULL; case LUA_TFUNCTION: { liAction *a = li_lua_make_action(L, -1); lua_pop(L, 1); return li_value_new_action(srv, a); } case LUA_TLIGHTUSERDATA: case LUA_TTHREAD: case LUA_TNONE: default: ERROR(srv, "Unexpected lua type: %s (%i)", lua_typename(L, lua_type(L, -1)), lua_type(L, -1)); lua_pop(L, 1); return NULL; } } GString* li_lua_togstring(lua_State *L, int ndx) { const char *buf; size_t len = 0; GString *str = NULL; if (lua_type(L, ndx) == LUA_TSTRING) { buf = lua_tolstring(L, ndx, &len); if (buf) str = g_string_new_len(buf, len); } else { lua_pushvalue(L, ndx); buf = lua_tolstring(L, -1, &len); if (buf) str = g_string_new_len(buf, len); lua_pop(L, 1); } return str; } int li_lua_push_value(lua_State *L, liValue *value) { if (NULL == value) { lua_pushnil(L); return 1; } switch (value->type) { case LI_VALUE_NONE: lua_pushnil(L); break; case LI_VALUE_BOOLEAN: lua_pushboolean(L, value->data.boolean); break; case LI_VALUE_NUMBER: lua_pushinteger(L, value->data.number); break; case LI_VALUE_STRING: lua_pushlstring(L, GSTR_LEN(value->data.string)); break; case LI_VALUE_LIST: { lua_newtable(L); LI_VALUE_FOREACH(entry, value) li_lua_push_value(L, entry); lua_rawseti(L, -2, _entry_i + 1); LI_VALUE_END_FOREACH() /* kvlist lookup for string/nil keys */ lua_push_kvlist_metatable(L); lua_setmetatable(L, -2); } break; case LI_VALUE_ACTION: li_action_acquire(value->data.val_action.action); li_lua_push_action(value->data.val_action.srv, L, value->data.val_action.action); break; case LI_VALUE_CONDITION: li_condition_acquire(value->data.val_cond.cond); li_lua_push_condition(value->data.val_cond.srv, L, value->data.val_cond.cond); break; default: /* ignore error and push nil */ lua_pushnil(L); break; } return 1; }