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  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source 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 #include "gromacs/utility/uniqueptr.h"
74
75 namespace gmx
76 {
77
78 //! Container type for mapping module names to module objects.
79 typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
80 //! Smart pointer type for managing a CommandLineModuleGroup.
81 typedef gmx_unique_ptr<internal::CommandLineModuleGroupData>::type
82     CommandLineModuleGroupDataPointer;
83 //! Container type for keeping a list of module groups.
84 typedef std::vector<CommandLineModuleGroupDataPointer>
85     CommandLineModuleGroupList;
86
87 class CommandLineHelpModule;
88
89 namespace internal
90 {
91
92 /*! \internal
93  * \brief
94  * Internal data for a CommandLineModuleManager module group.
95  *
96  * This class contains the state of a module group.  CommandLineModuleGroup
97  * provides the public interface to construct/alter the state, and
98  * CommandLineModuleManager and its associated classes use it for help output.
99  *
100  * \ingroup module_commandline
101  */
102 class CommandLineModuleGroupData
103 {
104     public:
105         /*! \brief
106          * Shorthand for a list of modules contained in the group.
107          *
108          * The first element in the contained pair contains the tag
109          * (gmx-something) of the module, and the second element contains the
110          * description.  The second element is never NULL.
111          */
112         typedef std::vector<std::pair<std::string, const char *> > ModuleList;
113
114         /*! \brief
115          * Constructs an empty module group.
116          *
117          * \param[in] modules  List of all modules
118          *     (used for checking and default descriptions).
119          * \param[in] title    Title of the group.
120          *
121          * Does not throw.
122          */
123         CommandLineModuleGroupData(const CommandLineModuleMap &modules,
124                                    const char                 *title)
125             : allModules_(modules), title_(title)
126         {
127         }
128
129         //! Returns the title for the group.
130         const char *title() const { return title_; }
131         //! Returns the list of modules in the group.
132         const ModuleList &modules() const { return modules_; }
133
134         /*! \brief
135          * Adds a module to the group.
136          *
137          * \param[in] name        Name of the module.
138          * \param[in] description Description of the module in this group.
139          * \throws    std::bad_alloc if out of memory.
140          *
141          * If \p description is NULL, the description returned by the module is
142          * used.
143          */
144         void addModule(const char *name, const char *description)
145         {
146             CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
147             GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
148                                "Non-existent module added to a group");
149             if (description == NULL)
150             {
151                 description = moduleIter->second->shortDescription();
152                 GMX_RELEASE_ASSERT(description != NULL,
153                                    "Module without a description added to a group");
154             }
155             const char *const program =
156                 ProgramInfo::getInstance().invariantProgramName().c_str();
157             std::string       tag(formatString("%s-%s", program, name));
158             modules_.push_back(std::make_pair(tag, description));
159         }
160
161     private:
162         const CommandLineModuleMap &allModules_;
163         const char                 *title_;
164         ModuleList                  modules_;
165
166         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineModuleGroupData);
167 };
168
169 }   // namespace internal
170
171 namespace
172 {
173
174 /********************************************************************
175  * RootHelpTopic
176  */
177
178 struct RootHelpText
179 {
180     static const char        name[];
181     static const char        title[];
182     static const char *const text[];
183 };
184
185 // The first two are not used.
186 const char        RootHelpText::name[]  = "";
187 const char        RootHelpText::title[] = "";
188 const char *const RootHelpText::text[]  = {
189     "Usage: [PROGRAM] <command> [<args>]",
190 };
191
192 /*! \brief
193  * Help topic that forms the root of the help tree for the help subcommand.
194  *
195  * \ingroup module_commandline
196  */
197 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
198 {
199     public:
200         /*! \brief
201          * Creates a root help topic.
202          *
203          * \param[in] modules  List of modules for to use for module listings.
204          *
205          * Does not throw.
206          */
207         explicit RootHelpTopic(const CommandLineModuleMap &modules)
208             : modules_(modules)
209         {
210         }
211
212         virtual void writeHelp(const HelpWriterContext &context) const;
213
214     private:
215         void printModuleList(const HelpWriterContext &context) const;
216
217         const CommandLineModuleMap &modules_;
218
219         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
220 };
221
222 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
223 {
224     if (context.outputFormat() != eHelpOutputFormat_Console)
225     {
226         // TODO: Implement once the situation with Redmine issue #969 is more
227         // clear.
228         GMX_THROW(NotImplementedError(
229                           "Root help is not implemented for this output format"));
230     }
231     writeBasicHelpTopic(context, *this, helpText());
232     // TODO: If/when this list becomes long, it may be better to only print
233     // "common" commands here, and have a separate topic (e.g.,
234     // "help commands") that prints the full list.
235     printModuleList(context);
236     context.writeTextBlock(
237             "For additional help on a command, use '[PROGRAM] help <command>'");
238     writeSubTopicList(context,
239                       "\nAdditional help is available on the following topics:");
240     context.writeTextBlock(
241             "To access the help, use '[PROGRAM] help <topic>'.");
242 }
243
244 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
245 {
246     if (context.outputFormat() != eHelpOutputFormat_Console)
247     {
248         // TODO: Implement once the situation with Redmine issue #969 is more
249         // clear.
250         GMX_THROW(NotImplementedError(
251                           "Module list is not implemented for this output format"));
252     }
253     int maxNameLength = 0;
254     CommandLineModuleMap::const_iterator module;
255     for (module = modules_.begin(); module != modules_.end(); ++module)
256     {
257         int nameLength = static_cast<int>(module->first.length());
258         if (module->second->shortDescription() != NULL
259             && nameLength > maxNameLength)
260         {
261             maxNameLength = nameLength;
262         }
263     }
264     File              &file = context.outputFile();
265     TextTableFormatter formatter;
266     formatter.addColumn(NULL, maxNameLength + 1, false);
267     formatter.addColumn(NULL, 72 - maxNameLength, true);
268     formatter.setFirstColumnIndent(4);
269     file.writeLine();
270     file.writeLine("Available commands:");
271     for (module = modules_.begin(); module != modules_.end(); ++module)
272     {
273         const char *name        = module->first.c_str();
274         const char *description = module->second->shortDescription();
275         if (description != NULL)
276         {
277             formatter.clear();
278             formatter.addColumnLine(0, name);
279             formatter.addColumnLine(1, description);
280             file.writeString(formatter.formatRow());
281         }
282     }
283 }
284
285 /********************************************************************
286  * ModuleHelpTopic declaration
287  */
288
289 /*! \brief
290  * Help topic wrapper for a command-line module.
291  *
292  * This class implements HelpTopicInterface such that it wraps a
293  * CommandLineModuleInterface, allowing subcommand "help <command>"
294  * to produce the help for "<command>".
295  *
296  * \ingroup module_commandline
297  */
298 class ModuleHelpTopic : public HelpTopicInterface
299 {
300     public:
301         //! Constructs a help topic for a specific module.
302         ModuleHelpTopic(const CommandLineModuleInterface &module,
303                         const CommandLineHelpModule      &helpModule)
304             : module_(module), helpModule_(helpModule)
305         {
306         }
307
308         virtual const char *name() const { return module_.name(); }
309         virtual const char *title() const { return NULL; }
310         virtual bool hasSubTopics() const { return false; }
311         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
312         {
313             return NULL;
314         }
315         virtual void writeHelp(const HelpWriterContext &context) const;
316
317     private:
318         const CommandLineModuleInterface &module_;
319         const CommandLineHelpModule      &helpModule_;
320
321         GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
322 };
323
324 /********************************************************************
325  * HelpExportInterface
326  */
327
328 /*! \brief
329  * Callbacks for exporting help information for command-line modules.
330  *
331  * \ingroup module_commandline
332  */
333 class HelpExportInterface
334 {
335     public:
336         //! Shorthand for a list of modules contained in a group.
337         typedef internal::CommandLineModuleGroupData::ModuleList
338             ModuleGroupContents;
339
340         virtual ~HelpExportInterface() {};
341
342         /*! \brief
343          * Called once before exporting individual modules.
344          *
345          * Can, e.g., open shared output files (e.g., if the output is written
346          * into a single file, or if a separate index is required) and write
347          * headers into them.
348          */
349         virtual void startModuleExport() = 0;
350         /*! \brief
351          * Called to export the help for each module.
352          *
353          * \param[in] module      Module for which the help should be exported.
354          * \param[in] tag         Unique tag for the module (gmx-something).
355          * \param[in] displayName Display name for the module (gmx something).
356          */
357         virtual void exportModuleHelp(
358             const CommandLineModuleInterface &module,
359             const std::string                &tag,
360             const std::string                &displayName) = 0;
361         /*! \brief
362          * Called after all modules have been exported.
363          *
364          * Can close files opened in startModuleExport(), write footers to them
365          * etc.
366          */
367         virtual void finishModuleExport() = 0;
368
369         /*! \brief
370          * Called once before exporting module groups.
371          *
372          * Can, e.g., open a single output file for listing all the groups.
373          */
374         virtual void startModuleGroupExport() = 0;
375         /*! \brief
376          * Called to export the help for each module group.
377          *
378          * \param[in] title    Title for the group.
379          * \param[in] modules  List of modules in the group.
380          */
381         virtual void exportModuleGroup(const char                *title,
382                                        const ModuleGroupContents &modules) = 0;
383         /*! \brief
384          * Called after all module groups have been exported.
385          *
386          * Can close files opened in startModuleGroupExport(), write footers to them
387          * etc.
388          */
389         virtual void finishModuleGroupExport() = 0;
390 };
391
392 /*! \internal \brief
393  * Adds hyperlinks to modules within this binary.
394  *
395  * \param[in,out] links   Links are added here.
396  * \param[in]     modules Modules in the current binary.
397  * \throws        std::bad_alloc if out of memory.
398  *
399  * Initializes a HelpLinks object with links to modules defined in \p modules.
400  *
401  * \ingroup module_commandline
402  */
403 void initProgramLinks(HelpLinks *links, const CommandLineModuleMap &modules)
404 {
405     // TODO: Use the local ProgramInfo reference from CommandLineModuleManager
406     // (to do this nicely requires reordering the code in the file).
407     const char *const                    program =
408         ProgramInfo::getInstance().realBinaryName().c_str();
409     CommandLineModuleMap::const_iterator module;
410     for (module = modules.begin(); module != modules.end(); ++module)
411     {
412         if (module->second->shortDescription() != NULL)
413         {
414             std::string linkName("[gmx-" + module->first + "]");
415             std::string targetName(
416                     formatString("%s-%s", program, module->first.c_str()));
417             std::string displayName(
418                     formatString("[TT]%s %s[tt]", program, module->first.c_str()));
419             links->addLink(linkName, targetName, displayName);
420         }
421     }
422 }
423
424 /********************************************************************
425  * HelpExportMan
426  */
427
428 /*! \internal \brief
429  * Implements export for man pages.
430  *
431  * \ingroup module_commandline
432  */
433 class HelpExportMan : public HelpExportInterface
434 {
435     public:
436         //! Initializes man page exporter.
437         explicit HelpExportMan(const CommandLineModuleMap &modules)
438             : links_(eHelpOutputFormat_Man)
439         {
440             initProgramLinks(&links_, modules);
441         }
442
443         virtual void startModuleExport() {}
444         virtual void exportModuleHelp(
445             const CommandLineModuleInterface &module,
446             const std::string                &tag,
447             const std::string                &displayName);
448         virtual void finishModuleExport() {}
449
450         virtual void startModuleGroupExport();
451         virtual void exportModuleGroup(const char                *title,
452                                        const ModuleGroupContents &modules);
453         virtual void finishModuleGroupExport();
454
455     private:
456         HelpLinks                links_;
457         boost::scoped_ptr<File>  man7File_;
458         std::string              man7Footer_;
459 };
460
461 void HelpExportMan::exportModuleHelp(
462         const CommandLineModuleInterface &module,
463         const std::string                &tag,
464         const std::string                &displayName)
465 {
466     File file("man1/" + tag + ".1", "w");
467
468     // TODO: It would be nice to remove the VERSION prefix from the version
469     // string to make it shorter.
470     file.writeLine(formatString(".TH %s 1 \"\" \"%s\" \"GROMACS Manual\"\n",
471                                 tag.c_str(),
472                                 GromacsVersion()));
473     file.writeLine(".SH NAME");
474     file.writeLine(formatString("%s - %s", tag.c_str(),
475                                 module.shortDescription()));
476     file.writeLine();
477
478     CommandLineHelpContext context(&file, eHelpOutputFormat_Man, &links_);
479     context.setModuleDisplayName(displayName);
480     module.writeHelp(context);
481
482     file.writeLine(".SH SEE ALSO");
483     file.writeLine(".BR gromacs(7)");
484     file.writeLine();
485     file.writeLine("More information about \\fBGROMACS\\fR is available at <\\fIhttp://www.gromacs.org/\\fR>.");
486 }
487
488 void HelpExportMan::startModuleGroupExport()
489 {
490     const char *const programListPlaceholder = "@PROGMANPAGES@";
491
492     const std::string man7Template = gmx::File::readToString("man7/gromacs.7.in");
493     const size_t      index        = man7Template.find(programListPlaceholder);
494     GMX_RELEASE_ASSERT(index != std::string::npos,
495                        "gromacs.7.in must contain a @PROGMANPAGES@ line");
496     std::string header = man7Template.substr(0, index);
497     man7Footer_ = man7Template.substr(index + std::strlen(programListPlaceholder));
498     header      = replaceAll(header, "@VERSION@", GromacsVersion());
499     man7File_.reset(new File("man7/gromacs.7", "w"));
500     man7File_->writeLine(header);
501 }
502
503 void HelpExportMan::exportModuleGroup(const char                *title,
504                                       const ModuleGroupContents &modules)
505 {
506     man7File_->writeLine(formatString(".Sh \"%s\"", title));
507     man7File_->writeLine(formatString(".IX Subsection \"%s\"", title));
508     man7File_->writeLine(".Vb");
509     man7File_->writeLine(".ta 16n");
510
511     ModuleGroupContents::const_iterator module;
512     for (module = modules.begin(); module != modules.end(); ++module)
513     {
514         const std::string &tag(module->first);
515         man7File_->writeLine(formatString("\\&  %s\t%s",
516                                           tag.c_str(), module->second));
517     }
518
519     man7File_->writeLine(".Ve");
520 }
521
522 void HelpExportMan::finishModuleGroupExport()
523 {
524     man7File_->writeLine(man7Footer_);
525     man7File_->close();
526 }
527
528 /********************************************************************
529  * HelpExportHtml
530  */
531
532 /*! \internal \brief
533  * Implements export for HTML help.
534  *
535  * \ingroup module_commandline
536  */
537 class HelpExportHtml : public HelpExportInterface
538 {
539     public:
540         //! Initializes HTML exporter.
541         explicit HelpExportHtml(const CommandLineModuleMap &modules);
542
543         virtual void startModuleExport();
544         virtual void exportModuleHelp(
545             const CommandLineModuleInterface &module,
546             const std::string                &tag,
547             const std::string                &displayName);
548         virtual void finishModuleExport();
549
550         virtual void startModuleGroupExport();
551         virtual void exportModuleGroup(const char                *title,
552                                        const ModuleGroupContents &modules);
553         virtual void finishModuleGroupExport();
554
555     private:
556         void setupHeaderAndFooter();
557
558         void writeHtmlHeader(File *file, const std::string &title) const;
559         void writeHtmlFooter(File *file) const;
560
561         HelpLinks                links_;
562         boost::scoped_ptr<File>  indexFile_;
563         std::string              header_;
564         std::string              footer_;
565 };
566
567 HelpExportHtml::HelpExportHtml(const CommandLineModuleMap &modules)
568     : links_(eHelpOutputFormat_Html)
569 {
570     initProgramLinks(&links_, modules);
571     char *linksFilename = low_gmxlibfn("links.dat", FALSE, FALSE);
572     if (linksFilename != NULL)
573     {
574         scoped_ptr_sfree guard(linksFilename);
575         File             linksFile(linksFilename, "r");
576         std::string      line;
577         while (linksFile.readLine(&line))
578         {
579             links_.addLink(line, "../online/" + line, line);
580         }
581     }
582     setupHeaderAndFooter();
583 }
584
585 void HelpExportHtml::setupHeaderAndFooter()
586 {
587     header_ = gmx::File::readToString("header.html.in");
588     header_ = replaceAll(header_, "@VERSION@", GromacsVersion());
589     gmx::File::writeFileFromString("header.html", header_);
590     header_ = replaceAll(header_, "@ROOTPATH@", "../");
591     footer_ = gmx::File::readToString("footer.html");
592 }
593
594 void HelpExportHtml::startModuleExport()
595 {
596     indexFile_.reset(new File("final/programs/byname.html", "w"));
597     writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Name");
598     indexFile_->writeLine("<H3>GROMACS Programs Alphabetically</H3>");
599 }
600
601 void HelpExportHtml::exportModuleHelp(
602         const CommandLineModuleInterface &module,
603         const std::string                &tag,
604         const std::string                &displayName)
605 {
606     File file("final/programs/" + tag + ".html", "w");
607     writeHtmlHeader(&file, displayName);
608
609     CommandLineHelpContext context(&file, eHelpOutputFormat_Html, &links_);
610     context.setModuleDisplayName(displayName);
611     module.writeHelp(context);
612
613     writeHtmlFooter(&file);
614     file.close();
615
616     indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
617                                        tag.c_str(), displayName.c_str(),
618                                        module.shortDescription()));
619 }
620
621 void HelpExportHtml::finishModuleExport()
622 {
623     writeHtmlFooter(indexFile_.get());
624     indexFile_->close();
625 }
626
627 void HelpExportHtml::startModuleGroupExport()
628 {
629     indexFile_.reset(new File("final/programs/bytopic.html", "w"));
630     writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Topic");
631     indexFile_->writeLine("<H3>GROMACS Programs by Topic</H3>");
632 }
633
634 void HelpExportHtml::exportModuleGroup(const char                *title,
635                                        const ModuleGroupContents &modules)
636 {
637     indexFile_->writeLine(formatString("<H4>%s</H4>", title));
638
639     ModuleGroupContents::const_iterator module;
640     for (module = modules.begin(); module != modules.end(); ++module)
641     {
642         const std::string     &tag(module->first);
643         std::string            displayName(tag);
644         std::replace(displayName.begin(), displayName.end(), '-', ' ');
645         indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
646                                            tag.c_str(), displayName.c_str(),
647                                            module->second));
648     }
649 }
650
651 void HelpExportHtml::finishModuleGroupExport()
652 {
653     writeHtmlFooter(indexFile_.get());
654     indexFile_->close();
655 }
656
657 void HelpExportHtml::writeHtmlHeader(File *file, const std::string &title) const
658 {
659     file->writeLine(replaceAll(header_, "@TITLE@", title));
660 }
661
662 void HelpExportHtml::writeHtmlFooter(File *file) const
663 {
664     file->writeLine(footer_);
665 }
666
667 }   // namespace
668
669 /********************************************************************
670  * CommandLineHelpModule
671  */
672
673 /*! \internal
674  * \brief
675  * Command-line module for producing help.
676  *
677  * This module implements the 'help' subcommand that is automatically added by
678  * CommandLineModuleManager.
679  *
680  * \ingroup module_commandline
681  */
682 class CommandLineHelpModule : public CommandLineModuleInterface
683 {
684     public:
685         /*! \brief
686          * Creates a command-line help module.
687          *
688          * \param[in] programInfo Information about the running binary.
689          * \param[in] modules  List of modules for to use for module listings.
690          * \param[in] groups   List of module groups.
691          * \throws    std::bad_alloc if out of memory.
692          */
693         CommandLineHelpModule(const ProgramInfo                &programInfo,
694                               const CommandLineModuleMap       &modules,
695                               const CommandLineModuleGroupList &groups);
696
697         /*! \brief
698          * Adds a top-level help topic.
699          *
700          * \param[in] topic  Help topic to add.
701          * \throws    std::bad_alloc if out of memory.
702          */
703         void addTopic(HelpTopicPointer topic);
704         //! Sets whether hidden options will be shown in help.
705         void setShowHidden(bool bHidden) { bHidden_ = bHidden; }
706         /*! \brief
707          * Sets an override to show the help for the given module.
708          *
709          * If called, the help module directly prints the help for the given
710          * module when called, skipping any other processing.
711          */
712         void setModuleOverride(const CommandLineModuleInterface &module)
713         {
714             moduleOverride_ = &module;
715         }
716
717         //! Returns the context object for help output.
718         const CommandLineHelpContext &context() const
719         {
720             return *context_;
721         }
722         //! Returns the program info object for the running binary.
723         const ProgramInfo &programInfo() const
724         {
725             return programInfo_;
726         }
727
728         virtual const char *name() const { return "help"; }
729         virtual const char *shortDescription() const
730         {
731             return "Print help information";
732         }
733
734         virtual int run(int argc, char *argv[]);
735         virtual void writeHelp(const CommandLineHelpContext &context) const;
736
737     private:
738         void exportHelp(HelpExportInterface *exporter) const;
739
740         boost::scoped_ptr<RootHelpTopic>  rootTopic_;
741         const ProgramInfo                &programInfo_;
742         const CommandLineModuleMap       &modules_;
743         const CommandLineModuleGroupList &groups_;
744
745         CommandLineHelpContext           *context_;
746         const CommandLineModuleInterface *moduleOverride_;
747         bool                              bHidden_;
748
749         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
750 };
751
752 CommandLineHelpModule::CommandLineHelpModule(
753         const ProgramInfo                &programInfo,
754         const CommandLineModuleMap       &modules,
755         const CommandLineModuleGroupList &groups)
756     : rootTopic_(new RootHelpTopic(modules)), programInfo_(programInfo),
757       modules_(modules), groups_(groups),
758       context_(NULL), moduleOverride_(NULL), bHidden_(false)
759 {
760 }
761
762 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
763 {
764     rootTopic_->addSubTopic(move(topic));
765 }
766
767 int CommandLineHelpModule::run(int argc, char *argv[])
768 {
769     const char *const exportFormats[] = { "man", "html", "completion" };
770     std::string       exportFormat;
771     Options           options(NULL, NULL);
772     options.addOption(StringOption("export").store(&exportFormat)
773                           .enumValue(exportFormats));
774     CommandLineParser(&options).parse(&argc, argv);
775     if (!exportFormat.empty())
776     {
777         boost::scoped_ptr<HelpExportInterface> exporter;
778         if (exportFormat == "man")
779         {
780             exporter.reset(new HelpExportMan(modules_));
781         }
782         else if (exportFormat == "html")
783         {
784             exporter.reset(new HelpExportHtml(modules_));
785         }
786         else
787         {
788             GMX_THROW(NotImplementedError("This help format is not implemented"));
789         }
790         exportHelp(exporter.get());
791         return 0;
792     }
793
794     HelpLinks                                 links(eHelpOutputFormat_Console);
795     initProgramLinks(&links, modules_);
796     boost::scoped_ptr<CommandLineHelpContext> context(
797             new CommandLineHelpContext(&File::standardOutput(),
798                                        eHelpOutputFormat_Console, &links));
799     context->setShowHidden(bHidden_);
800     if (moduleOverride_ != NULL)
801     {
802         context->setModuleDisplayName(programInfo_.displayName());
803         moduleOverride_->writeHelp(*context);
804         return 0;
805     }
806     context_ = context.get();
807
808     HelpManager       helpManager(*rootTopic_, context->writerContext());
809     try
810     {
811         for (int i = 1; i < argc; ++i)
812         {
813             helpManager.enterTopic(argv[i]);
814         }
815     }
816     catch (const InvalidInputError &ex)
817     {
818         fprintf(stderr, "%s\n", ex.what());
819         return 2;
820     }
821     helpManager.writeCurrentTopic();
822     return 0;
823 }
824
825 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
826 {
827     const HelpWriterContext &writerContext = context.writerContext();
828     // TODO: Implement.
829     if (writerContext.outputFormat() != eHelpOutputFormat_Console)
830     {
831         return;
832     }
833     writerContext.writeTextBlock(
834             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
835     // TODO: More information.
836 }
837
838 void CommandLineHelpModule::exportHelp(HelpExportInterface *exporter) const
839 {
840     // TODO: Would be nicer to have the file names supplied by the build system
841     // and/or export a list of files from here.
842     const char *const program = programInfo_.realBinaryName().c_str();
843
844     exporter->startModuleExport();
845     CommandLineModuleMap::const_iterator module;
846     for (module = modules_.begin(); module != modules_.end(); ++module)
847     {
848         if (module->second->shortDescription() != NULL)
849         {
850             const char *const moduleName = module->first.c_str();
851             std::string       tag(formatString("%s-%s", program, moduleName));
852             std::string       displayName(tag);
853             std::replace(displayName.begin(), displayName.end(), '-', ' ');
854             exporter->exportModuleHelp(*module->second, tag, displayName);
855         }
856     }
857     exporter->finishModuleExport();
858
859     exporter->startModuleGroupExport();
860     CommandLineModuleGroupList::const_iterator group;
861     for (group = groups_.begin(); group != groups_.end(); ++group)
862     {
863         exporter->exportModuleGroup((*group)->title(), (*group)->modules());
864     }
865     exporter->finishModuleGroupExport();
866 }
867
868 namespace
869 {
870
871 /********************************************************************
872  * ModuleHelpTopic implementation
873  */
874
875 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
876 {
877     CommandLineHelpContext context(helpModule_.context());
878     const char *const      program =
879         helpModule_.programInfo().realBinaryName().c_str();
880     context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
881     module_.writeHelp(context);
882 }
883
884 /********************************************************************
885  * CMainCommandLineModule
886  */
887
888 /*! \internal \brief
889  * Implements a CommandLineModuleInterface, given a function with C/C++ main()
890  * signature.
891  *
892  * \ingroup module_commandline
893  */
894 class CMainCommandLineModule : public CommandLineModuleInterface
895 {
896     public:
897         //! \copydoc gmx::CommandLineModuleManager::CMainFunction
898         typedef CommandLineModuleManager::CMainFunction CMainFunction;
899
900         /*! \brief
901          * Creates a wrapper module for the given main function.
902          *
903          * \param[in] name             Name for the module.
904          * \param[in] shortDescription One-line description for the module.
905          * \param[in] mainFunction     Main function to wrap.
906          *
907          * Does not throw.  This is essential for correct implementation of
908          * CommandLineModuleManager::runAsMainCMain().
909          */
910         CMainCommandLineModule(const char *name, const char *shortDescription,
911                                CMainFunction mainFunction)
912             : name_(name), shortDescription_(shortDescription),
913               mainFunction_(mainFunction)
914         {
915         }
916
917         virtual const char *name() const
918         {
919             return name_;
920         }
921         virtual const char *shortDescription() const
922         {
923             return shortDescription_;
924         }
925
926         virtual int run(int argc, char *argv[])
927         {
928             return mainFunction_(argc, argv);
929         }
930         virtual void writeHelp(const CommandLineHelpContext &context) const
931         {
932             const HelpOutputFormat format = context.writerContext().outputFormat();
933             const char            *type;
934             switch (format)
935             {
936                 case eHelpOutputFormat_Console:
937                     type = "help";
938                     break;
939                 case eHelpOutputFormat_Man:
940                     type = "nroff";
941                     break;
942                 case eHelpOutputFormat_Html:
943                     type = "html";
944                     break;
945                 default:
946                     GMX_THROW(NotImplementedError(
947                                       "Command-line help is not implemented for this output format"));
948             }
949             char *argv[4];
950             int   argc = 3;
951             // TODO: The constness should not be cast away.
952             argv[0] = const_cast<char *>(name_);
953             argv[1] = const_cast<char *>("-man");
954             argv[2] = const_cast<char *>(type);
955             argv[3] = NULL;
956             GlobalCommandLineHelpContext global(context);
957             mainFunction_(argc, argv);
958         }
959
960     private:
961         const char             *name_;
962         const char             *shortDescription_;
963         CMainFunction           mainFunction_;
964
965 };
966
967 }   // namespace
968
969 /********************************************************************
970  * CommandLineModuleManager::Impl
971  */
972
973 /*! \internal \brief
974  * Private implementation class for CommandLineModuleManager.
975  *
976  * \ingroup module_commandline
977  */
978 class CommandLineModuleManager::Impl
979 {
980     public:
981         /*! \brief
982          * Initializes the implementation class.
983          *
984          * \param     programInfo  Program information for the running binary.
985          */
986         explicit Impl(ProgramInfo *programInfo);
987
988         /*! \brief
989          * Helper method that adds a given module to the module manager.
990          *
991          * \throws    std::bad_alloc if out of memory.
992          */
993         void addModule(CommandLineModulePointer module);
994         /*! \brief
995          * Creates the help module if it does not yet exist.
996          *
997          * \throws    std::bad_alloc if out of memory.
998          *
999          * This method should be called before accessing \a helpModule_.
1000          */
1001         void ensureHelpModuleExists();
1002
1003         /*! \brief
1004          * Finds a module that matches a name.
1005          *
1006          * \param[in] name  Module name to find.
1007          * \returns   Iterator to the found module, or
1008          *      \c modules_.end() if not found.
1009          *
1010          * Does not throw.
1011          */
1012         CommandLineModuleMap::const_iterator
1013         findModuleByName(const std::string &name) const;
1014         /*! \brief
1015          * Finds a module that the name of the binary.
1016          *
1017          * \param[in] programInfo  Program information object to use.
1018          * \throws    std::bad_alloc if out of memory.
1019          * \returns   Iterator to the found module, or
1020          *      \c modules_.end() if not found.
1021          *
1022          * Checks whether the program is invoked through a symlink whose name
1023          * is different from ProgramInfo::realBinaryName(), and if so, checks
1024          * if a module name matches the name of the symlink.
1025          *
1026          * Note that the \p programInfo parameter is currently not necessary
1027          * (as the program info object is also contained as a member), but it
1028          * clarifies the control flow.
1029          */
1030         CommandLineModuleMap::const_iterator
1031         findModuleFromBinaryName(const ProgramInfo &programInfo) const;
1032
1033         /*! \brief
1034          * Processes command-line options for the wrapper binary.
1035          *
1036          * \param[in,out] argc On input, argc passed to run().
1037          *     On output, argc to be passed to the module.
1038          * \param[in,out] argv On input, argv passed to run().
1039          *     On output, argv to be passed to the module.
1040          * \throws    InvalidInputError if there are invalid options.
1041          * \returns   The module that should be run.
1042          *
1043          * Handles command-line options that affect the wrapper binary
1044          * (potentially changing the members of \c this in response to the
1045          * options).  Also finds the module that should be run and the
1046          * arguments that should be passed to it.
1047          */
1048         CommandLineModuleInterface *
1049         processCommonOptions(int *argc, char ***argv);
1050
1051         /*! \brief
1052          * Maps module names to module objects.
1053          *
1054          * Owns the contained modules.
1055          */
1056         CommandLineModuleMap         modules_;
1057         /*! \brief
1058          * List of groupings for modules for help output.
1059          *
1060          * Owns the contained module group data objects.
1061          * CommandLineModuleGroup objects point to the data objects contained
1062          * here.
1063          */
1064         CommandLineModuleGroupList   moduleGroups_;
1065         //! Information about the currently running program.
1066         ProgramInfo                 &programInfo_;
1067         /*! \brief
1068          * Module that implements help for the binary.
1069          *
1070          * The pointed module is owned by the \a modules_ container.
1071          */
1072         CommandLineHelpModule       *helpModule_;
1073         //! Settings for what to write in the startup header.
1074         BinaryInformationSettings    binaryInfoSettings_;
1075         //! If non-NULL, run this module in single-module mode.
1076         CommandLineModuleInterface  *singleModule_;
1077         //! Whether all stderr output should be suppressed.
1078         bool                         bQuiet_;
1079         //! Whether to write the startup information to stdout iso stderr.
1080         bool                         bStdOutInfo_;
1081
1082     private:
1083         GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
1084 };
1085
1086 CommandLineModuleManager::Impl::Impl(ProgramInfo *programInfo)
1087     : programInfo_(*programInfo), helpModule_(NULL), singleModule_(NULL),
1088       bQuiet_(false), bStdOutInfo_(false)
1089 {
1090     binaryInfoSettings_.copyright(true);
1091 }
1092
1093 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
1094 {
1095     GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
1096                "Attempted to register a duplicate module name");
1097     ensureHelpModuleExists();
1098     HelpTopicPointer helpTopic(new ModuleHelpTopic(*module, *helpModule_));
1099     modules_.insert(std::make_pair(std::string(module->name()),
1100                                    move(module)));
1101     helpModule_->addTopic(move(helpTopic));
1102 }
1103
1104 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
1105 {
1106     if (helpModule_ == NULL)
1107     {
1108         helpModule_ = new CommandLineHelpModule(programInfo_, modules_,
1109                                                 moduleGroups_);
1110         addModule(CommandLineModulePointer(helpModule_));
1111     }
1112 }
1113
1114 CommandLineModuleMap::const_iterator
1115 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
1116 {
1117     // TODO: Accept unambiguous prefixes?
1118     return modules_.find(name);
1119 }
1120
1121 CommandLineModuleMap::const_iterator
1122 CommandLineModuleManager::Impl::findModuleFromBinaryName(
1123         const ProgramInfo &programInfo) const
1124 {
1125     std::string binaryName = programInfo.invariantProgramName();
1126     if (binaryName == programInfo.realBinaryName())
1127     {
1128         return modules_.end();
1129     }
1130     if (binaryName.compare(0, 2, "g_") == 0)
1131     {
1132         binaryName.erase(0, 2);
1133     }
1134     if (binaryName.compare(0, 3, "gmx") == 0)
1135     {
1136         binaryName.erase(0, 3);
1137     }
1138     return findModuleByName(binaryName);
1139 }
1140
1141 CommandLineModuleInterface *
1142 CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
1143 {
1144     // Check if we are directly invoking a certain module.
1145     CommandLineModuleInterface *module = singleModule_;
1146     if (module == NULL)
1147     {
1148         // Also check for invokation through named symlinks.
1149         CommandLineModuleMap::const_iterator moduleIter
1150             = findModuleFromBinaryName(programInfo_);
1151         if (moduleIter != modules_.end())
1152         {
1153             module = moduleIter->second.get();
1154         }
1155     }
1156
1157     bool bHelp      = false;
1158     bool bHidden    = false;
1159     bool bVersion   = false;
1160     bool bCopyright = true;
1161     // TODO: Print the common options into the help.
1162     // TODO: It would be nice to propagate at least the -quiet option to
1163     // the modules so that they can also be quiet in response to this.
1164     Options options(NULL, NULL);
1165     options.addOption(BooleanOption("h").store(&bHelp));
1166     options.addOption(BooleanOption("hidden").store(&bHidden));
1167     options.addOption(BooleanOption("quiet").store(&bQuiet_));
1168     options.addOption(BooleanOption("version").store(&bVersion));
1169     options.addOption(BooleanOption("copyright").store(&bCopyright));
1170
1171     if (module == NULL)
1172     {
1173         // If not in single-module mode, process options to the wrapper binary.
1174         // TODO: Ideally, this could be done by CommandLineParser.
1175         int argcForWrapper = 1;
1176         while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
1177         {
1178             ++argcForWrapper;
1179         }
1180         if (argcForWrapper > 1)
1181         {
1182             CommandLineParser(&options).parse(&argcForWrapper, *argv);
1183         }
1184         // If no action requested and there is a module specified, process it.
1185         if (argcForWrapper < *argc && !bHelp && !bVersion)
1186         {
1187             const char *moduleName = (*argv)[argcForWrapper];
1188             CommandLineModuleMap::const_iterator moduleIter
1189                 = findModuleByName(moduleName);
1190             if (moduleIter == modules_.end())
1191             {
1192                 std::string message =
1193                     formatString("'%s' is not a GROMACS command.", moduleName);
1194                 GMX_THROW(InvalidInputError(message));
1195             }
1196             module = moduleIter->second.get();
1197             *argc -= argcForWrapper;
1198             *argv += argcForWrapper;
1199             // After this point, argc and argv are the same independent of
1200             // which path is taken: (*argv)[0] is the module name.
1201         }
1202     }
1203     if (module != NULL)
1204     {
1205         if (singleModule_ == NULL)
1206         {
1207             programInfo_.setDisplayName(
1208                     programInfo_.realBinaryName() + " " + module->name());
1209         }
1210         // Recognize the common options also after the module name.
1211         // TODO: It could be nicer to only recognize -h/-hidden if module is not
1212         // null.
1213         CommandLineParser(&options).skipUnknown(true).parse(argc, *argv);
1214     }
1215     options.finish();
1216     binaryInfoSettings_.extendedInfo(bVersion);
1217     binaryInfoSettings_.copyright(bCopyright);
1218     if (bVersion)
1219     {
1220         bQuiet_      = false;
1221         bStdOutInfo_ = true;
1222         return NULL;
1223     }
1224     // If no module specified and no other action, show the help.
1225     // Also explicitly specifying -h for the wrapper binary goes here.
1226     if (module == NULL || bHelp)
1227     {
1228         ensureHelpModuleExists();
1229         if (module != NULL)
1230         {
1231             helpModule_->setModuleOverride(*module);
1232         }
1233         *argc  = 1;
1234         module = helpModule_;
1235     }
1236     if (module == helpModule_)
1237     {
1238         helpModule_->setShowHidden(bHidden);
1239     }
1240     return module;
1241 }
1242
1243 /********************************************************************
1244  * CommandLineModuleManager
1245  */
1246
1247 CommandLineModuleManager::CommandLineModuleManager(ProgramInfo *programInfo)
1248     : impl_(new Impl(programInfo))
1249 {
1250 }
1251
1252 CommandLineModuleManager::~CommandLineModuleManager()
1253 {
1254 }
1255
1256 void CommandLineModuleManager::setQuiet(bool bQuiet)
1257 {
1258     impl_->bQuiet_ = bQuiet;
1259 }
1260
1261 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
1262 {
1263     impl_->singleModule_ = module;
1264 }
1265
1266 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
1267 {
1268     impl_->addModule(move(module));
1269 }
1270
1271 void CommandLineModuleManager::addModuleCMain(
1272         const char *name, const char *shortDescription,
1273         CMainFunction mainFunction)
1274 {
1275     CommandLineModulePointer module(
1276             new CMainCommandLineModule(name, shortDescription, mainFunction));
1277     addModule(move(module));
1278 }
1279
1280 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
1281         const char *title)
1282 {
1283     CommandLineModuleGroupDataPointer group(
1284             new internal::CommandLineModuleGroupData(impl_->modules_, title));
1285     impl_->moduleGroups_.push_back(move(group));
1286     return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
1287 }
1288
1289 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
1290 {
1291     impl_->ensureHelpModuleExists();
1292     impl_->helpModule_->addTopic(move(topic));
1293 }
1294
1295 int CommandLineModuleManager::run(int argc, char *argv[])
1296 {
1297     CommandLineModuleInterface *module;
1298     const bool                  bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
1299     try
1300     {
1301         module = impl_->processCommonOptions(&argc, &argv);
1302     }
1303     catch (const std::exception &)
1304     {
1305         if (bMaster && !impl_->bQuiet_)
1306         {
1307             printBinaryInformation(stderr, impl_->programInfo_,
1308                                    impl_->binaryInfoSettings_);
1309         }
1310         throw;
1311     }
1312     if (!bMaster)
1313     {
1314         impl_->bQuiet_ = true;
1315     }
1316     if (!impl_->bQuiet_)
1317     {
1318         FILE *out = (impl_->bStdOutInfo_ ? stdout : stderr);
1319         printBinaryInformation(out, impl_->programInfo_,
1320                                impl_->binaryInfoSettings_);
1321         fprintf(out, "\n");
1322     }
1323     if (module == NULL)
1324     {
1325         return 0;
1326     }
1327     int rc = module->run(argc, argv);
1328     if (!impl_->bQuiet_)
1329     {
1330         gmx_thanx(stderr);
1331     }
1332     return rc;
1333 }
1334
1335 // static
1336 int CommandLineModuleManager::runAsMainSingleModule(
1337         int argc, char *argv[], CommandLineModuleInterface *module)
1338 {
1339     ProgramInfo &programInfo = gmx::init(&argc, &argv);
1340     try
1341     {
1342         CommandLineModuleManager manager(&programInfo);
1343         manager.setSingleModule(module);
1344         int rc = manager.run(argc, argv);
1345         gmx::finalize();
1346         return rc;
1347     }
1348     catch (const std::exception &ex)
1349     {
1350         printFatalErrorMessage(stderr, ex);
1351         return processExceptionAtExit(ex);
1352     }
1353 }
1354
1355 // static
1356 int CommandLineModuleManager::runAsMainCMain(
1357         int argc, char *argv[], CMainFunction mainFunction)
1358 {
1359     CMainCommandLineModule module(argv[0], NULL, mainFunction);
1360     return runAsMainSingleModule(argc, argv, &module);
1361 }
1362
1363 /********************************************************************
1364  * CommandLineModuleGroup
1365  */
1366
1367 void CommandLineModuleGroup::addModule(const char *name)
1368 {
1369     impl_->addModule(name, NULL);
1370 }
1371
1372 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
1373                                                       const char *description)
1374 {
1375     impl_->addModule(name, description);
1376 }
1377
1378 } // namespace gmx