diff options
Diffstat (limited to 'src/lib-lua/dlua-table.c')
-rw-r--r-- | src/lib-lua/dlua-table.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/lib-lua/dlua-table.c b/src/lib-lua/dlua-table.c new file mode 100644 index 0000000..8467a7e --- /dev/null +++ b/src/lib-lua/dlua-table.c @@ -0,0 +1,301 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "dlua-script-private.h" + +/* + * Adjust the index by the specified delta. + * + * In a couple of places we need to adjust the passed in index to reflect + * additional items pushed onto the stack. We cannot blindly adjust the + * index because the index could be one of three things and only one of them + * is supposed to be ajusted: + * + * 1. negative number: index relative to top of stack, adjust + * 2. positive number: absolute index, don't adjust + * 3. special registry index: don't adjust + */ +static inline int adj(int idx, int delta) +{ + if ((idx == LUA_REGISTRYINDEX) || (idx > 0)) + return idx; + else + return idx - delta; +} + +/* + * Pushes onto the stack the value t[k], where t is the value at the given + * index and k is the value at the top of the stack. Unlike lua_gettable(), + * this function checks the type of the retreived value against the passed + * in type. [-1,+0..1,e] + * + * Return value: + * -1 = incompatible type + * 0 = nil or none + * 1 = found + */ +static int dlua_table_get(lua_State *L, int idx, int type) +{ + /* can only work with tables */ + if (!lua_istable(L, idx)) + return -1; + + lua_gettable(L, idx); + + /* check if the field was there */ + if (lua_isnoneornil(L, -1)) { + lua_pop(L, 1); + return 0; + } + + /* check that the field is the expected type */ + if (lua_type(L, -1) != type) { + lua_pop(L, 1); + return -1; + } + + return 1; +} + +/* Get by string name [-0,+1,e] */ +int dlua_table_get_by_str(lua_State *L, int idx, int type, const char *field) +{ + /* push the key */ + lua_pushstring(L, field); + + return dlua_table_get(L, adj(idx, 1), type); +} + +/* Get by int name [-0,+1,e] */ +int dlua_table_get_by_int(lua_State *L, int idx, int type, lua_Integer field) +{ + /* push the key */ + lua_pushinteger(L, field); + + return dlua_table_get(L, adj(idx, 1), type); +} + +/* Get by thread [-0,+1,e] */ +int dlua_table_get_by_thread(lua_State *L, int idx, int type) +{ + /* push the key */ + lua_pushthread(L); + + return dlua_table_get(L, adj(idx, 1), type); +} + +/* generate a set of functions to access fields of an integral data type */ +#define GET_INTTYPE(fxn, ctype, minval, maxval, unsigned_check) \ +int fxn##_by_str(lua_State *L, int idx, const char *field, \ + ctype *value_r) \ +{ \ + lua_Integer tmp; \ + int ret; \ + \ + ret = dlua_table_get_luainteger_by_str(L, idx, field, &tmp); \ + if (ret < 1) \ + return ret; \ + \ + if (unsigned_check) { \ + if ((tmp < 0) || (((uintmax_t) tmp) > (maxval))) \ + return -1; \ + } else { \ + if ((tmp < (minval)) || (tmp > (intmax_t) (maxval))) \ + return -1; \ + } \ + \ + *value_r = (ctype) tmp; \ + \ + return 1; \ +} \ +int fxn##_by_int(lua_State *L, int idx, lua_Integer field, \ + ctype *value_r) \ +{ \ + lua_Integer tmp; \ + int ret; \ + \ + ret = dlua_table_get_luainteger_by_int(L, idx, field, &tmp); \ + if (ret < 1) \ + return ret; \ + \ + if (unsigned_check) { \ + if ((tmp < 0) || (((uintmax_t) tmp) > (maxval))) \ + return -1; \ + } else { \ + if ((tmp < (minval)) || (tmp > (intmax_t) (maxval))) \ + return -1; \ + } \ + \ + *value_r = (ctype) tmp; \ + \ + return 1; \ +} \ +int fxn##_by_thread(lua_State *L, int idx, ctype *value_r) \ +{ \ + lua_Integer tmp; \ + int ret; \ + \ + ret = dlua_table_get_luainteger_by_thread(L, idx, &tmp); \ + if (ret < 1) \ + return ret; \ + \ + if (unsigned_check) { \ + if ((tmp < 0) || (((uintmax_t) tmp) > (maxval))) \ + return -1; \ + } else { \ + if ((tmp < (minval)) || (tmp > (intmax_t) (maxval))) \ + return -1; \ + } \ + \ + *value_r = (ctype) tmp; \ + \ + return 1; \ +} + +/* generate a set of functions to access fields of a binary data type */ +#define GET_DATAPTR(fxn) \ +int fxn##_by_str(lua_State *L, int idx, const char *field, \ + const unsigned char **value_r, size_t *len_r) \ +{ \ + int ret; \ + \ + ret = dlua_table_get_by_str(L, idx, LUA_TSTRING, field); \ + if (ret < 1) \ + return ret; \ + \ + *value_r = (const unsigned char *) lua_tolstring(L, -1, len_r); \ + lua_pop(L, 1); \ + \ + return 1; \ +} \ +int fxn##_by_int(lua_State *L, int idx, lua_Integer field, \ + const unsigned char **value_r, size_t *len_r) \ +{ \ + int ret; \ + \ + ret = dlua_table_get_by_int(L, idx, LUA_TSTRING, field); \ + if (ret < 1) \ + return ret; \ + \ + *value_r = (const unsigned char *) lua_tolstring(L, -1, len_r); \ + lua_pop(L, 1); \ + \ + return 1; \ +} \ +int fxn##_by_thread(lua_State *L, int idx, \ + const unsigned char **value_r, size_t *len_r) \ +{ \ + int ret; \ + \ + ret = dlua_table_get_by_thread(L, idx, LUA_TSTRING); \ + if (ret < 1) \ + return ret; \ + \ + *value_r = (const unsigned char *) lua_tolstring(L, -1, len_r); \ + lua_pop(L, 1); \ + \ + return 1; \ +} + +/* generate a set of functions to access fields of a generic-ish type */ +#define GET_GENERIC(fxn, ctype, ltype, cvt) \ +int fxn##_by_str(lua_State *L, int idx, const char *field, ctype *value_r)\ +{ \ + int ret; \ + \ + ret = dlua_table_get_by_str(L, idx, (ltype), field); \ + if (ret < 1) \ + return ret; \ + \ + *value_r = cvt(L, -1); \ + lua_pop(L, 1); \ + \ + return 1; \ +} \ +int fxn##_by_int(lua_State *L, int idx, lua_Integer field, ctype *value_r)\ +{ \ + int ret; \ + \ + ret = dlua_table_get_by_int(L, idx, (ltype), field); \ + if (ret < 1) \ + return ret; \ + \ + *value_r = cvt(L, -1); \ + lua_pop(L, 1); \ + \ + return 1; \ +} \ +int fxn##_by_thread(lua_State *L, int idx, ctype *value_r) \ +{ \ + int ret; \ + \ + ret = dlua_table_get_by_thread(L, idx, (ltype)); \ + if (ret < 1) \ + return ret; \ + \ + *value_r = cvt(L, -1); \ + lua_pop(L, 1); \ + \ + return 1; \ +} + +GET_INTTYPE(dlua_table_get_int, int, INT_MIN, INT_MAX, FALSE); +GET_INTTYPE(dlua_table_get_intmax, intmax_t, INTMAX_MIN, INTMAX_MAX, FALSE); +GET_INTTYPE(dlua_table_get_uint, unsigned int, 0, UINT_MAX, TRUE); +GET_INTTYPE(dlua_table_get_uintmax, uintmax_t, 0, UINTMAX_MAX, TRUE); + +/* we need to use lua_tointegerx which takes an extra argument */ +int dlua_table_get_luainteger_by_str(lua_State *L, int idx, const char *field, + lua_Integer *value_r) +{ + int isnum; + int ret; + + ret = dlua_table_get_by_str(L, idx, LUA_TNUMBER, field); + if (ret < 1) + return ret; + + *value_r = lua_tointegerx(L, -1, &isnum); + lua_pop(L, 1); + + return (isnum == 1) ? 1 : -1; +} + +/* we need to use lua_tointegerx which takes an extra argument */ +int dlua_table_get_luainteger_by_int(lua_State *L, int idx, lua_Integer field, + lua_Integer *value_r) +{ + int isnum; + int ret; + + ret = dlua_table_get_by_int(L, idx, LUA_TNUMBER, field); + if (ret < 1) + return ret; + + *value_r = lua_tointegerx(L, -1, &isnum); + lua_pop(L, 1); + + return (isnum == 1) ? 1 : -1; +} + +/* we need to use lua_tointegerx which takes an extra argument */ +int dlua_table_get_luainteger_by_thread(lua_State *L, int idx, + lua_Integer *value_r) +{ + int isnum; + int ret; + + ret = dlua_table_get_by_thread(L, idx, LUA_TNUMBER); + if (ret < 1) + return ret; + + *value_r = lua_tointegerx(L, -1, &isnum); + lua_pop(L, 1); + + return (isnum == 1) ? 1 : -1; +} + +GET_GENERIC(dlua_table_get_number, lua_Number, LUA_TNUMBER, lua_tonumber); +GET_GENERIC(dlua_table_get_bool, bool, LUA_TBOOLEAN, lua_toboolean); +GET_GENERIC(dlua_table_get_string, const char *, LUA_TSTRING, lua_tostring); +GET_DATAPTR(dlua_table_get_data); |