/* vifm * Copyright (C) 2020 xaizek. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "common.h" #include "../compat/reallocarray.h" #include "../engine/options.h" #include "../engine/text_buffer.h" #include "../ui/ui.h" #include "../utils/macros.h" #include "../utils/utils.h" #include "lua/lauxlib.h" #include "lua/lua.h" static int deduplicate_ints(int array[], int count); static int int_sorter(const void *first, const void *second); int vlua_cmn_check_opt_arg(lua_State *lua, int arg_idx, int expected_type) { int type = lua_type(lua, arg_idx); if(type == LUA_TNONE) { return 0; } if(lua_type(lua, arg_idx) != expected_type) { luaL_error(lua, "Parameter #%d value must be a %s", arg_idx, lua_typename(lua, expected_type)); } return 1; } void vlua_cmn_check_field(lua_State *lua, int table_idx, const char name[], int lua_type) { int type = lua_getfield(lua, table_idx, name); if(type == LUA_TNIL) { luaL_error(lua, "`%s` key is mandatory", name); } if(type != lua_type) { luaL_error(lua, "`%s` value must be a %s", name, lua_typename(lua, lua_type)); } } int vlua_cmn_check_opt_field(lua_State *lua, int table_idx, const char name[], int lua_type) { int type = lua_getfield(lua, table_idx, name); if(type == LUA_TNIL) { lua_pop(lua, 1); return 0; } if(type != lua_type) { return luaL_error(lua, "`%s` value must be a %s", name, lua_typename(lua, lua_type)); } return 1; } void * vlua_cmn_check_this(lua_State *lua, int idx) { void *udata = lua_touserdata(lua, idx); if(udata == NULL) { const char *type = lua_typename(lua, lua_type(lua, idx)); luaL_error(lua, "Value at %d must be user data, got %s", idx, type); } return udata; } void * vlua_cmn_to_pointer(lua_State *lua) { void *ptr = (void *)lua_topointer(lua, -1); vlua_cmn_set_pointer(lua, ptr); return ptr; } void vlua_cmn_set_pointer(lua_State *lua, void *ptr) { lua_pushlightuserdata(lua, ptr); lua_pushvalue(lua, -2); lua_settable(lua, LUA_REGISTRYINDEX); } void vlua_cmn_from_pointer(lua_State *lua, void *ptr) { lua_pushlightuserdata(lua, ptr); lua_gettable(lua, LUA_REGISTRYINDEX); } void vlua_cmn_drop_pointer(lua_State *lua, void *ptr) { lua_pushlightuserdata(lua, ptr); lua_pushnil(lua); lua_settable(lua, LUA_REGISTRYINDEX); } int vlua_cmn_get_opt(lua_State *lua, opt_t *opt) { int nresults = 0; switch(opt->type) { case OPT_BOOL: lua_pushboolean(lua, opt->val.bool_val); nresults = 1; break; case OPT_INT: lua_pushinteger(lua, opt->val.int_val); nresults = 1; break; case OPT_STR: case OPT_STRLIST: case OPT_ENUM: case OPT_SET: case OPT_CHARSET: lua_pushstring(lua, vle_opt_to_string(opt)); nresults = 1; break; } return nresults; } int vlua_cmn_set_opt(lua_State *lua, opt_t *opt) { vle_tb_clear(vle_err); if(opt->type == OPT_BOOL) { luaL_checktype(lua, 3, LUA_TBOOLEAN); if(lua_toboolean(lua, -1)) { (void)vle_opt_on(opt); } else { (void)vle_opt_off(opt); } } else if(opt->type == OPT_INT) { luaL_checktype(lua, 3, LUA_TNUMBER); /* Let vle_opt_assign() handle floating point case. */ (void)vle_opt_assign(opt, lua_tostring(lua, 3)); } else if(opt->type == OPT_STR || opt->type == OPT_STRLIST || opt->type == OPT_ENUM || opt->type == OPT_SET || opt->type == OPT_CHARSET) { (void)vle_opt_assign(opt, luaL_checkstring(lua, 3)); } if(vle_tb_get_data(vle_err)[0] != '\0') { vle_tb_append_linef(vle_err, "Failed to set value of option %s", opt->name); return luaL_error(lua, "%s", vle_tb_get_data(vle_err)); } return 0; } void vlua_cmn_push_str_array(lua_State *lua, char *array[], int len) { int i; lua_createtable(lua, len, /*nrec=*/0); for(i = 0; i < len; ++i) { lua_pushstring(lua, array[i]); lua_seti(lua, -2, i + 1); } } void vlua_cmn_make_metatable(lua_State *lua, const char name[]) { if(name == NULL) { lua_createtable(lua, /*narr=*/0, /*nrec=*/2); } else { luaL_newmetatable(lua, name); } lua_pushvalue(lua, -1); lua_setfield(lua, -2, "__index"); lua_pushboolean(lua, 0); lua_setfield(lua, -2, "__metatable"); } int vlua_cmn_extract_indexes(lua_State *lua, view_t *view, int *count, int *indexes[]) { if(!lua_istable(lua, -1)) { return 1; } if(lua_getfield(lua, -1, "indexes") != LUA_TTABLE) { lua_pop(lua, 1); return 1; } lua_len(lua, -1); *count = lua_tointeger(lua, -1); *indexes = reallocarray(NULL, *count, sizeof((*indexes)[0])); if(*indexes == NULL) { *count = 0; lua_pop(lua, 2); return 1; } int i = 0; lua_pushnil(lua); while(lua_next(lua, -3) != 0) { int idx = lua_tointeger(lua, -1) - 1; /* XXX: Non-convertable to integer indexes are converted to (0 - 1) and * thrown away by the next line. */ if(idx >= 0 && idx < view->list_rows) { (*indexes)[i++] = idx; } lua_pop(lua, 1); } *count = i; *count = deduplicate_ints(*indexes, *count); lua_pop(lua, 2); return 0; } /* Removes duplicates from array of ints while sorting it. Returns new array * size. */ static int deduplicate_ints(int array[], int count) { if(count == 0) { return 0; } /* Sort list of indexes to simplify finding duplicates. */ safe_qsort(array, count, sizeof(array[0]), &int_sorter); /* Drop duplicates from the list of indexes. */ int i; int j = 1; for(i = 1; i < count; ++i) { if(array[i] != array[j - 1]) { array[j++] = array[i]; } } return j; } /* qsort() comparer that sorts ints. Returns standard -1, 0, 1 for * comparisons. */ static int int_sorter(const void *first, const void *second) { const int *a = first; const int *b = second; return SORT_CMP(*a, *b); } /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set cinoptions+=t0 : */