From 9b6106f8aa2389188801b4e72318d826fc2010b3 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Mon, 27 Oct 2025 21:44:39 +0000 Subject: [PATCH] [Add] Implement build system for user scripts. --- assets/scripts/bin/build.lua | 27 ++++++++++++++++++++++ common/src/lua_api.cpp | 44 ++++++++++++++++++++++++++++++++++++ common/src/lua_api.h | 2 ++ common/src/lua_processor.cpp | 21 +++++++++-------- common/src/session.cpp | 39 +++++++++++++++++++++----------- 5 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 assets/scripts/bin/build.lua diff --git a/assets/scripts/bin/build.lua b/assets/scripts/bin/build.lua new file mode 100644 index 0000000..a3404b3 --- /dev/null +++ b/assets/scripts/bin/build.lua @@ -0,0 +1,27 @@ +-- /bin/build - "Compiles" a .lua file into an executable. +local source_filename = arg[1] + +if not source_filename then + return "build: missing file operand" +end + +-- Check for .lua extension. +if not source_filename:match("%.lua$") then + return "build: input file must be a .lua file" +end + +local current_dir = bettola.get_current_dir(context) +local source_node = current_dir.children[source_filename] + +if not source_node then + return "build: cannot open '" .. source_filename .."': No such file" +end + +if source_node.type ~= 0 then + return "build: '" .. source_filename .. "' is not a regular file" +end + +local executable_filename = source_filename:gsub("%.lua$", "") +local source_content = source_node.content + +return bettola.create_executable(context, executable_filename, source_content) diff --git a/common/src/lua_api.cpp b/common/src/lua_api.cpp index b37e880..47fe9bf 100644 --- a/common/src/lua_api.cpp +++ b/common/src/lua_api.cpp @@ -78,6 +78,50 @@ std::string write_file(Session& context, const std::string& path, return ""; } +std::string create_executable(Session& context, const std::string& path, + const std::string& content) { + vfs_node* parent_dir = nullptr; + std::string filename; + + if(path[0] == '/') { + Machine* session_machine = context.get_session_machine(); + vfs_node* root = session_machine->vfs_root; + + size_t last_slash = path.find_last_of('/'); + if(last_slash == 0) { + parent_dir = root; + filename = path.substr(1); + } else { + std::string parent_path = path.substr(0, last_slash); + parent_dir = find_node_by_path(root, parent_path); + filename = path.substr(last_slash+1); + } + } else { + parent_dir = context.get_current_dir(); + filename = path; + } + + if(!parent_dir) { + return "create_executable: cannot create file '" + path + "': No such file or directory"; + } + + if(parent_dir->type != DIR_NODE) { + return "create_executable: cannot create file in '" + get_full_path(parent_dir) + + "': Not a directory"; + } + + /* Overwrite if exists. */ + auto it = parent_dir->children.find(filename); + if(it != parent_dir->children.end()) { + delete it->second; + parent_dir->children.erase(it); + } + vfs_node* new_exec = new_node(filename, EXEC_NODE, parent_dir); + new_exec->content = content; + parent_dir->children[filename] = new_exec; + return ""; +} + std::string cd(Session& context, const std::string& path) { vfs_node* current_dir = context.get_current_dir(); diff --git a/common/src/lua_api.h b/common/src/lua_api.h index 6d685df..fd27bd3 100644 --- a/common/src/lua_api.h +++ b/common/src/lua_api.h @@ -13,6 +13,8 @@ vfs_node* get_current_dir(Session& context); std::string rm(Session& context, const std::string& filename); std::string write_file(Session& context, const std::string& path, const std::string& content); +std::string create_executable(Session& context, const std::string& path, + const std::string& content); std::string ls(Session& context); std::string cd(Session& context, const std::string& path); std::string scp(Session& context, const std::string& source, diff --git a/common/src/lua_processor.cpp b/common/src/lua_processor.cpp index a1da61b..a6c307f 100644 --- a/common/src/lua_processor.cpp +++ b/common/src/lua_processor.cpp @@ -23,16 +23,17 @@ LuaProcessor::LuaProcessor(Session& context) { /* Create the 'bettola' API table. */ sol::table bettola_api = _lua.create_named_table("bettola"); - bettola_api.set_function("rm", &api::rm); - bettola_api.set_function("ls", &api::ls); - bettola_api.set_function("write_file", &api::write_file); - bettola_api.set_function("get_current_dir", &api::get_current_dir); - bettola_api.set_function("cd", &api::cd); - bettola_api.set_function("scp", &api::scp); - bettola_api.set_function("close_terminal", &api::close_terminal); - bettola_api.set_function("ssh", &api::ssh); - bettola_api.set_function("nmap", &api::nmap); - bettola_api.set_function("disconnect", &api::disconnect); + bettola_api.set_function("rm", &api::rm); + bettola_api.set_function("ls", &api::ls); + bettola_api.set_function("write_file", &api::write_file); + bettola_api.set_function("create_executable", &api::create_executable); + bettola_api.set_function("get_current_dir", &api::get_current_dir); + bettola_api.set_function("cd", &api::cd); + bettola_api.set_function("scp", &api::scp); + bettola_api.set_function("close_terminal", &api::close_terminal); + bettola_api.set_function("ssh", &api::ssh); + bettola_api.set_function("nmap", &api::nmap); + bettola_api.set_function("disconnect", &api::disconnect); } LuaProcessor::~LuaProcessor(void) {} diff --git a/common/src/session.cpp b/common/src/session.cpp index 08e7cf5..3bb1e32 100644 --- a/common/src/session.cpp +++ b/common/src/session.cpp @@ -93,23 +93,36 @@ std::string Session::process_command(const std::string& command) { args.push_back(arg); } - /* Find the /bin directory in the current machine's VFS. */ - vfs_node* bin_dir = find_node_by_path(_session_machine->vfs_root, "/bin"); - if(bin_dir && bin_dir->type == DIR_NODE) { - auto it = bin_dir->children.find(command_name); - if(it != bin_dir->children.end()) { - vfs_node* command_node = it->second; - if(command_node->type == EXEC_NODE) { - /* Execute the script. */ - bool is_remote = (_session_machine != _home_machine); - sol::object result = lua.execute(command_node->content, *this, args, is_remote); - return result.is() ? result.as() - : "[Script returned an unexpected type]"; + vfs_node* command_node = nullptr; + if(command_name.find('/') != std::string::npos) { + /* Path provided, find node directly. */ + command_node = find_node_by_path(_session_machine->vfs_root, command_name); + } else { + /* No path, search current dir then /bin. */ + vfs_node* current_dir = get_current_dir(); + if(current_dir->children.count(command_name)) { + command_node = current_dir->children.at(command_name); + } else { + vfs_node* bin_dir = find_node_by_path(_session_machine->vfs_root, "/bin"); + if(bin_dir && bin_dir->children.count(command_name)) { + command_node = bin_dir->children.at(command_name); } } } - return "Unknown command: " + command_name + "\n"; + if(command_node) { + if(command_node->type == EXEC_NODE) { + bool is_remote = (_session_machine != _home_machine); + sol::object result = lua.execute(command_node->content, *this, args, is_remote); + return result.is() ? result.as() + : "[Script returned non-string value]"; + } else if(command_node->type == DIR_NODE) { + return "Cannot execute '" + command_name + "' It is a directory."; + } else { + return "Cannot execute '" + command_name + "': Not an executable file."; + } + } + return "Unknown command: " + command_name; } std::string Session::write_file(const std::string& path, const std::string& content) {