22
33#include < iostream>
44#include < unordered_map>
5+ #include < unordered_set>
56#include < vector>
7+ #include < fstream>
8+ #include < sstream>
9+ #include < filesystem>
10+ #include < algorithm>
611
712#include " options.h"
813#include " utils.h"
914#include " VoidScript.hpp"
1015#include " Symbols/SymbolContainer.hpp"
1116
17+ // Struct to hold module information
18+ struct ModuleInfo {
19+ std::string name;
20+ std::string path;
21+ std::string description;
22+ };
23+
1224// Supported command-line parameters and descriptions
1325const std::unordered_map<std::string, std::string> params = {
1426 { " --help" , " Print this help message" },
@@ -17,10 +29,60 @@ const std::unordered_map<std::string, std::string> params = {
1729 { " --enable-tags" , " Only parse tokens between PARSER_OPEN_TAG and PARSER_CLOSE_TAG when enabled" },
1830 { " --suppress-tags-outside" ,
1931 " Suppress text outside PARSER_OPEN_TAG/PARSER_CLOSE_TAG when tag filtering is enabled" },
20- { " -m, --modules" , " List loaded modules" },
32+ { " -m, --modules" , " List loaded modules with detailed information" },
33+ { " --module-info" , " Display detailed information about a specific module" },
2134 { " -c, --command" , " Execute script string instead of reading from file" },
2235};
2336
37+ /* *
38+ * @brief Scan Modules/ directory for external modules
39+ * @return Vector of external ModuleInfo
40+ */
41+ static std::vector<ModuleInfo> scanExternalModules () {
42+ std::vector<ModuleInfo> modules;
43+
44+ // Determine modules directory (similar to VoidScript::loadPlugins)
45+ std::string modulesPath = " ./Modules" ;
46+
47+ // Try to find the modules directory relative to executable if possible
48+ char exePath[1024 ];
49+ ssize_t count = readlink (" /proc/self/exe" , exePath, sizeof (exePath));
50+ if (count != -1 ) {
51+ exePath[count] = ' \0 ' ;
52+ std::string binPath (exePath);
53+ size_t lastSlash = binPath.find_last_of (" /\\ " );
54+ std::string binDir = (lastSlash != std::string::npos) ? binPath.substr (0 , lastSlash) : " ." ;
55+ std::string altPath = binDir + " /Modules" ;
56+ if (utils::exists (altPath) && utils::is_directory (altPath)) {
57+ modulesPath = altPath;
58+ }
59+ }
60+
61+ // Scan modules directory if it exists
62+ if (utils::exists (modulesPath) && utils::is_directory (modulesPath)) {
63+ for (const auto & entry : std::filesystem::directory_iterator (modulesPath)) {
64+ if (entry.is_directory ()) {
65+ std::string module_name = entry.path ().filename ().string ();
66+ std::string lowercase_name = module_name;
67+ std::transform (lowercase_name.begin (), lowercase_name.end (), lowercase_name.begin (), ::tolower);
68+ std::string lib_name = " libvoidscript-module-" + lowercase_name + " .so" ;
69+ std::string module_path = modulesPath + " /" + module_name + " /" + lib_name;
70+
71+ // Try to get description from SymbolContainer (will be available after modules are loaded)
72+ auto symbolContainer = Symbols::SymbolContainer::instance ();
73+ std::string description = symbolContainer->getModuleDescription (module_name);
74+ if (description.empty ()) {
75+ description = " No description available." ;
76+ }
77+
78+ modules.push_back ({module_name, module_path, description});
79+ }
80+ }
81+ }
82+
83+ return modules;
84+ }
85+
2486int main (int argc, char * argv[]) {
2587 std::string usage = " Usage: " + std::string (argv[0 ]);
2688 for (const auto & [key, value] : params) {
@@ -87,34 +149,104 @@ int main(int argc, char * argv[]) {
87149 suppressTagsOutside = true ;
88150 } else if (a == " -m" || a == " --modules" ) {
89151 VoidScript voidscript (" modules" , false , false , false , false , false , false , std::vector<std::string>{});
90- auto modules = Symbols::SymbolContainer::instance ()->getModuleNames ();
152+ auto symbolContainer = Symbols::SymbolContainer::instance ();
153+ auto moduleNames = symbolContainer->getModuleNames ();
91154
92- // Define external modules based on their location in ./Modules/
93- const std::vector<std::string> externalModules = {" Curl" , " Format" , " Imagick" , " MariaDb" , " Xml2" };
155+ // Scan external modules from directory
156+ std::vector<ModuleInfo> externalModules = scanExternalModules ();
157+ std::unordered_set<std::string> externalModuleNames;
158+ for (const auto & mod : externalModules) {
159+ externalModuleNames.insert (mod.name );
160+ }
94161
95- // Separate modules into built-in and external
96- std::vector<std::string> builtin, external;
97- for (const auto & module : modules) {
98- if (std::find (externalModules.begin (), externalModules.end (), module ) != externalModules.end ()) {
99- external.push_back (module );
162+ // Separate modules into built-in and external ModuleInfo
163+ std::vector<ModuleInfo> builtin, external;
164+ for (const auto & name : moduleNames) {
165+ std::string description = symbolContainer->getModuleDescription (name);
166+ if (description.empty ()) {
167+ description = " No description available." ;
168+ }
169+ if (externalModuleNames.count (name)) {
170+ // Get path from external modules
171+ for (const auto & extMod : externalModules) {
172+ if (extMod.name == name) {
173+ external.push_back ({name, extMod.path , description});
174+ break ;
175+ }
176+ }
100177 } else {
101- builtin.push_back (module );
178+ builtin.push_back ({name, " " , description} );
102179 }
103180 }
104181
105182 // Print built-in modules
106183 std::cout << " Built-in modules:\n " ;
107184 for (const auto & module : builtin) {
108- std::cout << " " << module << " \n " ;
185+ std::cout << " Name: " << module .name << " \n " ;
186+ std::cout << " Path: " << (module .path .empty () ? " (built-in)" : module .path ) << " \n " ;
187+ std::cout << " Description: " << module .description << " \n " ;
188+ std::cout << " \n " ;
109189 }
110190 std::cout << " \n " ;
111191
112192 // Print external modules
113193 std::cout << " External modules:\n " ;
114194 for (const auto & module : external) {
115- std::cout << " " << module << " \n " ;
195+ std::cout << " Name: " << module .name << " \n " ;
196+ std::cout << " Path: " << module .path << " \n " ;
197+ std::cout << " Description: " << module .description << " \n " ;
198+ std::cout << " \n " ;
116199 }
117200 return 0 ;
201+ } else if (a == " --module-info" ) {
202+ // Next argument should be the module name
203+ if (i + 1 >= argc) {
204+ std::cerr << " Error: --module-info requires a module name\n " ;
205+ std::cerr << usage << " \n " ;
206+ return 1 ;
207+ }
208+ std::string moduleName = argv[++i];
209+
210+ VoidScript voidscript (" module-info" , false , false , false , false , false , false , std::vector<std::string>{});
211+ auto symbolContainer = Symbols::SymbolContainer::instance ();
212+ auto moduleNames = symbolContainer->getModuleNames ();
213+
214+ // Check if the module exists
215+ if (std::find (moduleNames.begin (), moduleNames.end (), moduleName) != moduleNames.end ()) {
216+ std::string description = symbolContainer->getModuleDescription (moduleName);
217+ if (description.empty ()) {
218+ description = " No description available." ;
219+ }
220+
221+ std::cout << " Module Information:\n " ;
222+ std::cout << " Name: " << moduleName << " \n " ;
223+
224+ // Check if external
225+ std::vector<ModuleInfo> externalModules = scanExternalModules ();
226+ bool isExternal = false ;
227+ std::string path = " " ;
228+ for (const auto & mod : externalModules) {
229+ if (mod.name == moduleName) {
230+ path = mod.path ;
231+ isExternal = true ;
232+ break ;
233+ }
234+ }
235+
236+ if (isExternal) {
237+ std::cout << " Path: " << path << " \n " ;
238+ std::cout << " Type: External\n " ;
239+ } else {
240+ std::cout << " Path: (built-in)\n " ;
241+ std::cout << " Type: Built-in\n " ;
242+ }
243+ std::cout << " Description: " << description << " \n " ;
244+ return 0 ;
245+ }
246+
247+ // Not found
248+ std::cerr << " Error: Module '" << moduleName << " ' not found.\n " ;
249+ return 1 ;
118250 } else if (a == " -c" || a == " --command" ) {
119251 // Next argument should be the script content
120252 if (i + 1 >= argc) {
0 commit comments