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