You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

256 lines
5.5 KiB

#include <lighttpd/value_lua.h>
#include <lighttpd/condition_lua.h>
#include <lighttpd/actions_lua.h>
#include <lighttpd/core_lua.h>
#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 HEDLEY_NEVER_INLINE void init_kvlist_mt(lua_State *L) {
lua_pushcclosure(L, lua_kvlist_index, 0);
lua_setfield(L, -2, "__index");
}
static void lua_push_kvlist_metatable(lua_State *L) {
if (li_lua_new_protected_metatable(L, LUA_KVLIST_VALUE)) {
init_kvlist_mt(L);
}
}
void li_lua_init_value_mt(lua_State *L) {
lua_push_kvlist_metatable(L);
lua_pop(L, 1);
}
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, "Unexpected 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;
}