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