From c91e935c62b8e254b9daadf37b915c983518bff4 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Fri, 18 Apr 2025 16:25:35 +0000
Subject: [PATCH] add dynamic module load, more escape seq

---
 src/Modules/ModuleManager.hpp |  134 ++++++++++++++++++++++++++++++++++++++------
 1 files changed, 114 insertions(+), 20 deletions(-)

diff --git a/src/Modules/ModuleManager.hpp b/src/Modules/ModuleManager.hpp
index c694493..03b0a12 100644
--- a/src/Modules/ModuleManager.hpp
+++ b/src/Modules/ModuleManager.hpp
@@ -2,14 +2,25 @@
 #ifndef MODULES_MODULEMANAGER_HPP
 #define MODULES_MODULEMANAGER_HPP
 
-#include <memory>
-#include <vector>
-#include "BaseModule.hpp"
+#include <filesystem>
 #include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
 #include <unordered_map>
+#include <vector>
+
+#include "BaseModule.hpp"
 #include "Symbols/Value.hpp"
 
+#ifndef _WIN32
+#    include <dlfcn.h>
+#else
+#    include <windows.h>
+#endif
+
 namespace Modules {
+using CallbackFunction = std::function<Symbols::Value(const std::vector<Symbols::Value> &)>;
 
 /**
  * @brief Manager for registering and invoking modules.
@@ -19,7 +30,7 @@
     /**
      * @brief Get singleton instance of ModuleManager.
      */
-    static ModuleManager &instance() {
+    static ModuleManager & instance() {
         static ModuleManager mgr;
         return mgr;
     }
@@ -28,55 +39,138 @@
      * @brief Add a module to the manager.
      * @param module Unique pointer to a BaseModule.
      */
-    void addModule(std::unique_ptr<BaseModule> module) {
-        modules_.push_back(std::move(module));
-    }
+    void addModule(std::unique_ptr<BaseModule> module) { modules_.push_back(std::move(module)); }
 
     /**
      * @brief Invoke all registered modules to register their symbols.
      */
     void registerAll() {
-        for (const auto &module : modules_) {
+        for (const auto & module : modules_) {
             module->registerModule();
         }
     }
 
   private:
     ModuleManager() = default;
-    std::vector<std::unique_ptr<BaseModule>> modules_;
+    std::vector<std::unique_ptr<BaseModule>>                                                            modules_;
     // Built-in function callbacks: name -> function
-    std::unordered_map<std::string,
-        std::function<Symbols::Value(const std::vector<Symbols::Value>&)>> callbacks_;
+    std::unordered_map<std::string, std::function<Symbols::Value(const std::vector<Symbols::Value> &)>> callbacks_;
+    // Plugin handles for dynamically loaded modules
+    std::vector<void *>                                                                                 pluginHandles_;
   public:
     /**
      * @brief Register a built-in function callback.
      * @param name Name of the function.
      * @param cb Callable taking argument values and returning a Value.
      */
-    void registerFunction(const std::string &name,
-                          std::function<Symbols::Value(const std::vector<Symbols::Value>&)> cb) {
+    void registerFunction(const std::string &                                                name,
+                          std::function<Symbols::Value(const std::vector<Symbols::Value> &)> cb) {
         callbacks_[name] = std::move(cb);
     }
 
     /**
      * @brief Check if a built-in function is registered.
      */
-    bool hasFunction(const std::string &name) const {
-        return callbacks_.find(name) != callbacks_.end();
-    }
+    bool hasFunction(const std::string & name) const { return callbacks_.find(name) != callbacks_.end(); }
 
     /**
      * @brief Call a built-in function callback.
      */
-    Symbols::Value callFunction(const std::string &name,
-                                const std::vector<Symbols::Value> &args) const {
+    Symbols::Value callFunction(const std::string & name, const std::vector<Symbols::Value> & args) const {
         auto it = callbacks_.find(name);
         if (it == callbacks_.end()) {
             throw std::runtime_error("Built-in function callback not found: " + name);
         }
         return it->second(args);
     }
+
+    /**
+     * @brief Load all plugin modules from specified directory.
+     * @param directory Path to directory containing plugin shared libraries.
+     */
+    void loadPlugins(const std::string & directory) {
+        namespace fs = std::filesystem;
+        if (!fs::exists(directory) || !fs::is_directory(directory)) {
+            return;
+        }
+        // Recursively search for plugin shared libraries
+        for (const auto & entry : fs::recursive_directory_iterator(directory)) {
+            if (!entry.is_regular_file()) {
+                continue;
+            }
+#ifdef _WIN32
+            if (entry.path().extension() == ".dll") {
+#else
+            if (entry.path().extension() == ".so") {
+#endif
+                loadPlugin(entry.path().string());
+            }
+        }
+    }
+
+    /**
+     * @brief Load a single plugin module from shared library.
+     * @param path Filesystem path to the shared library.
+     */
+    void loadPlugin(const std::string & path) {
+#ifndef _WIN32
+        void * handle = dlopen(path.c_str(), RTLD_NOW);
+        if (!handle) {
+            throw std::runtime_error("Failed to load module: " + path + ": " + dlerror());
+        }
+#else
+        HMODULE handle = LoadLibraryA(path.c_str());
+        if (!handle) {
+            throw std::runtime_error("Failed to load module: " + path);
+        }
+#endif
+        pluginHandles_.push_back(handle);
+
+#ifndef _WIN32
+        dlerror();  // clear any existing error
+        using PluginInitFunc    = void (*)();
+        auto         initFunc   = reinterpret_cast<PluginInitFunc>(dlsym(handle, "plugin_init"));
+        const char * dlsymError = dlerror();
+        if (dlsymError) {
+            dlclose(handle);
+            pluginHandles_.pop_back();
+            throw std::runtime_error("Cannot find symbol 'plugin_init' in " + path + ": " + dlsymError);
+        }
+        initFunc();
+#else
+        using PluginInitFunc = void(__cdecl *)();
+        auto initFunc        = reinterpret_cast<PluginInitFunc>(GetProcAddress(handle, "plugin_init"));
+        if (!initFunc) {
+            FreeLibrary(handle);
+            pluginHandles_.pop_back();
+            throw std::runtime_error("Cannot find symbol 'plugin_init' in " + path);
+        }
+        initFunc();
+#endif
+    }
+
+    /**
+     * @brief Destructor unloads modules and plugin libraries in safe order.
+     * Modules (and their callback functions) are destroyed before the libraries are unloaded,
+     * ensuring that destructors (in plugin code) run while the libraries are still mapped.
+     */
+    ~ModuleManager() {
+        // Destroy module instances (may call code in plugin libraries)
+        modules_.clear();
+        // Clear callback functions (may refer to plugin code)
+        callbacks_.clear();
+        // Unload all dynamically loaded plugin libraries
+        for (auto handle : pluginHandles_) {
+#ifndef _WIN32
+            dlclose(handle);
+#else
+            FreeLibrary((HMODULE) handle);
+#endif
+        }
+        // Clear handles
+        pluginHandles_.clear();
+    }
 };
 
-} // namespace Modules
-#endif // MODULES_MODULEMANAGER_HPP
\ No newline at end of file
+}  // namespace Modules
+#endif  // MODULES_MODULEMANAGER_HPP

--
Gitblit v1.9.3