Framework for help export from wrapper binary.
[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 namespace
78 {
79
80 /********************************************************************
81  * RootHelpTopic
82  */
83
84 struct RootHelpText
85 {
86     static const char        name[];
87     static const char        title[];
88     static const char *const text[];
89 };
90
91 // The first two are not used.
92 const char        RootHelpText::name[]  = "";
93 const char        RootHelpText::title[] = "";
94 const char *const RootHelpText::text[]  = {
95     "Usage: [PROGRAM] <command> [<args>]",
96 };
97
98 /*! \internal \brief
99  * Help topic that forms the root of the help tree for the help subcommand.
100  *
101  * \ingroup module_commandline
102  */
103 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
104 {
105     public:
106         /*! \brief
107          * Creates a root help topic.
108          *
109          * \param[in] modules  List of modules for to use for module listings.
110          *
111          * Does not throw.
112          */
113         explicit RootHelpTopic(const CommandLineModuleMap &modules)
114             : modules_(modules)
115         {
116         }
117
118         virtual void writeHelp(const HelpWriterContext &context) const;
119
120     private:
121         void printModuleList(const HelpWriterContext &context) const;
122
123         const CommandLineModuleMap &modules_;
124
125         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
126 };
127
128 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
129 {
130     if (context.outputFormat() != eHelpOutputFormat_Console)
131     {
132         // TODO: Implement once the situation with Redmine issue #969 is more
133         // clear.
134         GMX_THROW(NotImplementedError(
135                           "Root help is not implemented for this output format"));
136     }
137     writeBasicHelpTopic(context, *this, helpText());
138     // TODO: If/when this list becomes long, it may be better to only print
139     // "common" commands here, and have a separate topic (e.g.,
140     // "help commands") that prints the full list.
141     printModuleList(context);
142     context.writeTextBlock(
143             "For additional help on a command, use '[PROGRAM] help <command>'");
144     writeSubTopicList(context,
145                       "\nAdditional help is available on the following topics:");
146     context.writeTextBlock(
147             "To access the help, use '[PROGRAM] help <topic>'.");
148 }
149
150 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
151 {
152     if (context.outputFormat() != eHelpOutputFormat_Console)
153     {
154         // TODO: Implement once the situation with Redmine issue #969 is more
155         // clear.
156         GMX_THROW(NotImplementedError(
157                           "Module list is not implemented for this output format"));
158     }
159     int maxNameLength = 0;
160     CommandLineModuleMap::const_iterator module;
161     for (module = modules_.begin(); module != modules_.end(); ++module)
162     {
163         int nameLength = static_cast<int>(module->first.length());
164         if (module->second->shortDescription() != NULL
165             && nameLength > maxNameLength)
166         {
167             maxNameLength = nameLength;
168         }
169     }
170     File              &file = context.outputFile();
171     TextTableFormatter formatter;
172     formatter.addColumn(NULL, maxNameLength + 1, false);
173     formatter.addColumn(NULL, 72 - maxNameLength, true);
174     formatter.setFirstColumnIndent(4);
175     file.writeLine();
176     file.writeLine("Available commands:");
177     for (module = modules_.begin(); module != modules_.end(); ++module)
178     {
179         const char *name        = module->first.c_str();
180         const char *description = module->second->shortDescription();
181         if (description != NULL)
182         {
183             formatter.clear();
184             formatter.addColumnLine(0, name);
185             formatter.addColumnLine(1, description);
186             file.writeString(formatter.formatRow());
187         }
188     }
189 }
190
191 /********************************************************************
192  * ModuleHelpTopic
193  */
194
195 /*! \internal \brief
196  * Help topic wrapper for a command-line module.
197  *
198  * This class implements HelpTopicInterface such that it wraps a
199  * CommandLineModuleInterface, allowing subcommand "help <command>"
200  * to produce the help for "<command>".
201  *
202  * \ingroup module_commandline
203  */
204 class ModuleHelpTopic : public HelpTopicInterface
205 {
206     public:
207         //! Constructs a help topic for a specific module.
208         explicit ModuleHelpTopic(const CommandLineModuleInterface &module)
209             : module_(module)
210         {
211         }
212
213         virtual const char *name() const { return module_.name(); }
214         virtual const char *title() const { return NULL; }
215         virtual bool hasSubTopics() const { return false; }
216         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
217         {
218             return NULL;
219         }
220         virtual void writeHelp(const HelpWriterContext &context) const;
221
222     private:
223         const CommandLineModuleInterface &module_;
224
225         GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
226 };
227
228 void ModuleHelpTopic::writeHelp(const HelpWriterContext &context) const
229 {
230     CommandLineHelpContext newContext(&context.outputFile(),
231                                       context.outputFormat());
232     module_.writeHelp(newContext);
233 }
234
235 /********************************************************************
236  * HelpExportInterface
237  */
238
239 /*! \internal \brief
240  * Callbacks for exporting help information for command-line modules.
241  *
242  * \ingroup module_commandline
243  */
244 class HelpExportInterface
245 {
246     public:
247         virtual ~HelpExportInterface() {};
248
249         /*! \brief
250          * Called to export the help for each module.
251          *
252          * \param[in] tag     Unique tag for the module (gmx-something).
253          * \param[in] module  Module for which the help should be exported.
254          */
255         virtual void exportModuleHelp(const std::string                &tag,
256                                       const CommandLineModuleInterface &module) = 0;
257 };
258
259 }   // namespace
260
261 /********************************************************************
262  * CommandLineHelpModule
263  */
264
265 /*! \internal \brief
266  * Command-line module for producing help.
267  *
268  * This module implements the 'help' subcommand that is automatically added by
269  * CommandLineModuleManager.
270  *
271  * \ingroup module_commandline
272  */
273 class CommandLineHelpModule : public CommandLineModuleInterface
274 {
275     public:
276         /*! \brief
277          * Creates a command-line help module.
278          *
279          * \param[in] modules  List of modules for to use for module listings.
280          * \throws    std::bad_alloc if out of memory.
281          */
282         explicit CommandLineHelpModule(const CommandLineModuleMap &modules);
283
284         /*! \brief
285          * Adds a top-level help topic.
286          *
287          * \param[in] topic  Help topic to add.
288          * \throws    std::bad_alloc if out of memory.
289          */
290         void addTopic(HelpTopicPointer topic);
291
292         virtual const char *name() const { return "help"; }
293         virtual const char *shortDescription() const
294         {
295             return "Print help information";
296         }
297
298         virtual int run(int argc, char *argv[]);
299         virtual void writeHelp(const CommandLineHelpContext &context) const;
300
301     private:
302         void exportHelp(HelpExportInterface *exporter) const;
303
304         boost::scoped_ptr<RootHelpTopic>  rootTopic_;
305         const CommandLineModuleMap       &modules_;
306
307         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
308 };
309
310 CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
311     : rootTopic_(new RootHelpTopic(modules)), modules_(modules)
312 {
313 }
314
315 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
316 {
317     rootTopic_->addSubTopic(move(topic));
318 }
319
320 int CommandLineHelpModule::run(int argc, char *argv[])
321 {
322     const char *const exportFormats[] = { "man", "html", "completion" };
323     std::string       exportFormat;
324     Options           options(NULL, NULL);
325     options.addOption(StringOption("export").store(&exportFormat)
326                           .enumValue(exportFormats));
327     CommandLineParser(&options).parse(&argc, argv);
328     if (!exportFormat.empty())
329     {
330         boost::scoped_ptr<HelpExportInterface> exporter;
331         {
332             GMX_THROW(NotImplementedError("This help format is not implemented"));
333         }
334         exportHelp(exporter.get());
335         return 0;
336     }
337
338     HelpWriterContext context(&File::standardOutput(),
339                               eHelpOutputFormat_Console);
340     HelpManager       helpManager(*rootTopic_, context);
341     try
342     {
343         for (int i = 1; i < argc; ++i)
344         {
345             helpManager.enterTopic(argv[i]);
346         }
347     }
348     catch (const InvalidInputError &ex)
349     {
350         fprintf(stderr, "%s\n", ex.what());
351         return 2;
352     }
353     helpManager.writeCurrentTopic();
354     return 0;
355 }
356
357 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
358 {
359     const HelpWriterContext &writerContext = context.writerContext();
360     // TODO: Implement.
361     if (writerContext.outputFormat() != eHelpOutputFormat_Console)
362     {
363         return;
364     }
365     writerContext.writeTextBlock(
366             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
367     // TODO: More information.
368 }
369
370 void CommandLineHelpModule::exportHelp(HelpExportInterface *exporter) const
371 {
372     // TODO: Would be nicer to have the file names supplied by the build system
373     // and/or export a list of files from here.
374     const char *const program =
375         ProgramInfo::getInstance().invariantProgramName().c_str();
376
377     CommandLineModuleMap::const_iterator module;
378     for (module = modules_.begin(); module != modules_.end(); ++module)
379     {
380         if (module->second->shortDescription() != NULL)
381         {
382             const char *const moduleName = module->first.c_str();
383             std::string       tag(formatString("%s-%s", program, moduleName));
384             exporter->exportModuleHelp(tag, *module->second);
385         }
386     }
387 }
388
389 namespace
390 {
391
392 /********************************************************************
393  * CMainCommandLineModule
394  */
395
396 /*! \internal \brief
397  * Implements a CommandLineModuleInterface, given a function with C/C++ main()
398  * signature.
399  *
400  * \ingroup module_commandline
401  */
402 class CMainCommandLineModule : public CommandLineModuleInterface
403 {
404     public:
405         //! \copydoc gmx::CommandLineModuleManager::CMainFunction
406         typedef CommandLineModuleManager::CMainFunction CMainFunction;
407
408         /*! \brief
409          * Creates a wrapper module for the given main function.
410          *
411          * \param[in] name             Name for the module.
412          * \param[in] shortDescription One-line description for the module.
413          * \param[in] mainFunction     Main function to wrap.
414          *
415          * Does not throw.  This is essential for correct implementation of
416          * CommandLineModuleManager::runAsMainCMain().
417          */
418         CMainCommandLineModule(const char *name, const char *shortDescription,
419                                CMainFunction mainFunction)
420             : name_(name), shortDescription_(shortDescription),
421               mainFunction_(mainFunction)
422         {
423         }
424
425         virtual const char *name() const
426         {
427             return name_;
428         }
429         virtual const char *shortDescription() const
430         {
431             return shortDescription_;
432         }
433
434         virtual int run(int argc, char *argv[])
435         {
436             return mainFunction_(argc, argv);
437         }
438         virtual void writeHelp(const CommandLineHelpContext &context) const
439         {
440             const HelpOutputFormat format = context.writerContext().outputFormat();
441             char                  *argv[2];
442             int                    argc = 1;
443             // TODO: The constness should not be cast away.
444             argv[0] = const_cast<char *>(name_);
445             switch (format)
446             {
447                 case eHelpOutputFormat_Console:
448                     argv[1] = const_cast<char *>("-h");
449                     argc    = 2;
450                     break;
451                 default:
452                     GMX_THROW(NotImplementedError(
453                                       "Command-line help is not implemented for this output format"));
454             }
455             GlobalCommandLineHelpContext global(context);
456             mainFunction_(argc, argv);
457         }
458
459     private:
460         const char             *name_;
461         const char             *shortDescription_;
462         CMainFunction           mainFunction_;
463
464 };
465
466 }   // namespace
467
468 /********************************************************************
469  * CommandLineModuleManager::Impl
470  */
471
472 /*! \internal \brief
473  * Private implementation class for CommandLineModuleManager.
474  *
475  * \ingroup module_commandline
476  */
477 class CommandLineModuleManager::Impl
478 {
479     public:
480         /*! \brief
481          * Initializes the implementation class.
482          *
483          * \param     programInfo  Program information for the running binary.
484          */
485         explicit Impl(ProgramInfo *programInfo);
486
487         /*! \brief
488          * Finds a module that matches a name.
489          *
490          * \param[in] name  Module name to find.
491          * \returns   Iterator to the found module, or
492          *      \c modules_.end() if not found.
493          *
494          * Does not throw.
495          */
496         CommandLineModuleMap::const_iterator
497         findModuleByName(const std::string &name) const;
498         /*! \brief
499          * Finds a module that the name of the binary.
500          *
501          * \param[in] programInfo  Program information object to use.
502          * \throws    std::bad_alloc if out of memory.
503          * \returns   Iterator to the found module, or
504          *      \c modules_.end() if not found.
505          *
506          * Checks whether the program is invoked through a symlink whose name
507          * is different from ProgramInfo::realBinaryName(), and if so, checks
508          * if a module name matches the name of the symlink.
509          *
510          * Note that the \p programInfo parameter is currently not necessary
511          * (as the program info object is also contained as a member), but it
512          * clarifies the control flow.
513          */
514         CommandLineModuleMap::const_iterator
515         findModuleFromBinaryName(const ProgramInfo &programInfo) const;
516
517         /*! \brief
518          * Processes command-line options for the wrapper binary.
519          *
520          * \param[in,out] argc On input, argc passed to run().
521          *     On output, argc to be passed to the module.
522          * \param[in,out] argv On input, argv passed to run().
523          *     On output, argv to be passed to the module.
524          * \throws    InvalidInputError if there are invalid options.
525          * \returns   The module that should be run.
526          *
527          * Handles command-line options that affect the wrapper binary
528          * (potentially changing the members of \c this in response to the
529          * options).  Also finds the module that should be run and the
530          * arguments that should be passed to it.
531          */
532         CommandLineModuleInterface *
533         processCommonOptions(int *argc, char ***argv);
534
535         /*! \brief
536          * Maps module names to module objects.
537          *
538          * Owns the contained modules.
539          */
540         CommandLineModuleMap         modules_;
541         //! Information about the currently running program.
542         ProgramInfo                 &programInfo_;
543         /*! \brief
544          * Module that implements help for the binary.
545          *
546          * The pointed module is owned by the \a modules_ container.
547          */
548         CommandLineHelpModule       *helpModule_;
549         //! Settings for what to write in the startup header.
550         BinaryInformationSettings    binaryInfoSettings_;
551         //! If non-NULL, run this module in single-module mode.
552         CommandLineModuleInterface  *singleModule_;
553         //! Whether all stderr output should be suppressed.
554         bool                         bQuiet_;
555         //! Whether to write the startup information to stdout iso stderr.
556         bool                         bStdOutInfo_;
557
558     private:
559         GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
560 };
561
562 CommandLineModuleManager::Impl::Impl(ProgramInfo *programInfo)
563     : programInfo_(*programInfo), helpModule_(NULL), singleModule_(NULL),
564       bQuiet_(false), bStdOutInfo_(false)
565 {
566     binaryInfoSettings_.copyright(true);
567 }
568
569 CommandLineModuleMap::const_iterator
570 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
571 {
572     // TODO: Accept unambiguous prefixes?
573     return modules_.find(name);
574 }
575
576 CommandLineModuleMap::const_iterator
577 CommandLineModuleManager::Impl::findModuleFromBinaryName(
578         const ProgramInfo &programInfo) const
579 {
580     std::string binaryName = programInfo.invariantProgramName();
581     if (binaryName == programInfo.realBinaryName())
582     {
583         return modules_.end();
584     }
585     if (binaryName.compare(0, 2, "g_") == 0)
586     {
587         binaryName.erase(0, 2);
588     }
589     return findModuleByName(binaryName);
590 }
591
592 CommandLineModuleInterface *
593 CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
594 {
595     // Check if we are directly invoking a certain module.
596     CommandLineModuleInterface *module = singleModule_;
597     if (module == NULL)
598     {
599         // Also check for invokation through named symlinks.
600         CommandLineModuleMap::const_iterator moduleIter
601             = findModuleFromBinaryName(programInfo_);
602         if (moduleIter != modules_.end())
603         {
604             module = moduleIter->second.get();
605         }
606     }
607
608     bool bHelp      = false;
609     bool bVersion   = false;
610     bool bCopyright = true;
611     // TODO: Print the common options into the help.
612     // TODO: It would be nice to propagate at least the -quiet option to
613     // the modules so that they can also be quiet in response to this.
614     // TODO: Consider handling -h and related options here instead of in the
615     // modules (also -hidden needs to be transfered here to make that work).
616     // That would mean that with -h, all module-specific options would get
617     // ignored.  This means that the help output would not depend on the
618     // command line, but would always show the default values (making it
619     // possible to simplify it further), but also that mdrun -h could not be
620     // used for option validation in g_tune_pme.
621     Options options(NULL, NULL);
622     if (module == NULL)
623     {
624         options.addOption(BooleanOption("h").store(&bHelp));
625     }
626     options.addOption(BooleanOption("quiet").store(&bQuiet_));
627     options.addOption(BooleanOption("version").store(&bVersion));
628     options.addOption(BooleanOption("copyright").store(&bCopyright));
629
630     if (module == NULL)
631     {
632         // If not in single-module mode, process options to the wrapper binary.
633         // TODO: Ideally, this could be done by CommandLineParser.
634         int argcForWrapper = 1;
635         while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
636         {
637             ++argcForWrapper;
638         }
639         if (argcForWrapper > 1)
640         {
641             CommandLineParser(&options).parse(&argcForWrapper, *argv);
642         }
643         // If no action requested and there is a module specified, process it.
644         if (argcForWrapper < *argc && !bHelp && !bVersion)
645         {
646             const char *moduleName = (*argv)[argcForWrapper];
647             CommandLineModuleMap::const_iterator moduleIter
648                 = findModuleByName(moduleName);
649             if (moduleIter == modules_.end())
650             {
651                 std::string message =
652                     formatString("'%s' is not a GROMACS command.", moduleName);
653                 GMX_THROW(InvalidInputError(message));
654             }
655             module = moduleIter->second.get();
656             programInfo_.setDisplayName(
657                     programInfo_.realBinaryName() + "-" + moduleIter->first);
658             *argc -= argcForWrapper;
659             *argv += argcForWrapper;
660             // After this point, argc and argv are the same independent of
661             // which path is taken: (*argv)[0] is the module name.
662         }
663     }
664     else
665     {
666         // In single-module mode, recognize the common options also after the
667         // module name.
668         CommandLineParser(&options).skipUnknown(true).parse(argc, *argv);
669     }
670     options.finish();
671     binaryInfoSettings_.extendedInfo(bVersion);
672     binaryInfoSettings_.copyright(bCopyright);
673     if (bVersion)
674     {
675         bQuiet_      = false;
676         bStdOutInfo_ = true;
677         return NULL;
678     }
679     // If no module specified and no other action, show the help.
680     // Also explicitly specifying -h for the wrapper binary goes here.
681     if (module == NULL)
682     {
683         *argc = 1;
684         return helpModule_;
685     }
686     return module;
687 }
688
689 /********************************************************************
690  * CommandLineModuleManager
691  */
692
693 CommandLineModuleManager::CommandLineModuleManager(ProgramInfo *programInfo)
694     : impl_(new Impl(programInfo))
695 {
696 }
697
698 CommandLineModuleManager::~CommandLineModuleManager()
699 {
700 }
701
702 void CommandLineModuleManager::setQuiet(bool bQuiet)
703 {
704     impl_->bQuiet_ = bQuiet;
705 }
706
707 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
708 {
709     GMX_ASSERT(impl_->modules_.find(module->name()) == impl_->modules_.end(),
710                "Attempted to register a duplicate module name");
711     HelpTopicPointer helpTopic(new ModuleHelpTopic(*module));
712     impl_->modules_.insert(std::make_pair(std::string(module->name()),
713                                           move(module)));
714     addHelpTopic(move(helpTopic));
715 }
716
717 void CommandLineModuleManager::addModuleCMain(
718         const char *name, const char *shortDescription,
719         CMainFunction mainFunction)
720 {
721     CommandLineModulePointer module(
722             new CMainCommandLineModule(name, shortDescription, mainFunction));
723     addModule(move(module));
724 }
725
726 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
727 {
728     if (impl_->helpModule_ == NULL)
729     {
730         impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
731         addModule(CommandLineModulePointer(impl_->helpModule_));
732     }
733     impl_->helpModule_->addTopic(move(topic));
734 }
735
736 int CommandLineModuleManager::run(int argc, char *argv[])
737 {
738     CommandLineModuleInterface *module;
739     const bool                  bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
740     try
741     {
742         module = impl_->processCommonOptions(&argc, &argv);
743     }
744     catch (const std::exception &)
745     {
746         if (bMaster && !impl_->bQuiet_)
747         {
748             printBinaryInformation(stderr, impl_->programInfo_,
749                                    impl_->binaryInfoSettings_);
750         }
751         throw;
752     }
753     if (!bMaster)
754     {
755         impl_->bQuiet_ = true;
756     }
757     if (!impl_->bQuiet_)
758     {
759         FILE *out = (impl_->bStdOutInfo_ ? stdout : stderr);
760         printBinaryInformation(out, impl_->programInfo_,
761                                impl_->binaryInfoSettings_);
762         fprintf(out, "\n");
763     }
764     if (module == NULL)
765     {
766         return 0;
767     }
768     int rc = module->run(argc, argv);
769     if (!impl_->bQuiet_)
770     {
771         gmx_thanx(stderr);
772     }
773     return rc;
774 }
775
776 // static
777 int CommandLineModuleManager::runAsMainSingleModule(
778         int argc, char *argv[], CommandLineModuleInterface *module)
779 {
780     ProgramInfo &programInfo = gmx::init(&argc, &argv);
781     try
782     {
783         CommandLineModuleManager manager(&programInfo);
784         manager.impl_->singleModule_ = module;
785         int rc = manager.run(argc, argv);
786         gmx::finalize();
787         return rc;
788     }
789     catch (const std::exception &ex)
790     {
791         printFatalErrorMessage(stderr, ex);
792         return processExceptionAtExit(ex);
793     }
794 }
795
796 // static
797 int CommandLineModuleManager::runAsMainCMain(
798         int argc, char *argv[], CMainFunction mainFunction)
799 {
800     CMainCommandLineModule module(argv[0], NULL, mainFunction);
801     return runAsMainSingleModule(argc, argv, &module);
802 }
803
804 } // namespace gmx