terom@93: #include "lua_objs.h" terom@116: #include "lua_irc.h" terom@104: #include "log.h" terom@93: terom@108: #include terom@98: #include terom@98: terom@116: void lua_obj_create_type (lua_State *L, const char *name, const struct luaL_Reg methods[]) terom@96: { terom@96: luaL_newmetatable(L, name); terom@96: terom@96: // set the metatable __index to itself terom@96: lua_pushvalue(L, -1); terom@96: lua_setfield(L, -1, "__index"); terom@96: terom@96: // register the methods to the metatable terom@96: luaL_register(L, NULL, methods); terom@96: } terom@96: terom@116: void* lua_obj_create_obj (lua_State *L, const char *name, size_t size) terom@96: { terom@96: // create the new userdata on the stack terom@96: void *ud = lua_newuserdata(L, size); terom@96: terom@96: // get the type and set it terom@96: luaL_getmetatable(L, name); terom@96: lua_setmetatable(L, -2); terom@96: terom@96: // ok terom@96: return ud; terom@96: } terom@96: terom@116: void* lua_obj_create_global_type (lua_State *L, const char *type_name, const struct luaL_Reg methods[], const char *global_name, size_t size) terom@103: { terom@103: // allocate the global object terom@103: void *obj = lua_newuserdata(L, size); terom@103: terom@103: // create the type metatable terom@103: lua_obj_create_type(L, type_name, methods); terom@103: terom@103: // set the userdata's metatable terom@103: lua_setmetatable(L, -2); terom@103: terom@103: // store it as a global terom@103: lua_setglobal(L, global_name); terom@103: terom@103: // ok terom@103: return obj; terom@103: } terom@103: terom@116: void* lua_obj_get_obj (lua_State *L, const char *func, const char *name) terom@96: { terom@96: void *ud; terom@96: terom@96: // validate the userdata arg terom@96: if ((ud = luaL_checkudata(L, 1, name)) == NULL) { terom@96: luaL_error(L, "bad type argument to %s: `%s` expected", func, name); terom@103: terom@103: // XXX: needs a __noreturn__ attribute terom@103: return NULL; terom@96: terom@96: } else { terom@96: // ok terom@96: return ud; terom@96: terom@96: } terom@96: } terom@96: terom@141: const char *lua_arg_string (lua_State *L, int index, const char *name, const char *def) terom@141: { terom@141: const char *value; terom@141: terom@141: // use default? terom@141: if (lua_isnoneornil(L, index) && def != (const char *) LUA_ARG_REQUIRED) terom@141: return def; terom@141: terom@141: // value given? terom@141: if ((value = lua_tostring(L, index))) terom@141: return value; terom@141: terom@141: // error terom@141: luaL_error(L, "missing value for required string argument <%d:%s>", index, name); terom@141: } terom@141: terom@141: bool lua_arg_bool (lua_State *L, int index, const char *name, int def) terom@141: { terom@141: bool value; terom@141: terom@141: // use default? terom@141: if (lua_isnoneornil(L, index) && def != LUA_ARG_REQUIRED) terom@141: return def; terom@141: terom@141: // value given? terom@141: value = lua_toboolean(L, index); terom@141: terom@141: return value; terom@141: terom@141: // error terom@141: // luaL_error(L, "missing value of required boolean argument <%d:%s>", index, name); terom@141: } terom@141: terom@96: /** terom@103: * Wrapper for module terom@103: */ terom@103: struct lua_module { terom@103: struct module *module; terom@103: }; terom@103: terom@103: /** terom@103: * Create a lua_module userdata from the given module and push it onto the stack, returning 1. terom@113: * terom@113: * The given module should be a reference of its own right. terom@103: */ terom@103: static int lua_module_create (lua_State *L, struct module *module) terom@103: { terom@103: // create the new obj terom@103: struct lua_module *lua_module = lua_obj_create_obj(L, "spbot.module", sizeof(*lua_module)); terom@103: terom@103: // initialize terom@103: lua_module->module = module; terom@103: terom@103: // ok terom@103: return 1; terom@103: } terom@103: terom@107: /** terom@113: * module_put() our module reference terom@113: */ terom@113: static int lua_module__gc (lua_State *L) terom@113: { terom@113: struct lua_module *lua_module = lua_obj_get_obj(L, __func__, "spbot.module"); terom@113: terom@113: // put it terom@113: module_put(lua_module->module); terom@113: terom@113: return 0; terom@113: } terom@113: terom@103: static int lua_module_conf (lua_State *L) terom@103: { terom@103: struct lua_module *lua_module = lua_obj_get_obj(L, __func__, "spbot.module"); terom@103: const struct config_option *option; terom@124: struct error_info err; terom@124: bool is_err = true; terom@124: terom@124: // the list of given config values, and temporary storage for string values terom@122: struct config_value values[CONFIG_VALUES_MAX], *value = values; terom@122: char *value_bufs[CONFIG_VALUES_MAX], **value_buf = value_bufs; terom@122: terom@122: // number of arguments given terom@122: int nargs = lua_gettop(L), argidx = 2; terom@122: terom@122: // init to zero terom@122: memset(values, 0, sizeof(values)); terom@122: memset(value_bufs, 0, sizeof(value_bufs)); terom@103: terom@103: // XXX: come up with some better way... terom@103: struct nexus *nexus = lua_module->module->modules->nexus; terom@103: terom@103: // the config name terom@122: const char *conf_name = luaL_checkstring(L, argidx++); terom@103: terom@103: // look it up terom@103: if ((option = module_conf_lookup(lua_module->module, conf_name, &err)) == NULL) terom@103: return luaL_error(L, "module_conf_lookup: %s/%s: %s", module_name(lua_module->module), conf_name, error_msg(&err)); terom@107: terom@122: // maximum number of arguments accepted terom@122: int maxargs = config_params_count(option); terom@107: terom@122: // too many arguments? terom@124: if (nargs - argidx > maxargs) terom@124: return luaL_error(L, "lua_module_conf: too many arguments (>%d) given (%d)", maxargs, nargs - argidx); terom@122: terom@122: // the current param terom@122: const struct config_param *param = option->params; terom@107: terom@124: // apply each given argument to the correct param, storing it in value terom@122: for (; argidx <= nargs; argidx++, value++, param++) { terom@124: // the given config value terom@122: switch (lua_type(L, argidx)) { terom@124: case LUA_TNONE: terom@124: case LUA_TNIL: terom@124: // no value terom@124: value->type = CONFIG_NULL; terom@124: terom@124: break; terom@124: terom@122: case LUA_TSTRING: { terom@122: // string arg terom@122: const char *arg_str = lua_tostring(L, argidx); terom@122: terom@122: // copy it as a mutable string buffer terom@122: if ((*value_buf = strdup(arg_str)) == NULL) { terom@122: lua_pushfstring(L, "strdup"); terom@122: goto error; terom@122: } terom@122: terom@122: // parse it as a raw value terom@122: if (config_parse_param(param, nexus, value, *value_buf, &err)) { terom@122: lua_pushfstring(L, "config_parse: %s/%s: %s", option->name, *value_buf, error_msg(&err)); terom@122: goto error; terom@122: } terom@122: terom@122: // seek to next value_buf terom@122: value_buf++; terom@122: terom@122: } break; terom@122: terom@122: case LUA_TUSERDATA: terom@122: // some kind of userdata, use its metatable to figure out what type it is terom@122: if (!lua_getmetatable(L, argidx)) { terom@122: lua_pushfstring(L, "config value is userdata without metatable"); terom@122: goto error; terom@122: } terom@122: terom@122: // get the target metatable terom@122: lua_getfield(L, LUA_REGISTRYINDEX, "evirc.chan"); terom@122: terom@122: // is it a chan? terom@122: if (!lua_rawequal(L, -1, -2)) { terom@122: lua_pushfstring(L, "config value is userdata of unknown type"); terom@122: goto error; terom@122: } terom@122: terom@122: // pop the metatables terom@122: lua_pop(L, 2); terom@122: terom@122: // get the irc_chan terom@122: struct lua_chan *lua_chan = lua_touserdata(L, argidx); terom@122: terom@122: // build the value terom@122: value->type = CONFIG_IRC_CHAN; terom@122: value->irc_chan = lua_chan->chan; terom@122: terom@122: break; terom@107: terom@122: default: terom@122: lua_pushfstring(L, "config value is of unknown lua type '%s'", lua_typename(L, argidx)); terom@122: goto error; terom@107: terom@122: } terom@122: } terom@122: terom@122: // apply it terom@122: if (module_conf(lua_module->module, option, values, &err)) { terom@122: lua_pushfstring(L, "module_conf: %s/%s: %s", module_name(lua_module->module), option->name, error_msg(&err)); terom@122: goto error; terom@107: } terom@103: terom@103: // ok terom@122: is_err = false; terom@122: terom@122: error: terom@122: // release any allocated strings terom@122: for (value_buf = value_bufs; value_buf <= value_bufs + CONFIG_VALUES_MAX && *value_buf; value_buf++) terom@122: free(*value_buf); terom@122: terom@122: // either error or successful return terom@122: if (is_err) terom@122: return lua_error(L); terom@122: else terom@122: return 0; terom@103: } terom@103: terom@113: static int lua_module_unload (lua_State *L) terom@113: { terom@113: struct lua_module *lua_module = lua_obj_get_obj(L, __func__, "spbot.module"); terom@113: struct error_info err; terom@113: terom@113: // just unload it terom@113: if ((ERROR_CODE(&err) = module_unload(lua_module->module))) terom@113: return luaL_error(L, "module_unload: %s: %s", module_name(lua_module->module), error_msg(&err)); terom@113: terom@113: // ok terom@113: return 0; terom@113: } terom@113: terom@103: static const struct luaL_Reg lua_module_methods[] = { terom@113: { "__gc", &lua_module__gc }, terom@103: { "conf", &lua_module_conf }, terom@113: { "unload", &lua_module_unload }, terom@103: { NULL, NULL } terom@103: }; terom@103: terom@103: /** terom@103: * Initialize the lua_module object type terom@103: */ terom@103: static void lua_module_init (lua_State *L) terom@103: { terom@103: lua_obj_create_type(L, "spbot.module", lua_module_methods); terom@103: } terom@103: terom@103: /** terom@103: * Wrapper for modules terom@103: */ terom@103: struct lua_modules { terom@103: struct modules *modules; terom@108: terom@108: // strdup'd path for module_path terom@108: // XXX: remove when gc'd terom@108: char *path; terom@103: }; terom@103: terom@114: static int lua_modules__gc (lua_State *L) terom@114: { terom@114: struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules"); terom@114: terom@114: // remove the modules path if it was set by us terom@114: if (lua_modules->path && modules_path(lua_modules->modules, NULL) == lua_modules->path) terom@114: modules_path(lua_modules->modules, ""); terom@114: terom@114: // release any strdup'd path terom@114: free(lua_modules->path); terom@114: terom@114: // ok terom@114: return 0; terom@114: } terom@114: terom@108: static int lua_modules_path (lua_State *L) terom@108: { terom@108: struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules"); terom@108: char *path = NULL; terom@108: const char *old_path; terom@108: terom@108: if (!lua_isnoneornil(L, 2)) { terom@108: // the new path terom@108: if ((path = strdup(luaL_checkstring(L, 2))) == NULL) terom@108: return luaL_error(L, "strdup"); terom@108: } terom@108: terom@108: // set or get terom@108: old_path = modules_path(lua_modules->modules, path); terom@108: terom@108: // return the old path terom@108: if (old_path) terom@108: lua_pushstring(L, old_path); terom@108: else terom@108: lua_pushnil(L); terom@108: terom@108: if (path) { terom@108: // replace the old path terom@108: free(lua_modules->path); terom@108: lua_modules->path = path; terom@108: } terom@108: terom@108: // ok terom@108: return 1; terom@108: } terom@108: terom@103: static int lua_modules_load (lua_State *L) terom@103: { terom@103: struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules"); terom@103: struct module *module; terom@103: struct module_info info; terom@103: struct error_info err; terom@103: terom@103: // the module name/path terom@103: info.name = luaL_checkstring(L, 2); terom@108: info.path = lua_isnoneornil(L, 3) ? NULL : luaL_checkstring(L, 3); terom@103: terom@113: // load and get a new reference terom@103: if (module_load(lua_modules->modules, &module, &info, &err)) terom@103: return luaL_error(L, "module_load: %s/%s: %s", info.name, info.path, error_msg(&err)); terom@103: terom@103: // wrap terom@103: return lua_module_create(L, module); terom@103: } terom@103: terom@103: static int lua_modules_module (lua_State *L) terom@103: { terom@103: struct lua_modules *lua_modules = lua_obj_get_obj(L, __func__, "spbot.modules"); terom@103: struct module *module; terom@103: terom@103: // the module name terom@103: const char *name = luaL_checkstring(L, 2); terom@103: terom@113: // look it up, as a new reference terom@134: if ((module = modules_get(lua_modules->modules, name)) == NULL) terom@103: return luaL_error(L, "module_get: %s: no such module", name); terom@103: terom@103: // wrap terom@103: return lua_module_create(L, module); terom@103: } terom@103: terom@103: static const struct luaL_Reg lua_modules_methods[] = { terom@114: { "__gc", &lua_modules__gc }, terom@108: { "path", &lua_modules_path }, terom@103: { "load", &lua_modules_load }, terom@103: { "module", &lua_modules_module }, terom@103: { NULL, NULL } terom@103: }; terom@103: terom@103: /** terom@103: * Initialize the spbot.modules type for lua_modules, and registers an instance bound to the given modules list at terom@103: * 'modules'. terom@103: */ terom@103: static void lua_modules_init (lua_State *L, struct modules *modules) terom@103: { terom@103: // allocate the global "modules" object terom@103: struct lua_modules *lua_modules = lua_obj_create_global_type(L, "spbot.modules", lua_modules_methods, "modules", sizeof(*lua_modules)); terom@103: terom@103: // initialize it terom@103: lua_modules->modules = modules; terom@103: } terom@103: terom@103: /** terom@103: * Wrapper for nexus terom@103: */ terom@103: struct lua_nexus { terom@103: struct nexus *nexus; terom@103: }; terom@103: terom@103: static int lua_nexus_shutdown (lua_State *L) terom@103: { terom@103: struct lua_nexus *lua_nexus = lua_obj_get_obj(L, __func__, "spbot.nexus"); terom@103: terom@103: // just shut it down terom@103: nexus_shutdown(lua_nexus->nexus); terom@103: terom@103: return 0; terom@103: } terom@103: terom@109: static int lua_nexus_load_config (lua_State *L) terom@109: { terom@109: struct lua_nexus *lua_nexus = lua_obj_get_obj(L, __func__, "spbot.nexus"); terom@109: struct error_info err; terom@109: terom@109: const char *path = luaL_checkstring(L, 2); terom@109: terom@109: // just load it terom@109: if (nexus_load_config(lua_nexus->nexus, path, &err)) terom@109: return luaL_error(L, "nexus_load_config(%s): %s", path, error_msg(&err)); terom@109: terom@109: return 0; terom@109: } terom@109: terom@103: static const struct luaL_Reg lua_nexus_methods[] = { terom@103: { "shutdown", &lua_nexus_shutdown }, terom@109: { "load_config", &lua_nexus_load_config }, terom@103: { NULL, NULL } terom@103: }; terom@103: terom@103: /** terom@103: * Initialize the spbot.nexus type for lua_nexus, and registers an instance bound to the given nexus list at terom@103: * 'nexus'. terom@103: */ terom@103: static void lua_nexus_init (lua_State *L, struct nexus *nexus) terom@103: { terom@103: // allocate the global "nexus" object terom@103: struct lua_nexus *lua_nexus = lua_obj_create_global_type(L, "spbot.nexus", lua_nexus_methods, "nexus", sizeof(*lua_nexus)); terom@103: terom@103: // initialize it terom@103: lua_nexus->nexus = nexus; terom@93: } terom@93: terom@98: /** terom@104: * Global functions terom@104: */ terom@104: static int lua_log_level (lua_State *L) terom@104: { terom@104: // log level as a string terom@104: enum log_level new_level = luaL_checkoption(L, 1, NULL, log_level_names); terom@104: terom@104: // set it terom@104: set_log_level(new_level); terom@104: terom@104: // ok terom@104: return 0; terom@104: } terom@104: terom@104: static int lua_log (lua_State *L) terom@104: { terom@104: // log level as a string terom@104: enum log_level level = luaL_checkoption(L, 1, NULL, log_level_names); terom@104: terom@104: // log message terom@104: const char *msg = luaL_checkstring(L, 2); terom@104: terom@104: // log it terom@104: log_msg(level, "lua", "%s", msg); terom@104: terom@104: // ok terom@104: return 0; terom@104: } terom@104: terom@104: static const struct luaL_Reg lua_global_functions[] = { terom@104: { "log_level", &lua_log_level }, terom@104: { "log", &lua_log }, terom@104: { NULL, NULL } terom@104: }; terom@104: terom@104: static void lua_global_init (lua_State *L) terom@104: { terom@104: const struct luaL_Reg *reg; terom@104: terom@104: for (reg = lua_global_functions; reg->name && reg->func; reg++) { terom@104: // put the function on the stack terom@104: lua_pushcfunction(L, reg->func); terom@104: terom@104: // set the global terom@104: lua_setglobal(L, reg->name); terom@104: } terom@104: } terom@104: terom@116: void lua_objs_init (struct nexus_lua *lua) terom@93: { terom@93: // init the various bits terom@116: lua_global_init(lua->st); terom@116: lua_nexus_init(lua->st, lua->nexus); terom@116: lua_modules_init(lua->st, lua->nexus->modules); terom@116: lua_module_init(lua->st); terom@93: } terom@98: