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