From 1154e02fdded191147284997707a3b18ee3b43fd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 16 Sep 2024 13:53:11 -0400 Subject: WIP: edits in support of Lua script args --- indra/llcommon/lua_function.cpp | 39 +++++++++++++++++--- indra/llcommon/lua_function.h | 17 +++++++-- indra/newview/llluamanager.cpp | 79 ++++++++++++++++++++++++++++++----------- 3 files changed, 106 insertions(+), 29 deletions(-) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 2557fd0cc9..014ff3c4c4 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -60,7 +60,8 @@ namespace namespace lluau { -int dostring(lua_State* L, const std::string& desc, const std::string& text) +int dostring(lua_State* L, const std::string& desc, const std::string& text, + const std::vector& args) { auto r = loadstring(L, desc, text); if (r != LUA_OK) @@ -80,12 +81,22 @@ int dostring(lua_State* L, const std::string& desc, const std::string& text) // stack: compiled chunk, debug.traceback() lua_insert(L, -2); // stack: debug.traceback(), compiled chunk - LuaRemover cleanup(L, -2); + // capture absolute index of debug.traceback() + int traceback = lua_absindex(L, -2); + // remove it from stack on exit + LuaRemover cleanup(L, traceback); + + // push any args passed -- all strings -- script must handle any desired + // conversions + for (const auto& arg : args) + { + lua_pushstdstring(L, arg); + } // It's important to pass LUA_MULTRET as the expected number of return // values: if we pass any fixed number, we discard any returned values // beyond that number. - return lua_pcall(L, 0, LUA_MULTRET, -2); + return lua_pcall(L, int(args.size()), LUA_MULTRET, traceback); } int loadstring(lua_State *L, const std::string &desc, const std::string &text) @@ -804,7 +815,13 @@ bool LuaState::checkLua(const std::string& desc, int r) return true; } -std::pair LuaState::expr(const std::string& desc, const std::string& text) +std::pair LuaState::expr(const std::string& desc, const ScriptCommand& command) +{ + return expr(desc, , command.args); +} + +std::pair LuaState::expr(const std::string& desc, const std::string& text, + const std::vector& args) { /*---------------------------- feature flag ----------------------------*/ if (! mFeature) @@ -827,7 +844,7 @@ std::pair LuaState::expr(const std::string& desc, const std::string& }; LL_INFOS("Lua") << desc << " run" << LL_ENDL; - if (! checkLua(desc, lluau::dostring(mState, desc, text))) + if (! checkLua(desc, lluau::dostring(mState, desc, text, args))) { LL_WARNS("Lua") << desc << " error: " << mError << LL_ENDL; return { -1, mError }; @@ -1049,6 +1066,18 @@ std::pair LuaFunction::getState() return { registry, lookup }; } +/***************************************************************************** +* LuaCommand +*****************************************************************************/ +LuaCommand::LuaCommand(const std::string& command) +{ +} + +bool LuaCommand::found() const +{ + return ; +} + /***************************************************************************** * source_path() *****************************************************************************/ diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 10c201c234..a5022db225 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -19,6 +19,7 @@ #include "fsyspath.h" #include "llerror.h" #include "llsd.h" +#include "scriptcommand.h" #include "stringize.h" #include // std::uncaught_exceptions() #include // std::shared_ptr @@ -26,6 +27,7 @@ #include #include #include // std::pair +#include class LuaListener; @@ -55,8 +57,10 @@ namespace lluau // luau removed lua_dostring(), but since we perform the equivalent luau // sequence in multiple places, encapsulate it. desc and text are strings // rather than string_views because dostring() needs pointers to nul- - // terminated char arrays. - int dostring(lua_State* L, const std::string& desc, const std::string& text); + // terminated char arrays. Any args are pushed to the Lua stack before + // calling the Lua chunk in text. + int dostring(lua_State* L, const std::string& desc, const std::string& text, + const std::vector& args={}); int loadstring(lua_State* L, const std::string& desc, const std::string& text); fsyspath source_path(lua_State* L); @@ -92,13 +96,20 @@ public: // expr() is for when we want to capture any results left on the stack // by a Lua expression, possibly including multiple return values. + // Pass: + // desc = description used for logging et al. + // text = Lua chunk to execute, e.g. contents of a script file + // args = arguments, if any, to pass to script file + // Returns: // int < 0 means error, and LLSD::asString() is the error message. // int == 0 with LLSD::isUndefined() means the Lua expression returned no // results. // int == 1 means the Lua expression returned one result. // int > 1 with LLSD::isArray() means the Lua expression returned // multiple results, represented as the entries of the array. - std::pair expr(const std::string& desc, const std::string& text); + std::pair expr(const std::string& desc, const std::string& text, + const std::vector& args={}); + std::pair expr(const std::string& desc, const ScriptCommand& command); operator lua_State*() const { return mState; } diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 0bd21516bc..c4ce07e492 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -192,35 +192,72 @@ void LLLUAmanager::runScriptFile(const std::string &filename, bool autorun, LLCoros::instance().launch(filename, [filename, autorun, result_cb, finished_cb]() { ScriptObserver observer(LLCoros::getName(), filename); - llifstream in_file; - in_file.open(filename.c_str()); + // Use LLStringUtil::getTokens() to parse the script command line + auto tokens = LLStringUtil::getTokens(filename, + " \t\r\n", // drop_delims + "", // no keep_delims + "\"'", // either kind of quotes + "\\"); // backslash escape + +#error Under construction + // TODO: + // - Move this either/or file existence logic to LuaCommand() + // - Accept LuaCommand in all LLLUAmanager::mumbleScriptFile() methods + // - Update all existing mumbleScriptFile() callers + llifstream in_file; + in_file.open(tokens[0]); if (in_file.is_open()) { - if (autorun) - { - sAutorunScriptCount++; - } - sScriptCount++; - - // A script_finished_fn is used to initialize the LuaState. - // It will be called when the LuaState is destroyed. - LuaState L(finished_cb); - std::string text{std::istreambuf_iterator(in_file), {}}; - auto [count, result] = L.expr(filename, text); - if (result_cb) - { - result_cb(count, result); - } + // The first token is in fact the script filename. Now that the + // script file is open, we've consumed that token. The rest are + // command-line arguments. + tokens.erase(tokens.begin()); } else { - auto msg{ stringize("unable to open script file '", filename, "'") }; - LL_WARNS("Lua") << msg << LL_ENDL; - if (result_cb) + // Parsing the command line produced a script file path we can't + // open. Maybe that's because there are spaces in the original + // pathname that were neither quoted nor escaped? See if we can + // open the whole original command line string. + in_file.open(filename); + if (! in_file.is_open()) { - result_cb(-1, msg); + std::ostringstream msgstream; + msgstream << "Can't open script file " << std::quoted(tokens[0]); + if (filename != tokens[0]) + { + msgstream << " or " << std::quoted(filename); + } + auto msg = msgstream.str(); + LL_WARNS("Lua") << msg << LL_ENDL; + if (result_cb) + { + result_cb(-1, msg); + } + return; } + // Here we do have in_file open, using the whole input command + // line as its pathname. Discard any parts of it we mistook for + // command-line arguments. + tokens.clear(); + } + + // Here in_file is open. + if (autorun) + { + sAutorunScriptCount++; + } + sScriptCount++; + + // A script_finished_fn is used to initialize the LuaState. + // It will be called when the LuaState is destroyed. + LuaState L(finished_cb); + std::string text{std::istreambuf_iterator(in_file), {}}; + auto [count, result] = L.expr(filename, text, tokens); + if (result_cb) + { + result_cb(count, result); } }); } -- cgit v1.2.3