From 2242d42c9c896d754037d88a5a5a94f0a0fb16c6 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 7 Jun 2014 20:15:49 -0400 Subject: [PATCH 1/7] Implement runCommand in Lua API --- library/LuaApi.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 3a374445f..91baf5fbb 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2227,6 +2227,31 @@ static int internal_getDir(lua_State *L) } return 1; } + +static int internal_runCommand(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + std::string command = lua_tostring(L, 1); + buffered_color_ostream out; + Core::getInstance().runCommand(out, command); + auto fragments = out.fragments(); + lua_newtable(L); + int i = 1; + for (auto iter = fragments.begin(); iter != fragments.end(); iter++, i++) + { + int color = iter->first; + std::string output = iter->second; + lua_createtable(L, 2, 0); + lua_pushinteger(L, color); + lua_rawseti(L, -2, 1); + lua_pushstring(L, output.c_str()); + lua_rawseti(L, -2, 2); + lua_rawseti(L, -2, i); + } + lua_pushvalue(L, -1); + return 1; +} + static const luaL_Reg dfhack_internal_funcs[] = { { "getAddress", internal_getAddress }, { "setAddress", internal_setAddress }, @@ -2240,6 +2265,7 @@ static const luaL_Reg dfhack_internal_funcs[] = { { "memscan", internal_memscan }, { "diffscan", internal_diffscan }, { "getDir", internal_getDir }, + { "runCommand", internal_runCommand }, { NULL, NULL } }; From 91a93a00d2222039397d910aa346beddb9b2f5e6 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 7 Jun 2014 20:31:14 -0400 Subject: [PATCH 2/7] Add dfhack.run_command (Lua) Simplified version of runCommand --- library/lua/dfhack.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 77c62311a..10af8b43e 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -256,7 +256,8 @@ function dfhack.interpreter(prompt,hfile,env) print("Shortcuts:\n".. " '= foo' => '_1,_2,... = foo'\n".. " '! foo' => 'print(foo)'\n".. - "Both save the first result as '_'.") + " '~ foo' => 'printall(foo)'\n".. + "All of these save the first result as '_'.") print_banner = false end @@ -357,6 +358,16 @@ function dfhack.run_script(name,...) return f(...) end +function dfhack.run_command(command, ...) + command = command .. ' ' .. table.concat({...}, ' ') + fragments = internal.runCommand(command) + output = "" + for i, f in pairs(fragments) do + output = output .. f[2] + end + return output +end + -- Per-save init file function dfhack.getSavePath() From 143b1e3469f7161bcb38687a5d6ded28d9f95c3e Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 10 Jun 2014 13:41:01 -0400 Subject: [PATCH 3/7] Lua runCommand improvements * Return error codes (e.g. CR_FAILURE) when a command fails instead of output * Make dfhack.runCommand() take a list of arguments as well --- library/LuaApi.cpp | 8 +++++++- library/lua/dfhack.lua | 24 ++++++++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 91baf5fbb..d64551bab 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2230,10 +2230,16 @@ static int internal_getDir(lua_State *L) static int internal_runCommand(lua_State *L) { + CoreSuspender suspend; luaL_checktype(L, 1, LUA_TSTRING); std::string command = lua_tostring(L, 1); buffered_color_ostream out; - Core::getInstance().runCommand(out, command); + command_result res = Core::getInstance().runCommand(out, command); + if (res != CR_OK) + { + lua_pushinteger(L, (int)res); + return 1; + } auto fragments = out.fragments(); lua_newtable(L); int i = 1; diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 10af8b43e..b2f456532 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -10,6 +10,14 @@ local dfhack = dfhack local base_env = dfhack.BASE_G local _ENV = base_env +CR_LINK_FAILURE = -3 +CR_NEEDS_CONSOLE = -2 +CR_NOT_IMPLEMENTED = -1 +CR_OK = 0 +CR_FAILURE = 1 +CR_WRONG_USAGE = 2 +CR_NOT_FOUND = 3 + -- Console color constants COLOR_RESET = -1 @@ -358,11 +366,19 @@ function dfhack.run_script(name,...) return f(...) end -function dfhack.run_command(command, ...) - command = command .. ' ' .. table.concat({...}, ' ') - fragments = internal.runCommand(command) +function dfhack.run_command(...) + args = {...} + if type(args[1]) == 'table' then + command = table.concat(args[1], ' ') + else + command = table.concat(args, ' ') + end + result = internal.runCommand(command) + if type(result) == 'number' then + return result + end output = "" - for i, f in pairs(fragments) do + for i, f in pairs(result) do output = output .. f[2] end return output From 2a012591923863963629e60b138e330cfdca6d27 Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 10 Jun 2014 21:38:21 -0400 Subject: [PATCH 4/7] Fix runCommand crash, return output and result --- library/LuaApi.cpp | 9 +++------ library/lua/dfhack.lua | 9 ++++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index d64551bab..af318f305 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2230,18 +2230,15 @@ static int internal_getDir(lua_State *L) static int internal_runCommand(lua_State *L) { - CoreSuspender suspend; luaL_checktype(L, 1, LUA_TSTRING); + CoreSuspender suspend; std::string command = lua_tostring(L, 1); buffered_color_ostream out; command_result res = Core::getInstance().runCommand(out, command); - if (res != CR_OK) - { - lua_pushinteger(L, (int)res); - return 1; - } auto fragments = out.fragments(); lua_newtable(L); + lua_pushinteger(L, (int)res); + lua_setfield(L, -2, "status"); int i = 1; for (auto iter = fragments.begin(); iter != fragments.end(); iter++, i++) { diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index b2f456532..970d50a4a 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -374,14 +374,13 @@ function dfhack.run_command(...) command = table.concat(args, ' ') end result = internal.runCommand(command) - if type(result) == 'number' then - return result - end output = "" for i, f in pairs(result) do - output = output .. f[2] + if type(f) == 'table' then + output = output .. f[2] + end end - return output + return output, result.status end -- Per-save init file From 7593d8404cccabfac10ecd0b2e69a89b568bb5f0 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sun, 15 Jun 2014 22:51:43 -0400 Subject: [PATCH 5/7] Update NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 75c751900..8a821a1c2 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ DFHack future - support for calling a lua function via a protobuf request (demonstrated by dfhack-run --lua). - Lua API for listing files in directory. Needed for mod-manager. - Lua API for creating unit combat reports and writing to gamelog. + - Lua API for running arbitrary DFHack commands - support for multiple raw/init.d/*.lua init scripts in one save. - eventful now has a more friendly way of making custom sidebars - new plugin: building-hacks. Allows to add custom functionality and/or animations to buildings. From d538e13450ab9f19a3a5eaa90989096668efd4a5 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 16 Jun 2014 11:16:35 -0400 Subject: [PATCH 6/7] Allow runCommand arguments to be passed as a table internally --- library/LuaApi.cpp | 31 +++++++++++++++++++++++++++---- library/lua/dfhack.lua | 12 ++++++++++-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index af318f305..11cefcc06 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2230,11 +2230,34 @@ static int internal_getDir(lua_State *L) static int internal_runCommand(lua_State *L) { - luaL_checktype(L, 1, LUA_TSTRING); - CoreSuspender suspend; - std::string command = lua_tostring(L, 1); buffered_color_ostream out; - command_result res = Core::getInstance().runCommand(out, command); + command_result res; + if (lua_gettop(L) == 0) + { + lua_pushstring(L, ""); + } + if (lua_type(L, 1) == LUA_TTABLE) + { + std::string command = ""; + std::vector args; + lua_pushnil(L); // first key + while (lua_next(L, 1) != 0) + { + if (command == "") + command = lua_tostring(L, -1); + else + args.push_back(lua_tostring(L, -1)); + lua_pop(L, 1); // remove value, leave key + } + CoreSuspender suspend; + res = Core::getInstance().runCommand(out, command, args); + } + else + { + std::string command = lua_tostring(L, 1); + CoreSuspender suspend; + res = Core::getInstance().runCommand(out, command); + } auto fragments = out.fragments(); lua_newtable(L); lua_pushinteger(L, (int)res); diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 970d50a4a..3cc98e882 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -369,9 +369,17 @@ end function dfhack.run_command(...) args = {...} if type(args[1]) == 'table' then - command = table.concat(args[1], ' ') + command = args[1] + elseif #args > 1 and type(args[2]) == 'table' then + -- {args[1]} + args[2] + command = args[2] + table.insert(command, 1, args[1]) + elseif #args == 1 and type(args[1]) == 'string' then + command = args[1] + elseif #args > 1 and type(args[1]) == 'string' then + command = args else - command = table.concat(args, ' ') + error('Invalid arguments') end result = internal.runCommand(command) output = "" From fb922fab37128ab41af77995379a6e08705465e1 Mon Sep 17 00:00:00 2001 From: lethosor Date: Mon, 16 Jun 2014 11:40:26 -0400 Subject: [PATCH 7/7] Handle invalid arguments to runCommand --- library/LuaApi.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 11cefcc06..529f51edb 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -2236,7 +2236,8 @@ static int internal_runCommand(lua_State *L) { lua_pushstring(L, ""); } - if (lua_type(L, 1) == LUA_TTABLE) + int type_1 = lua_type(L, 1); + if (type_1 == LUA_TTABLE) { std::string command = ""; std::vector args; @@ -2252,12 +2253,18 @@ static int internal_runCommand(lua_State *L) CoreSuspender suspend; res = Core::getInstance().runCommand(out, command, args); } - else + else if (type_1 == LUA_TSTRING) { std::string command = lua_tostring(L, 1); CoreSuspender suspend; res = Core::getInstance().runCommand(out, command); } + else + { + lua_pushnil(L); + lua_pushfstring(L, "Expected table, got %s", lua_typename(L, type_1)); + return 2; + } auto fragments = out.fragments(); lua_newtable(L); lua_pushinteger(L, (int)res);