From d820a6bfd807b6d181a858f3ea8a0d0f5c2b1879 Mon Sep 17 00:00:00 2001
From: PilzAdam <pilzadam@minetest.net>
Date: Mon, 9 Sep 2013 22:50:25 +0200
Subject: [PATCH] Add Settings interface for Lua

---
 doc/lua_api.txt                   |  14 ++
 doc/menu_lua_api.txt              |   6 +
 src/script/lua_api/CMakeLists.txt |   1 +
 src/script/lua_api/l_settings.cpp | 216 ++++++++++++++++++++++++++++++
 src/script/lua_api/l_settings.h   |  73 ++++++++++
 src/script/lua_api/l_util.cpp     |  12 ++
 src/script/lua_api/l_util.h       |   3 +
 src/script/scripting_game.cpp     |   2 +
 src/script/scripting_mainmenu.cpp |   4 +
 9 files changed, 331 insertions(+)
 create mode 100644 src/script/lua_api/l_settings.cpp
 create mode 100644 src/script/lua_api/l_settings.h

diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index f593b0db8..ebd70ea6e 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -1069,6 +1069,8 @@ minetest.pos_to_string({x=X,y=Y,z=Z}) -> "(X,Y,Z)"
 minetest.string_to_pos(string) -> position
 ^ Same but in reverse
 ^ escapes characters [ ] \ , ;  that can not be used in formspecs
+minetest.is_yes(string)
+^ returns whether string can be interpreted as yes
 
 minetest namespace reference
 -----------------------------
@@ -1737,6 +1739,18 @@ methods:
   ^ from (minx,miny,minz) to (maxx,maxy,maxz) in the order of [z [y [x]]]
 - iterp(minp, maxp):  same as above, except takes a vector
 
+Settings: An interface to read config files in the format of minetest.conf
+- Can be created via Settings(filename)
+methods:
+- get(key) -> value
+- get_bool(key) -> boolean
+- set(key, value)
+- remove(key) -> success
+- get_names() -> {key1,...}
+- write() -> success
+  ^ write changes to file
+- to_table() -> {[key1]=value1,...}
+
 Mapgen objects
 ---------------
 A mapgen object is a construct used in map generation.  Mapgen objects can be used by an on_generate 
diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt
index d4bc093b0..5763f875c 100644
--- a/doc/menu_lua_api.txt
+++ b/doc/menu_lua_api.txt
@@ -182,3 +182,9 @@ string:split(separator)
 ^ eg. string:split("a,b", ",") == {"a","b"}
 string:trim()
 ^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
+minetest.is_yes(string)
+^ returns whether string can be interpreted as yes
+
+Class reference
+----------------
+Settings: see lua_api.txt
diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt
index d75c04335..08960d2ad 100644
--- a/src/script/lua_api/CMakeLists.txt
+++ b/src/script/lua_api/CMakeLists.txt
@@ -15,6 +15,7 @@ set(common_SCRIPT_LUA_API_SRCS
 	${CMAKE_CURRENT_SOURCE_DIR}/l_server.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp
 	PARENT_SCOPE)
 
 # Used by client only
diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp
new file mode 100644
index 000000000..13a88ee95
--- /dev/null
+++ b/src/script/lua_api/l_settings.cpp
@@ -0,0 +1,216 @@
+/*
+Minetest
+Copyright (C) 2013 PilzAdam <pilzadam@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "lua_api/l_settings.h"
+#include "lua_api/l_internal.h"
+#include "settings.h"
+#include "log.h"
+
+// garbage collector
+int LuaSettings::gc_object(lua_State* L)
+{
+	LuaSettings* o = *(LuaSettings **)(lua_touserdata(L, 1));
+	delete o;
+	return 0;
+}
+
+// get(self, key) -> value
+int LuaSettings::l_get(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	LuaSettings* o = checkobject(L, 1);
+
+	std::string key = std::string(luaL_checkstring(L, 2));
+	if (o->m_settings->exists(key)) {
+		std::string value = o->m_settings->get(key);
+		lua_pushstring(L, value.c_str());
+	} else {
+		lua_pushnil(L);
+	}
+
+	return 1;
+}
+
+// get_bool(self, key) -> boolean
+int LuaSettings::l_get_bool(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	LuaSettings* o = checkobject(L, 1);
+
+	std::string key = std::string(luaL_checkstring(L, 2));
+	if (o->m_settings->exists(key)) {
+		bool value = o->m_settings->getBool(key);
+		lua_pushboolean(L, value);
+	} else {
+		lua_pushnil(L);
+	}
+
+	return 1;
+}
+
+// set(self, key, value)
+int LuaSettings::l_set(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	LuaSettings* o = checkobject(L, 1);
+
+	std::string key = std::string(luaL_checkstring(L, 2));
+	const char* value = luaL_checkstring(L, 3);
+
+	o->m_settings->set(key, value);
+
+	return 1;
+}
+
+// remove(self, key) -> success
+int LuaSettings::l_remove(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	LuaSettings* o = checkobject(L, 1);
+
+	std::string key = std::string(luaL_checkstring(L, 2));
+
+	bool success = o->m_settings->remove(key);
+	lua_pushboolean(L, success);
+
+	return 1;
+}
+
+// get_names(self) -> {key1, ...}
+int LuaSettings::l_get_names(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	LuaSettings* o = checkobject(L, 1);
+
+	std::vector<std::string> keys = o->m_settings->getNames();
+
+	lua_newtable(L);
+	for (unsigned int i=0; i < keys.size(); i++)
+	{
+		lua_pushstring(L, keys[i].c_str());
+		lua_rawseti(L, -2, i + 1);
+	}
+
+	return 1;
+}
+
+// write(self) -> success
+int LuaSettings::l_write(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	LuaSettings* o = checkobject(L, 1);
+
+	bool success = o->m_settings->updateConfigFile(o->m_filename.c_str());
+	lua_pushboolean(L, success);
+
+	return 1;
+}
+
+// to_table(self) -> {[key1]=value1,...}
+int LuaSettings::l_to_table(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	LuaSettings* o = checkobject(L, 1);
+
+	std::vector<std::string> keys = o->m_settings->getNames();
+
+	lua_newtable(L);
+	for (unsigned int i=0; i < keys.size(); i++)
+	{
+		lua_pushstring(L, o->m_settings->get(keys[i]).c_str());
+		lua_setfield(L, -2, keys[i].c_str());
+	}
+
+	return 1;
+}
+
+LuaSettings::LuaSettings(const char* filename)
+{
+	m_filename = std::string(filename);
+
+	m_settings = new Settings();
+	m_settings->readConfigFile(m_filename.c_str());
+}
+
+LuaSettings::~LuaSettings()
+{
+	delete m_settings;
+}
+
+void LuaSettings::Register(lua_State* L)
+{
+	lua_newtable(L);
+	int methodtable = lua_gettop(L);
+	luaL_newmetatable(L, className);
+	int metatable = lua_gettop(L);
+
+	lua_pushliteral(L, "__metatable");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+	lua_pushliteral(L, "__index");
+	lua_pushvalue(L, methodtable);
+	lua_settable(L, metatable);
+
+	lua_pushliteral(L, "__gc");
+	lua_pushcfunction(L, gc_object);
+	lua_settable(L, metatable);
+
+	lua_pop(L, 1);  // drop metatable
+
+	luaL_openlib(L, 0, methods, 0);  // fill methodtable
+	lua_pop(L, 1);  // drop methodtable
+
+	// Can be created from Lua (Settings(filename))
+	lua_register(L, className, create_object);
+}
+
+// LuaSettings(filename)
+// Creates an LuaSettings and leaves it on top of stack
+int LuaSettings::create_object(lua_State* L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	const char* filename = luaL_checkstring(L, 1);
+	LuaSettings* o = new LuaSettings(filename);
+	*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+	luaL_getmetatable(L, className);
+	lua_setmetatable(L, -2);
+	return 1;
+}
+
+LuaSettings* LuaSettings::checkobject(lua_State* L, int narg)
+{
+	NO_MAP_LOCK_REQUIRED;
+	luaL_checktype(L, narg, LUA_TUSERDATA);
+	void *ud = luaL_checkudata(L, narg, className);
+	if(!ud) luaL_typerror(L, narg, className);
+	return *(LuaSettings**)ud;  // unbox pointer
+}
+
+const char LuaSettings::className[] = "Settings";
+const luaL_reg LuaSettings::methods[] = {
+	luamethod(LuaSettings, get),
+	luamethod(LuaSettings, get_bool),
+	luamethod(LuaSettings, set),
+	luamethod(LuaSettings, remove),
+	luamethod(LuaSettings, get_names),
+	luamethod(LuaSettings, write),
+	luamethod(LuaSettings, to_table),
+	{0,0}
+};
diff --git a/src/script/lua_api/l_settings.h b/src/script/lua_api/l_settings.h
new file mode 100644
index 000000000..cb0c09a73
--- /dev/null
+++ b/src/script/lua_api/l_settings.h
@@ -0,0 +1,73 @@
+/*
+Minetest
+Copyright (C) 2013 PilzAdam <pilzadam@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#ifndef L_SETTINGS_H_
+#define L_SETTINGS_H_
+
+#include "lua_api/l_base.h"
+
+class Settings;
+
+class LuaSettings : public ModApiBase {
+private:
+	static const char className[];
+	static const luaL_reg methods[];
+
+	// garbage collector
+	static int gc_object(lua_State* L);
+
+	// get(self, key) -> value
+	static int l_get(lua_State* L);
+
+	// get_bool(self, key) -> boolean
+	static int l_get_bool(lua_State* L);
+
+	// set(self, key, value)
+	static int l_set(lua_State* L);
+
+	// remove(self, key) -> success
+	static int l_remove(lua_State* L);
+
+	// get_names(self) -> {key1, ...}
+	static int l_get_names(lua_State* L);
+
+	// write(self) -> success
+	static int l_write(lua_State* L);
+
+	// to_table(self) -> {[key1]=value1,...}
+	static int l_to_table(lua_State* L);
+
+	Settings* m_settings;
+	std::string m_filename;
+
+public:
+	LuaSettings(const char* filename);
+	~LuaSettings();
+
+	// LuaSettings(filename)
+	// Creates an LuaSettings and leaves it on top of stack
+	static int create_object(lua_State* L);
+
+	static LuaSettings* checkobject(lua_State* L, int narg);
+
+	static void Register(lua_State* L);
+
+};
+
+#endif
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index 30fa56c42..34788accd 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -220,6 +220,16 @@ int ModApiUtil::l_get_password_hash(lua_State *L)
 	return 1;
 }
 
+// is_yes(string)
+int ModApiUtil::l_is_yes(lua_State *L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	std::string str = luaL_checkstring(L, 1);
+	bool yes = is_yes(str);
+	lua_pushboolean(L, yes);
+	return 1;
+}
+
 void ModApiUtil::Initialize(lua_State *L, int top)
 {
 	API_FCT(debug);
@@ -237,5 +247,7 @@ void ModApiUtil::Initialize(lua_State *L, int top)
 	API_FCT(get_hit_params);
 
 	API_FCT(get_password_hash);
+
+	API_FCT(is_yes);
 }
 
diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h
index 71c55b342..ae2163ec8 100644
--- a/src/script/lua_api/l_util.h
+++ b/src/script/lua_api/l_util.h
@@ -71,6 +71,9 @@ private:
 	// get_password_hash(name, raw_password)
 	static int l_get_password_hash(lua_State *L);
 
+	// is_yes(string)
+	static int l_is_yes(lua_State *L);
+
 public:
 	static void Initialize(lua_State *L, int top);
 
diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp
index dfbf471d2..043ee4bae 100644
--- a/src/script/scripting_game.cpp
+++ b/src/script/scripting_game.cpp
@@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_server.h"
 #include "lua_api/l_util.h"
 #include "lua_api/l_vmanip.h"
+#include "lua_api/l_settings.h"
 
 extern "C" {
 #include "lualib.h"
@@ -96,4 +97,5 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
 	NodeMetaRef::Register(L);
 	NodeTimerRef::Register(L);
 	ObjectRef::Register(L);
+	LuaSettings::Register(L);
 }
diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp
index 47461e7ca..31581a1bf 100644
--- a/src/script/scripting_mainmenu.cpp
+++ b/src/script/scripting_mainmenu.cpp
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_base.h"
 #include "lua_api/l_mainmenu.h"
 #include "lua_api/l_util.h"
+#include "lua_api/l_settings.h"
 
 extern "C" {
 #include "lualib.h"
@@ -62,4 +63,7 @@ void MainMenuScripting::InitializeModApi(lua_State *L, int top)
 	// Initialize mod api modules
 	ModApiMainMenu::Initialize(L, top);
 	ModApiUtil::Initialize(L, top);
+
+	// Register reference classes (userdata)
+	LuaSettings::Register(L);
 }