77#include < iostream>
88#include < vector>
99#include < string>
10+ #include < unistd.h>
11+ #include < fcntl.h>
1012
1113#include " logos_module.h"
1214#include " module_metadata.h"
@@ -18,29 +20,67 @@ QTextStream err(stderr);
1820
1921const char * VERSION = " 0.1.0" ;
2022
23+ // Global flag for debug mode
24+ static bool g_debugMode = false ;
25+
26+ // Custom Qt message handler to suppress debug/info messages unless in debug mode
27+ void customMessageHandler (QtMsgType type, const QMessageLogContext& context, const QString& msg) {
28+ if (!g_debugMode && (type == QtDebugMsg || type == QtInfoMsg)) {
29+ // Suppress debug and info messages when not in debug mode
30+ return ;
31+ }
32+
33+ // For warnings and errors, or when in debug mode, use default handling
34+ QByteArray localMsg = msg.toLocal8Bit ();
35+ const char * file = context.file ? context.file : " " ;
36+ const char * function = context.function ? context.function : " " ;
37+
38+ switch (type) {
39+ case QtDebugMsg:
40+ fprintf (stderr, " Debug: %s (%s:%u, %s)\n " , localMsg.constData (), file, context.line , function);
41+ break ;
42+ case QtInfoMsg:
43+ fprintf (stderr, " Info: %s (%s:%u, %s)\n " , localMsg.constData (), file, context.line , function);
44+ break ;
45+ case QtWarningMsg:
46+ fprintf (stderr, " Warning: %s (%s:%u, %s)\n " , localMsg.constData (), file, context.line , function);
47+ break ;
48+ case QtCriticalMsg:
49+ fprintf (stderr, " Critical: %s (%s:%u, %s)\n " , localMsg.constData (), file, context.line , function);
50+ break ;
51+ case QtFatalMsg:
52+ fprintf (stderr, " Fatal: %s (%s:%u, %s)\n " , localMsg.constData (), file, context.line , function);
53+ abort ();
54+ }
55+ }
56+
2157void printVersion () {
2258 out << " lm (Logos Module) version " << VERSION << Qt::endl;
2359}
2460
2561void printUsage () {
2662 out << " lm - Logos Module Inspector\n "
2763 << " \n "
28- << " Usage: lm < command> [options] <plugin-path>\n "
64+ << " Usage: lm [ command] [options] <plugin-path>\n "
2965 << " \n "
3066 << " Commands:\n "
67+ << " (default) Show both metadata and methods (when no command specified)\n "
3168 << " metadata Show plugin metadata (name, version, description, etc.)\n "
3269 << " methods Show plugin methods and signatures\n "
3370 << " \n "
3471 << " Options:\n "
3572 << " --json Output in JSON format\n "
73+ << " --debug Show debug output from plugin loading\n "
3674 << " --help, -h Show help information\n "
3775 << " --version, -v Show version information\n "
3876 << " \n "
3977 << " Examples:\n "
78+ << " lm /path/to/plugin.so\n "
79+ << " lm /path/to/plugin.so --json\n "
4080 << " lm metadata /path/to/plugin.so\n "
4181 << " lm methods /path/to/plugin.so\n "
4282 << " lm metadata /path/to/plugin.so --json\n "
43- << " lm methods /path/to/plugin.so --json\n " ;
83+ << " lm methods /path/to/plugin.so --json --debug \n " ;
4484}
4585
4686void printCommandHelp (const QString& command) {
@@ -51,15 +91,17 @@ void printCommandHelp(const QString& command) {
5191 << " type, and dependencies.\n "
5292 << " \n "
5393 << " Options:\n "
54- << " --json Output in JSON format\n " ;
94+ << " --json Output in JSON format\n "
95+ << " --debug Show debug output from plugin loading\n " ;
5596 } else if (command == " methods" ) {
5697 out << " Usage: lm methods [options] <plugin-path>\n "
5798 << " \n "
5899 << " Show all methods exposed by the plugin via Qt's meta-object system.\n "
59100 << " Displays method name, signature, return type, and parameters.\n "
60101 << " \n "
61102 << " Options:\n "
62- << " --json Output in JSON format\n " ;
103+ << " --json Output in JSON format\n "
104+ << " --debug Show debug output from plugin loading\n " ;
63105 }
64106}
65107
@@ -137,26 +179,22 @@ int cmdMetadata(const QString& pluginPath, bool jsonOutput) {
137179 return 1 ;
138180 }
139181
140- QString errorString;
141- LogosModule plugin = LogosModule::loadFromPath (absolutePath, &errorString);
142-
143- if (!plugin.isValid ()) {
144- err << " Error: Failed to load plugin: " << errorString << Qt::endl;
182+ auto metadata = LogosModule::extractMetadata (absolutePath);
183+ if (!metadata) {
184+ err << " Error: Failed to extract metadata from: " << pluginPath << Qt::endl;
145185 return 1 ;
146186 }
147187
148- const ModuleMetadata& metadata = plugin.metadata ();
149-
150188 if (jsonOutput) {
151- printMetadataJson (metadata);
189+ printMetadataJson (* metadata);
152190 } else {
153- printMetadataHuman (metadata);
191+ printMetadataHuman (* metadata);
154192 }
155193
156194 return 0 ;
157195}
158196
159- int cmdMethods (const QString& pluginPath, bool jsonOutput) {
197+ int cmdMethods (const QString& pluginPath, bool jsonOutput, bool debugOutput ) {
160198 QFileInfo fileInfo (pluginPath);
161199 QString absolutePath = fileInfo.absoluteFilePath ();
162200
@@ -166,7 +204,36 @@ int cmdMethods(const QString& pluginPath, bool jsonOutput) {
166204 }
167205
168206 QString errorString;
169- LogosModule plugin = LogosModule::loadFromPath (absolutePath, &errorString);
207+ LogosModule plugin;
208+
209+ if (!debugOutput) {
210+ // Redirect stdout and stderr to /dev/null during plugin loading
211+ int stdout_copy = dup (STDOUT_FILENO);
212+ int stderr_copy = dup (STDERR_FILENO);
213+
214+ int devnull = open (" /dev/null" , O_WRONLY);
215+ if (devnull != -1 ) {
216+ dup2 (devnull, STDOUT_FILENO);
217+ dup2 (devnull, STDERR_FILENO);
218+ close (devnull);
219+ }
220+
221+ // Load the plugin (this may trigger constructor output)
222+ plugin = LogosModule::loadFromPath (absolutePath, &errorString);
223+
224+ // Restore stdout and stderr
225+ if (stdout_copy != -1 ) {
226+ dup2 (stdout_copy, STDOUT_FILENO);
227+ close (stdout_copy);
228+ }
229+ if (stderr_copy != -1 ) {
230+ dup2 (stderr_copy, STDERR_FILENO);
231+ close (stderr_copy);
232+ }
233+ } else {
234+ // Debug mode: load normally without suppression
235+ plugin = LogosModule::loadFromPath (absolutePath, &errorString);
236+ }
170237
171238 if (!plugin.isValid ()) {
172239 err << " Error: Failed to load plugin: " << errorString << Qt::endl;
@@ -188,9 +255,100 @@ int cmdMethods(const QString& pluginPath, bool jsonOutput) {
188255 return 0 ;
189256}
190257
258+ int cmdInfo (const QString& pluginPath, bool jsonOutput, bool debugOutput) {
259+ QFileInfo fileInfo (pluginPath);
260+ QString absolutePath = fileInfo.absoluteFilePath ();
261+
262+ if (!fileInfo.exists ()) {
263+ err << " Error: Plugin file not found: " << pluginPath << Qt::endl;
264+ return 1 ;
265+ }
266+
267+ if (jsonOutput) {
268+ // For JSON output, combine metadata and methods into a single object
269+ auto metadata = LogosModule::extractMetadata (absolutePath);
270+ if (!metadata) {
271+ err << " Error: Failed to extract metadata from: " << pluginPath << Qt::endl;
272+ return 1 ;
273+ }
274+
275+ // Load plugin for methods
276+ QString errorString;
277+ LogosModule plugin;
278+
279+ if (!debugOutput) {
280+ // Suppress output during loading
281+ int stdout_copy = dup (STDOUT_FILENO);
282+ int stderr_copy = dup (STDERR_FILENO);
283+
284+ int devnull = open (" /dev/null" , O_WRONLY);
285+ if (devnull != -1 ) {
286+ dup2 (devnull, STDOUT_FILENO);
287+ dup2 (devnull, STDERR_FILENO);
288+ close (devnull);
289+ }
290+
291+ plugin = LogosModule::loadFromPath (absolutePath, &errorString);
292+
293+ if (stdout_copy != -1 ) {
294+ dup2 (stdout_copy, STDOUT_FILENO);
295+ close (stdout_copy);
296+ }
297+ if (stderr_copy != -1 ) {
298+ dup2 (stderr_copy, STDERR_FILENO);
299+ close (stderr_copy);
300+ }
301+ } else {
302+ plugin = LogosModule::loadFromPath (absolutePath, &errorString);
303+ }
304+
305+ if (!plugin.isValid ()) {
306+ err << " Error: Failed to load plugin: " << errorString << Qt::endl;
307+ return 1 ;
308+ }
309+
310+ // Build combined JSON object
311+ QJsonObject combined;
312+
313+ QJsonObject metadataObj;
314+ metadataObj[" name" ] = metadata->name ;
315+ metadataObj[" version" ] = metadata->version ;
316+ metadataObj[" description" ] = metadata->description ;
317+ metadataObj[" author" ] = metadata->author ;
318+ metadataObj[" type" ] = metadata->type ;
319+
320+ QJsonArray deps;
321+ for (const QString& dep : metadata->dependencies ) {
322+ deps.append (dep);
323+ }
324+ metadataObj[" dependencies" ] = deps;
325+
326+ combined[" metadata" ] = metadataObj;
327+ combined[" methods" ] = LogosModule::getMethodsAsJson (plugin.instance ());
328+
329+ QJsonDocument doc (combined);
330+ out << doc.toJson (QJsonDocument::Indented);
331+ } else {
332+ // For human-readable output, print metadata then methods with a separator
333+ int result = cmdMetadata (pluginPath, false );
334+ if (result != 0 ) {
335+ return result;
336+ }
337+
338+ out << " \n " ;
339+
340+ return cmdMethods (pluginPath, false , debugOutput);
341+ }
342+
343+ return 0 ;
344+ }
345+
191346int main (int argc, char * argv[]) {
192347 QCoreApplication app (argc, argv);
193348
349+ // Install custom message handler to suppress debug output by default
350+ qInstallMessageHandler (customMessageHandler);
351+
194352 std::vector<std::string> args;
195353 for (int i = 1 ; i < argc; ++i) {
196354 args.push_back (argv[i]);
@@ -213,24 +371,40 @@ int main(int argc, char* argv[]) {
213371 return 0 ;
214372 }
215373
216- std::string command = firstArg;
374+ std::string command;
375+ bool defaultMode = false ;
217376 bool jsonOutput = false ;
377+ bool debugOutput = false ;
218378 QString pluginPath;
219379
220- if (command != " metadata" && command != " methods" ) {
221- err << " Error: Unknown command '" << QString::fromStdString (command) << " '\n "
222- << " \n Run 'lm --help' to see available commands.\n " ;
380+ // Check if first arg is a command or a plugin path
381+ if (firstArg == " metadata" || firstArg == " methods" ) {
382+ command = firstArg;
383+ } else if (firstArg[0 ] != ' -' ) {
384+ // First arg is not a command and not an option, treat as plugin path
385+ defaultMode = true ;
386+ pluginPath = QString::fromStdString (firstArg);
387+ } else {
388+ err << " Error: Unknown option '" << QString::fromStdString (firstArg) << " '" << Qt::endl;
223389 return 1 ;
224390 }
225391
226- for (size_t i = 1 ; i < args.size (); ++i) {
392+ // Parse remaining arguments
393+ size_t startIdx = defaultMode ? 1 : 1 ; // Start after command or plugin path
394+ for (size_t i = startIdx; i < args.size (); ++i) {
227395 std::string arg = args[i];
228396
229397 if (arg == " --help" || arg == " -h" ) {
230- printCommandHelp (QString::fromStdString (command));
398+ if (defaultMode) {
399+ printUsage ();
400+ } else {
401+ printCommandHelp (QString::fromStdString (command));
402+ }
231403 return 0 ;
232404 } else if (arg == " --json" ) {
233405 jsonOutput = true ;
406+ } else if (arg == " --debug" ) {
407+ debugOutput = true ;
234408 } else if (arg[0 ] == ' -' ) {
235409 err << " Error: Unknown option '" << QString::fromStdString (arg) << " '" << Qt::endl;
236410 return 1 ;
@@ -246,14 +420,23 @@ int main(int argc, char* argv[]) {
246420
247421 if (pluginPath.isEmpty ()) {
248422 err << " Error: Missing plugin path" << Qt::endl;
249- err << " \n Usage: lm " << QString::fromStdString (command) << " [options] <plugin-path>" << Qt::endl;
423+ if (defaultMode) {
424+ err << " \n Usage: lm [options] <plugin-path>" << Qt::endl;
425+ } else {
426+ err << " \n Usage: lm " << QString::fromStdString (command) << " [options] <plugin-path>" << Qt::endl;
427+ }
250428 return 1 ;
251429 }
252430
253- if (command == " metadata" ) {
431+ // Set global debug flag
432+ g_debugMode = debugOutput;
433+
434+ if (defaultMode) {
435+ return cmdInfo (pluginPath, jsonOutput, debugOutput);
436+ } else if (command == " metadata" ) {
254437 return cmdMetadata (pluginPath, jsonOutput);
255438 } else if (command == " methods" ) {
256- return cmdMethods (pluginPath, jsonOutput);
439+ return cmdMethods (pluginPath, jsonOutput, debugOutput );
257440 }
258441
259442 return 0 ;
0 commit comments