00e27f7d60e95b40234f5b28e360ce3635c38268
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinemodulemanager.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013, by the GROMACS development team, led by
5  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6  * others, as listed in the AUTHORS file in the top-level source
7  * directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements gmx::CommandLineModuleManager.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_commandline
41  */
42 #include "cmdlinemodulemanager.h"
43
44 #include <cstdio>
45
46 #include <map>
47 #include <string>
48 #include <utility>
49
50 #include <boost/scoped_ptr.hpp>
51
52 #include "gromacs/legacyheaders/copyrite.h"
53 #include "gromacs/legacyheaders/network.h"
54
55 #include "gromacs/commandline/cmdlinehelpcontext.h"
56 #include "gromacs/commandline/cmdlinemodule.h"
57 #include "gromacs/commandline/cmdlineparser.h"
58 #include "gromacs/onlinehelp/helpformat.h"
59 #include "gromacs/onlinehelp/helpmanager.h"
60 #include "gromacs/onlinehelp/helptopic.h"
61 #include "gromacs/onlinehelp/helpwritercontext.h"
62 #include "gromacs/options/basicoptions.h"
63 #include "gromacs/options/options.h"
64 #include "gromacs/utility/exceptions.h"
65 #include "gromacs/utility/file.h"
66 #include "gromacs/utility/gmxassert.h"
67 #include "gromacs/utility/init.h"
68 #include "gromacs/utility/programinfo.h"
69 #include "gromacs/utility/stringutil.h"
70
71 namespace gmx
72 {
73
74 //! Container type for mapping module names to module objects.
75 typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
76
77 class CommandLineHelpModule;
78
79 namespace
80 {
81
82 /********************************************************************
83  * RootHelpTopic
84  */
85
86 struct RootHelpText
87 {
88     static const char        name[];
89     static const char        title[];
90     static const char *const text[];
91 };
92
93 // The first two are not used.
94 const char        RootHelpText::name[]  = "";
95 const char        RootHelpText::title[] = "";
96 const char *const RootHelpText::text[]  = {
97     "Usage: [PROGRAM] <command> [<args>]",
98 };
99
100 /*! \internal \brief
101  * Help topic that forms the root of the help tree for the help subcommand.
102  *
103  * \ingroup module_commandline
104  */
105 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
106 {
107     public:
108         /*! \brief
109          * Creates a root help topic.
110          *
111          * \param[in] modules  List of modules for to use for module listings.
112          *
113          * Does not throw.
114          */
115         explicit RootHelpTopic(const CommandLineModuleMap &modules)
116             : modules_(modules)
117         {
118         }
119
120         virtual void writeHelp(const HelpWriterContext &context) const;
121
122     private:
123         void printModuleList(const HelpWriterContext &context) const;
124
125         const CommandLineModuleMap &modules_;
126
127         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
128 };
129
130 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
131 {
132     if (context.outputFormat() != eHelpOutputFormat_Console)
133     {
134         // TODO: Implement once the situation with Redmine issue #969 is more
135         // clear.
136         GMX_THROW(NotImplementedError(
137                           "Root help is not implemented for this output format"));
138     }
139     writeBasicHelpTopic(context, *this, helpText());
140     // TODO: If/when this list becomes long, it may be better to only print
141     // "common" commands here, and have a separate topic (e.g.,
142     // "help commands") that prints the full list.
143     printModuleList(context);
144     context.writeTextBlock(
145             "For additional help on a command, use '[PROGRAM] help <command>'");
146     writeSubTopicList(context,
147                       "\nAdditional help is available on the following topics:");
148     context.writeTextBlock(
149             "To access the help, use '[PROGRAM] help <topic>'.");
150 }
151
152 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
153 {
154     if (context.outputFormat() != eHelpOutputFormat_Console)
155     {
156         // TODO: Implement once the situation with Redmine issue #969 is more
157         // clear.
158         GMX_THROW(NotImplementedError(
159                           "Module list is not implemented for this output format"));
160     }
161     int maxNameLength = 0;
162     CommandLineModuleMap::const_iterator module;
163     for (module = modules_.begin(); module != modules_.end(); ++module)
164     {
165         int nameLength = static_cast<int>(module->first.length());
166         if (module->second->shortDescription() != NULL
167             && nameLength > maxNameLength)
168         {
169             maxNameLength = nameLength;
170         }
171     }
172     File              &file = context.outputFile();
173     TextTableFormatter formatter;
174     formatter.addColumn(NULL, maxNameLength + 1, false);
175     formatter.addColumn(NULL, 72 - maxNameLength, true);
176     formatter.setFirstColumnIndent(4);
177     file.writeLine();
178     file.writeLine("Available commands:");
179     for (module = modules_.begin(); module != modules_.end(); ++module)
180     {
181         const char *name        = module->first.c_str();
182         const char *description = module->second->shortDescription();
183         if (description != NULL)
184         {
185             formatter.clear();
186             formatter.addColumnLine(0, name);
187             formatter.addColumnLine(1, description);
188             file.writeString(formatter.formatRow());
189         }
190     }
191 }
192
193 /********************************************************************
194  * ModuleHelpTopic declaration
195  */
196
197 /*! \internal \brief
198  * Help topic wrapper for a command-line module.
199  *
200  * This class implements HelpTopicInterface such that it wraps a
201  * CommandLineModuleInterface, allowing subcommand "help <command>"
202  * to produce the help for "<command>".
203  *
204  * \ingroup module_commandline
205  */
206 class ModuleHelpTopic : public HelpTopicInterface
207 {
208     public:
209         //! Constructs a help topic for a specific module.
210         ModuleHelpTopic(const CommandLineModuleInterface &module,
211                         const CommandLineHelpModule      &helpModule)
212             : module_(module), helpModule_(helpModule)
213         {
214         }
215
216         virtual const char *name() const { return module_.name(); }
217         virtual const char *title() const { return NULL; }
218         virtual bool hasSubTopics() const { return false; }
219         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
220         {
221             return NULL;
222         }
223         virtual void writeHelp(const HelpWriterContext &context) const;
224
225     private:
226         const CommandLineModuleInterface &module_;
227         const CommandLineHelpModule      &helpModule_;
228
229         GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
230 };
231
232 /********************************************************************
233  * HelpExportInterface
234  */
235
236 /*! \internal \brief
237  * Callbacks for exporting help information for command-line modules.
238  *
239  * \ingroup module_commandline
240  */
241 class HelpExportInterface
242 {
243     public:
244         virtual ~HelpExportInterface() {};
245
246         /*! \brief
247          * Called to export the help for each module.
248          *
249          * \param[in] tag     Unique tag for the module (gmx-something).
250          * \param[in] module  Module for which the help should be exported.
251          */
252         virtual void exportModuleHelp(const std::string                &tag,
253                                       const CommandLineModuleInterface &module) = 0;
254 };
255
256 }   // namespace
257
258 /********************************************************************
259  * CommandLineHelpModule
260  */
261
262 /*! \internal \brief
263  * Command-line module for producing help.
264  *
265  * This module implements the 'help' subcommand that is automatically added by
266  * CommandLineModuleManager.
267  *
268  * \ingroup module_commandline
269  */
270 class CommandLineHelpModule : public CommandLineModuleInterface
271 {
272     public:
273         /*! \brief
274          * Creates a command-line help module.
275          *
276          * \param[in] modules  List of modules for to use for module listings.
277          * \throws    std::bad_alloc if out of memory.
278          */
279         explicit CommandLineHelpModule(const CommandLineModuleMap &modules);
280
281         /*! \brief
282          * Adds a top-level help topic.
283          *
284          * \param[in] topic  Help topic to add.
285          * \throws    std::bad_alloc if out of memory.
286          */
287         void addTopic(HelpTopicPointer topic);
288         //! Sets whether hidden options will be shown in help.
289         void setShowHidden(bool bHidden) { bHidden_ = bHidden; }
290         /*! \brief
291          * Sets an override to show the help for the given module.
292          *
293          * If called, the help module directly prints the help for the given
294          * module when called, skipping any other processing.
295          */
296         void setModuleOverride(const CommandLineModuleInterface &module)
297         {
298             moduleOverride_ = &module;
299         }
300
301         //! Returns the context object for help output.
302         const CommandLineHelpContext &context() const
303         {
304             return *context_;
305         }
306
307         virtual const char *name() const { return "help"; }
308         virtual const char *shortDescription() const
309         {
310             return "Print help information";
311         }
312
313         virtual int run(int argc, char *argv[]);
314         virtual void writeHelp(const CommandLineHelpContext &context) const;
315
316     private:
317         void exportHelp(HelpExportInterface *exporter) const;
318
319         boost::scoped_ptr<RootHelpTopic>  rootTopic_;
320         const CommandLineModuleMap       &modules_;
321
322         CommandLineHelpContext           *context_;
323         const CommandLineModuleInterface *moduleOverride_;
324         bool                              bHidden_;
325
326         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
327 };
328
329 CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
330     : rootTopic_(new RootHelpTopic(modules)), modules_(modules),
331       context_(NULL), moduleOverride_(NULL), bHidden_(false)
332 {
333 }
334
335 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
336 {
337     rootTopic_->addSubTopic(move(topic));
338 }
339
340 int CommandLineHelpModule::run(int argc, char *argv[])
341 {
342     const char *const exportFormats[] = { "man", "html", "completion" };
343     std::string       exportFormat;
344     Options           options(NULL, NULL);
345     options.addOption(StringOption("export").store(&exportFormat)
346                           .enumValue(exportFormats));
347     CommandLineParser(&options).parse(&argc, argv);
348     if (!exportFormat.empty())
349     {
350         boost::scoped_ptr<HelpExportInterface> exporter;
351         {
352             GMX_THROW(NotImplementedError("This help format is not implemented"));
353         }
354         exportHelp(exporter.get());
355         return 0;
356     }
357
358     boost::scoped_ptr<CommandLineHelpContext> context(
359             new CommandLineHelpContext(&File::standardOutput(),
360                                        eHelpOutputFormat_Console));
361     context->setShowHidden(bHidden_);
362     context_ = context.get();
363     if (moduleOverride_ != NULL)
364     {
365         ModuleHelpTopic(*moduleOverride_, *this).writeHelp(context->writerContext());
366         return 0;
367     }
368
369     HelpManager       helpManager(*rootTopic_, context->writerContext());
370     try
371     {
372         for (int i = 1; i < argc; ++i)
373         {
374             helpManager.enterTopic(argv[i]);
375         }
376     }
377     catch (const InvalidInputError &ex)
378     {
379         fprintf(stderr, "%s\n", ex.what());
380         return 2;
381     }
382     helpManager.writeCurrentTopic();
383     return 0;
384 }
385
386 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
387 {
388     const HelpWriterContext &writerContext = context.writerContext();
389     // TODO: Implement.
390     if (writerContext.outputFormat() != eHelpOutputFormat_Console)
391     {
392         return;
393     }
394     writerContext.writeTextBlock(
395             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
396     // TODO: More information.
397 }
398
399 void CommandLineHelpModule::exportHelp(HelpExportInterface *exporter) const
400 {
401     // TODO: Would be nicer to have the file names supplied by the build system
402     // and/or export a list of files from here.
403     const char *const program =
404         ProgramInfo::getInstance().invariantProgramName().c_str();
405
406     CommandLineModuleMap::const_iterator module;
407     for (module = modules_.begin(); module != modules_.end(); ++module)
408     {
409         if (module->second->shortDescription() != NULL)
410         {
411             const char *const moduleName = module->first.c_str();
412             std::string       tag(formatString("%s-%s", program, moduleName));
413             exporter->exportModuleHelp(tag, *module->second);
414         }
415     }
416 }
417
418 namespace
419 {
420
421 /********************************************************************
422  * ModuleHelpTopic implementation
423  */
424
425 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
426 {
427     module_.writeHelp(helpModule_.context());
428 }
429
430 /********************************************************************
431  * CMainCommandLineModule
432  */
433
434 /*! \internal \brief
435  * Implements a CommandLineModuleInterface, given a function with C/C++ main()
436  * signature.
437  *
438  * \ingroup module_commandline
439  */
440 class CMainCommandLineModule : public CommandLineModuleInterface
441 {
442     public:
443         //! \copydoc gmx::CommandLineModuleManager::CMainFunction
444         typedef CommandLineModuleManager::CMainFunction CMainFunction;
445
446         /*! \brief
447          * Creates a wrapper module for the given main function.
448          *
449          * \param[in] name             Name for the module.
450          * \param[in] shortDescription One-line description for the module.
451          * \param[in] mainFunction     Main function to wrap.
452          *
453          * Does not throw.  This is essential for correct implementation of
454          * CommandLineModuleManager::runAsMainCMain().
455          */
456         CMainCommandLineModule(const char *name, const char *shortDescription,
457                                CMainFunction mainFunction)
458             : name_(name), shortDescription_(shortDescription),
459               mainFunction_(mainFunction)
460         {
461         }
462
463         virtual const char *name() const
464         {
465             return name_;
466         }
467         virtual const char *shortDescription() const
468         {
469             return shortDescription_;
470         }
471
472         virtual int run(int argc, char *argv[])
473         {
474             return mainFunction_(argc, argv);
475         }
476         virtual void writeHelp(const CommandLineHelpContext &context) const
477         {
478             const HelpOutputFormat format = context.writerContext().outputFormat();
479             const char            *type;
480             switch (format)
481             {
482                 case eHelpOutputFormat_Console:
483                     type = "help";
484                     break;
485                 default:
486                     GMX_THROW(NotImplementedError(
487                                       "Command-line help is not implemented for this output format"));
488             }
489             char *argv[4];
490             int   argc = 3;
491             // TODO: The constness should not be cast away.
492             argv[0] = const_cast<char *>(name_);
493             argv[1] = const_cast<char *>("-man");
494             argv[2] = const_cast<char *>(type);
495             argv[3] = NULL;
496             GlobalCommandLineHelpContext global(context);
497             mainFunction_(argc, argv);
498         }
499
500     private:
501         const char             *name_;
502         const char             *shortDescription_;
503         CMainFunction           mainFunction_;
504
505 };
506
507 }   // namespace
508
509 /********************************************************************
510  * CommandLineModuleManager::Impl
511  */
512
513 /*! \internal \brief
514  * Private implementation class for CommandLineModuleManager.
515  *
516  * \ingroup module_commandline
517  */
518 class CommandLineModuleManager::Impl
519 {
520     public:
521         /*! \brief
522          * Initializes the implementation class.
523          *
524          * \param     programInfo  Program information for the running binary.
525          */
526         explicit Impl(ProgramInfo *programInfo);
527
528         /*! \brief
529          * Helper method that adds a given module to the module manager.
530          *
531          * \throws    std::bad_alloc if out of memory.
532          */
533         void addModule(CommandLineModulePointer module);
534         /*! \brief
535          * Creates the help module if it does not yet exist.
536          *
537          * \throws    std::bad_alloc if out of memory.
538          *
539          * This method should be called before accessing \a helpModule_.
540          */
541         void ensureHelpModuleExists();
542
543         /*! \brief
544          * Finds a module that matches a name.
545          *
546          * \param[in] name  Module name to find.
547          * \returns   Iterator to the found module, or
548          *      \c modules_.end() if not found.
549          *
550          * Does not throw.
551          */
552         CommandLineModuleMap::const_iterator
553         findModuleByName(const std::string &name) const;
554         /*! \brief
555          * Finds a module that the name of the binary.
556          *
557          * \param[in] programInfo  Program information object to use.
558          * \throws    std::bad_alloc if out of memory.
559          * \returns   Iterator to the found module, or
560          *      \c modules_.end() if not found.
561          *
562          * Checks whether the program is invoked through a symlink whose name
563          * is different from ProgramInfo::realBinaryName(), and if so, checks
564          * if a module name matches the name of the symlink.
565          *
566          * Note that the \p programInfo parameter is currently not necessary
567          * (as the program info object is also contained as a member), but it
568          * clarifies the control flow.
569          */
570         CommandLineModuleMap::const_iterator
571         findModuleFromBinaryName(const ProgramInfo &programInfo) const;
572
573         /*! \brief
574          * Processes command-line options for the wrapper binary.
575          *
576          * \param[in,out] argc On input, argc passed to run().
577          *     On output, argc to be passed to the module.
578          * \param[in,out] argv On input, argv passed to run().
579          *     On output, argv to be passed to the module.
580          * \throws    InvalidInputError if there are invalid options.
581          * \returns   The module that should be run.
582          *
583          * Handles command-line options that affect the wrapper binary
584          * (potentially changing the members of \c this in response to the
585          * options).  Also finds the module that should be run and the
586          * arguments that should be passed to it.
587          */
588         CommandLineModuleInterface *
589         processCommonOptions(int *argc, char ***argv);
590
591         /*! \brief
592          * Maps module names to module objects.
593          *
594          * Owns the contained modules.
595          */
596         CommandLineModuleMap         modules_;
597         //! Information about the currently running program.
598         ProgramInfo                 &programInfo_;
599         /*! \brief
600          * Module that implements help for the binary.
601          *
602          * The pointed module is owned by the \a modules_ container.
603          */
604         CommandLineHelpModule       *helpModule_;
605         //! Settings for what to write in the startup header.
606         BinaryInformationSettings    binaryInfoSettings_;
607         //! If non-NULL, run this module in single-module mode.
608         CommandLineModuleInterface  *singleModule_;
609         //! Whether all stderr output should be suppressed.
610         bool                         bQuiet_;
611         //! Whether to write the startup information to stdout iso stderr.
612         bool                         bStdOutInfo_;
613
614     private:
615         GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
616 };
617
618 CommandLineModuleManager::Impl::Impl(ProgramInfo *programInfo)
619     : programInfo_(*programInfo), helpModule_(NULL), singleModule_(NULL),
620       bQuiet_(false), bStdOutInfo_(false)
621 {
622     binaryInfoSettings_.copyright(true);
623 }
624
625 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
626 {
627     GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
628                "Attempted to register a duplicate module name");
629     ensureHelpModuleExists();
630     HelpTopicPointer helpTopic(new ModuleHelpTopic(*module, *helpModule_));
631     modules_.insert(std::make_pair(std::string(module->name()),
632                                    move(module)));
633     helpModule_->addTopic(move(helpTopic));
634 }
635
636 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
637 {
638     if (helpModule_ == NULL)
639     {
640         helpModule_ = new CommandLineHelpModule(modules_);
641         addModule(CommandLineModulePointer(helpModule_));
642     }
643 }
644
645 CommandLineModuleMap::const_iterator
646 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
647 {
648     // TODO: Accept unambiguous prefixes?
649     return modules_.find(name);
650 }
651
652 CommandLineModuleMap::const_iterator
653 CommandLineModuleManager::Impl::findModuleFromBinaryName(
654         const ProgramInfo &programInfo) const
655 {
656     std::string binaryName = programInfo.invariantProgramName();
657     if (binaryName == programInfo.realBinaryName())
658     {
659         return modules_.end();
660     }
661     if (binaryName.compare(0, 2, "g_") == 0)
662     {
663         binaryName.erase(0, 2);
664     }
665     return findModuleByName(binaryName);
666 }
667
668 CommandLineModuleInterface *
669 CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
670 {
671     // Check if we are directly invoking a certain module.
672     CommandLineModuleInterface *module = singleModule_;
673     if (module == NULL)
674     {
675         // Also check for invokation through named symlinks.
676         CommandLineModuleMap::const_iterator moduleIter
677             = findModuleFromBinaryName(programInfo_);
678         if (moduleIter != modules_.end())
679         {
680             module = moduleIter->second.get();
681         }
682     }
683
684     bool bHelp      = false;
685     bool bHidden    = false;
686     bool bVersion   = false;
687     bool bCopyright = true;
688     // TODO: Print the common options into the help.
689     // TODO: It would be nice to propagate at least the -quiet option to
690     // the modules so that they can also be quiet in response to this.
691     Options options(NULL, NULL);
692     options.addOption(BooleanOption("h").store(&bHelp));
693     options.addOption(BooleanOption("hidden").store(&bHidden));
694     options.addOption(BooleanOption("quiet").store(&bQuiet_));
695     options.addOption(BooleanOption("version").store(&bVersion));
696     options.addOption(BooleanOption("copyright").store(&bCopyright));
697
698     if (module == NULL)
699     {
700         // If not in single-module mode, process options to the wrapper binary.
701         // TODO: Ideally, this could be done by CommandLineParser.
702         int argcForWrapper = 1;
703         while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
704         {
705             ++argcForWrapper;
706         }
707         if (argcForWrapper > 1)
708         {
709             CommandLineParser(&options).parse(&argcForWrapper, *argv);
710         }
711         // If no action requested and there is a module specified, process it.
712         if (argcForWrapper < *argc && !bHelp && !bVersion)
713         {
714             const char *moduleName = (*argv)[argcForWrapper];
715             CommandLineModuleMap::const_iterator moduleIter
716                 = findModuleByName(moduleName);
717             if (moduleIter == modules_.end())
718             {
719                 std::string message =
720                     formatString("'%s' is not a GROMACS command.", moduleName);
721                 GMX_THROW(InvalidInputError(message));
722             }
723             module = moduleIter->second.get();
724             programInfo_.setDisplayName(
725                     programInfo_.realBinaryName() + "-" + moduleIter->first);
726             *argc -= argcForWrapper;
727             *argv += argcForWrapper;
728             // After this point, argc and argv are the same independent of
729             // which path is taken: (*argv)[0] is the module name.
730         }
731     }
732     if (module != NULL)
733     {
734         // Recognize the common options also after the module name.
735         // TODO: It could be nicer to only recognize -h/-hidden if module is not
736         // null.
737         CommandLineParser(&options).skipUnknown(true).parse(argc, *argv);
738     }
739     options.finish();
740     binaryInfoSettings_.extendedInfo(bVersion);
741     binaryInfoSettings_.copyright(bCopyright);
742     if (bVersion)
743     {
744         bQuiet_      = false;
745         bStdOutInfo_ = true;
746         return NULL;
747     }
748     // If no module specified and no other action, show the help.
749     // Also explicitly specifying -h for the wrapper binary goes here.
750     if (module == NULL || bHelp)
751     {
752         ensureHelpModuleExists();
753         if (module != NULL)
754         {
755             helpModule_->setModuleOverride(*module);
756         }
757         *argc  = 1;
758         module = helpModule_;
759     }
760     if (module == helpModule_)
761     {
762         helpModule_->setShowHidden(bHidden);
763     }
764     return module;
765 }
766
767 /********************************************************************
768  * CommandLineModuleManager
769  */
770
771 CommandLineModuleManager::CommandLineModuleManager(ProgramInfo *programInfo)
772     : impl_(new Impl(programInfo))
773 {
774 }
775
776 CommandLineModuleManager::~CommandLineModuleManager()
777 {
778 }
779
780 void CommandLineModuleManager::setQuiet(bool bQuiet)
781 {
782     impl_->bQuiet_ = bQuiet;
783 }
784
785 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
786 {
787     impl_->addModule(move(module));
788 }
789
790 void CommandLineModuleManager::addModuleCMain(
791         const char *name, const char *shortDescription,
792         CMainFunction mainFunction)
793 {
794     CommandLineModulePointer module(
795             new CMainCommandLineModule(name, shortDescription, mainFunction));
796     addModule(move(module));
797 }
798
799 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
800 {
801     impl_->ensureHelpModuleExists();
802     impl_->helpModule_->addTopic(move(topic));
803 }
804
805 int CommandLineModuleManager::run(int argc, char *argv[])
806 {
807     CommandLineModuleInterface *module;
808     const bool                  bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
809     try
810     {
811         module = impl_->processCommonOptions(&argc, &argv);
812     }
813     catch (const std::exception &)
814     {
815         if (bMaster && !impl_->bQuiet_)
816         {
817             printBinaryInformation(stderr, impl_->programInfo_,
818                                    impl_->binaryInfoSettings_);
819         }
820         throw;
821     }
822     if (!bMaster)
823     {
824         impl_->bQuiet_ = true;
825     }
826     if (!impl_->bQuiet_)
827     {
828         FILE *out = (impl_->bStdOutInfo_ ? stdout : stderr);
829         printBinaryInformation(out, impl_->programInfo_,
830                                impl_->binaryInfoSettings_);
831         fprintf(out, "\n");
832     }
833     if (module == NULL)
834     {
835         return 0;
836     }
837     int rc = module->run(argc, argv);
838     if (!impl_->bQuiet_)
839     {
840         gmx_thanx(stderr);
841     }
842     return rc;
843 }
844
845 // static
846 int CommandLineModuleManager::runAsMainSingleModule(
847         int argc, char *argv[], CommandLineModuleInterface *module)
848 {
849     ProgramInfo &programInfo = gmx::init(&argc, &argv);
850     try
851     {
852         CommandLineModuleManager manager(&programInfo);
853         manager.impl_->singleModule_ = module;
854         int rc = manager.run(argc, argv);
855         gmx::finalize();
856         return rc;
857     }
858     catch (const std::exception &ex)
859     {
860         printFatalErrorMessage(stderr, ex);
861         return processExceptionAtExit(ex);
862     }
863 }
864
865 // static
866 int CommandLineModuleManager::runAsMainCMain(
867         int argc, char *argv[], CMainFunction mainFunction)
868 {
869     CMainCommandLineModule module(argv[0], NULL, mainFunction);
870     return runAsMainSingleModule(argc, argv, &module);
871 }
872
873 } // namespace gmx