Merge release-5-0 into master
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinehelpmodule.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014, 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::CommandLineHelpModule.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_commandline
41  */
42 #include "gromacs/commandline/cmdlinehelpmodule.h"
43
44 #include <string>
45 #include <vector>
46
47 #include <boost/scoped_ptr.hpp>
48
49 #include "gromacs/commandline/cmdlinehelpcontext.h"
50 #include "gromacs/commandline/cmdlinehelpwriter.h"
51 #include "gromacs/commandline/cmdlineparser.h"
52 #include "gromacs/commandline/shellcompletions.h"
53 #include "gromacs/onlinehelp/helpformat.h"
54 #include "gromacs/onlinehelp/helpmanager.h"
55 #include "gromacs/onlinehelp/helptopic.h"
56 #include "gromacs/onlinehelp/helpwritercontext.h"
57 #include "gromacs/options/basicoptions.h"
58 #include "gromacs/options/options.h"
59 #include "gromacs/utility/baseversion.h"
60 #include "gromacs/utility/exceptions.h"
61 #include "gromacs/utility/file.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/programcontext.h"
64 #include "gromacs/utility/stringutil.h"
65
66 namespace gmx
67 {
68
69 namespace
70 {
71 class HelpExportInterface;
72 class RootHelpTopic;
73 }   // namespace
74
75 /********************************************************************
76  * CommandLineHelpModuleImpl declaration
77  */
78
79 class CommandLineHelpModuleImpl
80 {
81     public:
82         CommandLineHelpModuleImpl(const ProgramContextInterface    &programContext,
83                                   const std::string                &binaryName,
84                                   const CommandLineModuleMap       &modules,
85                                   const CommandLineModuleGroupList &groups);
86
87         void exportHelp(HelpExportInterface *exporter) const;
88
89         boost::scoped_ptr<RootHelpTopic>  rootTopic_;
90         const ProgramContextInterface    &programContext_;
91         std::string                       binaryName_;
92         const CommandLineModuleMap       &modules_;
93         const CommandLineModuleGroupList &groups_;
94
95         CommandLineHelpContext           *context_;
96         const CommandLineModuleInterface *moduleOverride_;
97         bool                              bHidden_;
98
99         File                             *outputOverride_;
100
101         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
102 };
103
104 namespace
105 {
106
107 /********************************************************************
108  * RootHelpTopic
109  */
110
111 struct RootHelpText
112 {
113     static const char        name[];
114     static const char        title[];
115     static const char *const text[];
116 };
117
118 // The first two are not used.
119 const char        RootHelpText::name[]  = "";
120 const char        RootHelpText::title[] = "";
121 const char *const RootHelpText::text[]  = { "" };
122
123 /*! \brief
124  * Help topic that forms the root of the help tree for the help subcommand.
125  *
126  * \ingroup module_commandline
127  */
128 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
129 {
130     public:
131         /*! \brief
132          * Creates a root help topic.
133          *
134          * Does not throw.
135          */
136         explicit RootHelpTopic(const CommandLineHelpModuleImpl &helpModule)
137             : helpModule_(helpModule)
138         {
139         }
140
141         virtual void writeHelp(const HelpWriterContext &context) const;
142
143     private:
144         const CommandLineHelpModuleImpl  &helpModule_;
145
146         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
147 };
148
149 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
150 {
151     if (context.outputFormat() != eHelpOutputFormat_Console)
152     {
153         // TODO: Implement once the situation with Redmine issue #969 is more
154         // clear.
155         GMX_THROW(NotImplementedError(
156                           "Root help is not implemented for this output format"));
157     }
158     {
159         CommandLineCommonOptionsHolder optionsHolder;
160         CommandLineHelpContext         cmdlineContext(*helpModule_.context_);
161         cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
162         optionsHolder.initOptions();
163         // TODO: Add <command> [<args>] into the synopsis.
164         CommandLineHelpWriter(*optionsHolder.options())
165             .writeHelp(cmdlineContext);
166     }
167     // TODO: Consider printing a list of "core" commands. Would require someone
168     // to determine such a set...
169     writeSubTopicList(context,
170                       "Additional help is available on the following topics:");
171     context.writeTextBlock(
172             "To access the help, use '[PROGRAM] help <topic>'.[BR]"
173             "For help on a command, use '[PROGRAM] help <command>'.");
174 }
175
176 /********************************************************************
177  * CommandsHelpTopic
178  */
179
180 /*! \brief
181  * Help topic for listing the commands.
182  *
183  * \ingroup module_commandline
184  */
185 class CommandsHelpTopic : public HelpTopicInterface
186 {
187     public:
188         /*! \brief
189          * Creates a command list help topic.
190          *
191          * \param[in]     helpModule Help module to get module information from.
192          *
193          * Does not throw.
194          */
195         explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
196             : helpModule_(helpModule)
197         {
198         }
199
200         virtual const char *name() const { return "commands"; }
201         virtual const char *title() const { return "List of available commands"; }
202         virtual bool hasSubTopics() const { return false; }
203         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
204         {
205             return NULL;
206         }
207
208         virtual void writeHelp(const HelpWriterContext &context) const;
209
210     private:
211         const CommandLineHelpModuleImpl &helpModule_;
212
213         GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
214 };
215
216 void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
217 {
218     if (context.outputFormat() != eHelpOutputFormat_Console)
219     {
220         GMX_THROW(NotImplementedError(
221                           "Module list is not implemented for this output format"));
222     }
223     int maxNameLength = 0;
224     const CommandLineModuleMap           &modules = helpModule_.modules_;
225     CommandLineModuleMap::const_iterator  module;
226     for (module = modules.begin(); module != modules.end(); ++module)
227     {
228         int nameLength = static_cast<int>(module->first.length());
229         if (module->second->shortDescription() != NULL
230             && nameLength > maxNameLength)
231         {
232             maxNameLength = nameLength;
233         }
234     }
235     context.writeTextBlock(
236             "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
237             "Available commands:");
238     File              &file = context.outputFile();
239     TextTableFormatter formatter;
240     formatter.addColumn(NULL, maxNameLength + 1, false);
241     formatter.addColumn(NULL, 72 - maxNameLength, true);
242     formatter.setFirstColumnIndent(4);
243     for (module = modules.begin(); module != modules.end(); ++module)
244     {
245         const char *name        = module->first.c_str();
246         const char *description = module->second->shortDescription();
247         if (description != NULL)
248         {
249             formatter.clear();
250             formatter.addColumnLine(0, name);
251             formatter.addColumnLine(1, description);
252             file.writeString(formatter.formatRow());
253         }
254     }
255     context.writeTextBlock(
256             "For help on a command, use '[PROGRAM] help <command>'.");
257 }
258
259 /********************************************************************
260  * ModuleHelpTopic
261  */
262
263 /*! \brief
264  * Help topic wrapper for a command-line module.
265  *
266  * This class implements HelpTopicInterface such that it wraps a
267  * CommandLineModuleInterface, allowing subcommand "help <command>"
268  * to produce the help for "<command>".
269  *
270  * \ingroup module_commandline
271  */
272 class ModuleHelpTopic : public HelpTopicInterface
273 {
274     public:
275         //! Constructs a help topic for a specific module.
276         ModuleHelpTopic(const CommandLineModuleInterface &module,
277                         const CommandLineHelpModuleImpl  &helpModule)
278             : module_(module), helpModule_(helpModule)
279         {
280         }
281
282         virtual const char *name() const { return module_.name(); }
283         virtual const char *title() const { return NULL; }
284         virtual bool hasSubTopics() const { return false; }
285         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
286         {
287             return NULL;
288         }
289         virtual void writeHelp(const HelpWriterContext &context) const;
290
291     private:
292         const CommandLineModuleInterface &module_;
293         const CommandLineHelpModuleImpl  &helpModule_;
294
295         GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
296 };
297
298 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
299 {
300     CommandLineHelpContext context(*helpModule_.context_);
301     const char *const      program = helpModule_.binaryName_.c_str();
302     context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
303     module_.writeHelp(context);
304 }
305
306 /********************************************************************
307  * HelpExportInterface
308  */
309
310 /*! \brief
311  * Callbacks for exporting help information for command-line modules.
312  *
313  * \ingroup module_commandline
314  */
315 class HelpExportInterface
316 {
317     public:
318         //! Shorthand for a list of modules contained in a group.
319         typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
320
321         virtual ~HelpExportInterface() {};
322
323         /*! \brief
324          * Called once before exporting individual modules.
325          *
326          * Can, e.g., open shared output files (e.g., if the output is written
327          * into a single file, or if a separate index is required) and write
328          * headers into them.
329          */
330         virtual void startModuleExport() = 0;
331         /*! \brief
332          * Called to export the help for each module.
333          *
334          * \param[in] module      Module for which the help should be exported.
335          * \param[in] tag         Unique tag for the module (gmx-something).
336          * \param[in] displayName Display name for the module (gmx something).
337          */
338         virtual void exportModuleHelp(
339             const CommandLineModuleInterface &module,
340             const std::string                &tag,
341             const std::string                &displayName) = 0;
342         /*! \brief
343          * Called after all modules have been exported.
344          *
345          * Can close files opened in startModuleExport(), write footers to them
346          * etc.
347          */
348         virtual void finishModuleExport() = 0;
349
350         /*! \brief
351          * Called once before exporting module groups.
352          *
353          * Can, e.g., open a single output file for listing all the groups.
354          */
355         virtual void startModuleGroupExport() = 0;
356         /*! \brief
357          * Called to export the help for each module group.
358          *
359          * \param[in] title    Title for the group.
360          * \param[in] modules  List of modules in the group.
361          */
362         virtual void exportModuleGroup(const char                *title,
363                                        const ModuleGroupContents &modules) = 0;
364         /*! \brief
365          * Called after all module groups have been exported.
366          *
367          * Can close files opened in startModuleGroupExport(), write footers to them
368          * etc.
369          */
370         virtual void finishModuleGroupExport() = 0;
371 };
372
373 /*! \internal \brief
374  * Adds hyperlinks to modules within this binary.
375  *
376  * \param[in,out] links      Links are added here.
377  * \param[in]     helpModule Help module to get module information from.
378  * \throws        std::bad_alloc if out of memory.
379  *
380  * Initializes a HelpLinks object with links to modules defined in
381  * \p helpModule.
382  *
383  * \ingroup module_commandline
384  */
385 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
386 {
387     const char *const                    program = helpModule.binaryName_.c_str();
388     CommandLineModuleMap::const_iterator module;
389     for (module = helpModule.modules_.begin();
390          module != helpModule.modules_.end();
391          ++module)
392     {
393         if (module->second->shortDescription() != NULL)
394         {
395             std::string linkName("[gmx-" + module->first + "]");
396             std::string targetName(
397                     formatString("%s-%s", program, module->first.c_str()));
398             std::string displayName(
399                     formatString("[TT]%s %s[tt]", program, module->first.c_str()));
400             links->addLink(linkName, targetName, displayName);
401         }
402     }
403 }
404
405 /********************************************************************
406  * HelpExportMan
407  */
408
409 /*! \internal \brief
410  * Implements export for man pages.
411  *
412  * \ingroup module_commandline
413  */
414 class HelpExportMan : public HelpExportInterface
415 {
416     public:
417         //! Initializes man page exporter.
418         explicit HelpExportMan(const CommandLineHelpModuleImpl &helpModule)
419             : links_(eHelpOutputFormat_Man)
420         {
421             initProgramLinks(&links_, helpModule);
422         }
423
424         virtual void startModuleExport() {}
425         virtual void exportModuleHelp(
426             const CommandLineModuleInterface &module,
427             const std::string                &tag,
428             const std::string                &displayName);
429         virtual void finishModuleExport() {}
430
431         virtual void startModuleGroupExport();
432         virtual void exportModuleGroup(const char                *title,
433                                        const ModuleGroupContents &modules);
434         virtual void finishModuleGroupExport();
435
436     private:
437         HelpLinks                links_;
438         boost::scoped_ptr<File>  man7File_;
439         std::string              man7Footer_;
440 };
441
442 void HelpExportMan::exportModuleHelp(
443         const CommandLineModuleInterface &module,
444         const std::string                &tag,
445         const std::string                &displayName)
446 {
447     File file("man1/" + tag + ".1", "w");
448
449     // TODO: It would be nice to remove the VERSION prefix from the version
450     // string to make it shorter.
451     file.writeLine(formatString(".TH %s 1 \"\" \"%s\" \"GROMACS Manual\"\n",
452                                 tag.c_str(), gmx_version()));
453     file.writeLine(".SH NAME");
454     file.writeLine(formatString("%s - %s", tag.c_str(),
455                                 module.shortDescription()));
456     file.writeLine();
457
458     CommandLineHelpContext context(&file, eHelpOutputFormat_Man, &links_);
459     context.setModuleDisplayName(displayName);
460     module.writeHelp(context);
461
462     file.writeLine(".SH SEE ALSO");
463     file.writeLine(".BR gromacs(7)");
464     file.writeLine();
465     file.writeLine("More information about \\fBGROMACS\\fR is available at <\\fIhttp://www.gromacs.org/\\fR>.");
466 }
467
468 void HelpExportMan::startModuleGroupExport()
469 {
470     const char *const programListPlaceholder = "@PROGMANPAGES@";
471
472     const std::string man7Template = gmx::File::readToString("man7/gromacs.7.in");
473     const size_t      index        = man7Template.find(programListPlaceholder);
474     GMX_RELEASE_ASSERT(index != std::string::npos,
475                        "gromacs.7.in must contain a @PROGMANPAGES@ line");
476     std::string header = man7Template.substr(0, index);
477     man7Footer_ = man7Template.substr(index + std::strlen(programListPlaceholder));
478     header      = replaceAll(header, "@VERSION@", gmx_version());
479     man7File_.reset(new File("man7/gromacs.7", "w"));
480     man7File_->writeLine(header);
481 }
482
483 void HelpExportMan::exportModuleGroup(const char                *title,
484                                       const ModuleGroupContents &modules)
485 {
486     man7File_->writeLine(formatString(".Sh \"%s\"", title));
487     man7File_->writeLine(formatString(".IX Subsection \"%s\"", title));
488     man7File_->writeLine(".Vb");
489     man7File_->writeLine(".ta 16n");
490
491     ModuleGroupContents::const_iterator module;
492     for (module = modules.begin(); module != modules.end(); ++module)
493     {
494         const std::string &tag(module->first);
495         man7File_->writeLine(formatString("\\&  %s\t%s",
496                                           tag.c_str(), module->second));
497     }
498
499     man7File_->writeLine(".Ve");
500 }
501
502 void HelpExportMan::finishModuleGroupExport()
503 {
504     man7File_->writeLine(man7Footer_);
505     man7File_->close();
506 }
507
508 /********************************************************************
509  * HelpExportHtml
510  */
511
512 /*! \internal \brief
513  * Implements export for HTML help.
514  *
515  * \ingroup module_commandline
516  */
517 class HelpExportHtml : public HelpExportInterface
518 {
519     public:
520         //! Initializes HTML exporter.
521         explicit HelpExportHtml(const CommandLineHelpModuleImpl &helpModule);
522
523         virtual void startModuleExport();
524         virtual void exportModuleHelp(
525             const CommandLineModuleInterface &module,
526             const std::string                &tag,
527             const std::string                &displayName);
528         virtual void finishModuleExport();
529
530         virtual void startModuleGroupExport();
531         virtual void exportModuleGroup(const char                *title,
532                                        const ModuleGroupContents &modules);
533         virtual void finishModuleGroupExport();
534
535     private:
536         void setupHeaderAndFooter();
537
538         void writeHtmlHeader(File *file, const std::string &title) const;
539         void writeHtmlFooter(File *file) const;
540
541         HelpLinks                links_;
542         boost::scoped_ptr<File>  indexFile_;
543         std::string              header_;
544         std::string              footer_;
545 };
546
547 HelpExportHtml::HelpExportHtml(const CommandLineHelpModuleImpl &helpModule)
548     : links_(eHelpOutputFormat_Html)
549 {
550     File             linksFile("links.dat", "r");
551     std::string      line;
552     while (linksFile.readLine(&line))
553     {
554         links_.addLink(line, "../online/" + line, line);
555     }
556     linksFile.close();
557     initProgramLinks(&links_, helpModule);
558     setupHeaderAndFooter();
559 }
560
561 void HelpExportHtml::setupHeaderAndFooter()
562 {
563     header_ = gmx::File::readToString("header.html.in");
564     header_ = replaceAll(header_, "@VERSION@", gmx_version());
565     gmx::File::writeFileFromString("header.html", header_);
566     header_ = replaceAll(header_, "@ROOTPATH@", "../");
567     footer_ = gmx::File::readToString("footer.html");
568 }
569
570 void HelpExportHtml::startModuleExport()
571 {
572     indexFile_.reset(new File("final/programs/byname.html", "w"));
573     writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Name");
574     indexFile_->writeLine("<H3>GROMACS Programs Alphabetically</H3>");
575 }
576
577 void HelpExportHtml::exportModuleHelp(
578         const CommandLineModuleInterface &module,
579         const std::string                &tag,
580         const std::string                &displayName)
581 {
582     File file("final/programs/" + tag + ".html", "w");
583     writeHtmlHeader(&file, displayName);
584
585     CommandLineHelpContext context(&file, eHelpOutputFormat_Html, &links_);
586     context.setModuleDisplayName(displayName);
587     module.writeHelp(context);
588
589     writeHtmlFooter(&file);
590     file.close();
591
592     indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
593                                        tag.c_str(), displayName.c_str(),
594                                        module.shortDescription()));
595 }
596
597 void HelpExportHtml::finishModuleExport()
598 {
599     writeHtmlFooter(indexFile_.get());
600     indexFile_->close();
601 }
602
603 void HelpExportHtml::startModuleGroupExport()
604 {
605     indexFile_.reset(new File("final/programs/bytopic.html", "w"));
606     writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Topic");
607     indexFile_->writeLine("<H3>GROMACS Programs by Topic</H3>");
608 }
609
610 void HelpExportHtml::exportModuleGroup(const char                *title,
611                                        const ModuleGroupContents &modules)
612 {
613     indexFile_->writeLine(formatString("<H4>%s</H4>", title));
614
615     ModuleGroupContents::const_iterator module;
616     for (module = modules.begin(); module != modules.end(); ++module)
617     {
618         const std::string     &tag(module->first);
619         std::string            displayName(tag);
620         // TODO: This does not work if the binary name would contain a dash,
621         // but that is not currently the case.
622         size_t                 dashPos = displayName.find('-');
623         GMX_RELEASE_ASSERT(dashPos != std::string::npos,
624                            "There should always be at least one dash in the tag");
625         displayName[dashPos] = ' ';
626         indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
627                                            tag.c_str(), displayName.c_str(),
628                                            module->second));
629     }
630 }
631
632 void HelpExportHtml::finishModuleGroupExport()
633 {
634     writeHtmlFooter(indexFile_.get());
635     indexFile_->close();
636 }
637
638 void HelpExportHtml::writeHtmlHeader(File *file, const std::string &title) const
639 {
640     file->writeLine(replaceAll(header_, "@TITLE@", title));
641 }
642
643 void HelpExportHtml::writeHtmlFooter(File *file) const
644 {
645     file->writeLine(footer_);
646 }
647
648 /********************************************************************
649  * HelpExportCompletion
650  */
651
652 /*! \internal \brief
653  * Implements export for command-line completion.
654  *
655  * \ingroup module_commandline
656  */
657 class HelpExportCompletion : public HelpExportInterface
658 {
659     public:
660         //! Initializes completion exporter.
661         explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
662
663         virtual void startModuleExport();
664         virtual void exportModuleHelp(
665             const CommandLineModuleInterface &module,
666             const std::string                &tag,
667             const std::string                &displayName);
668         virtual void finishModuleExport();
669
670         virtual void startModuleGroupExport() {}
671         virtual void exportModuleGroup(const char                * /*title*/,
672                                        const ModuleGroupContents & /*modules*/) {}
673         virtual void finishModuleGroupExport() {}
674
675     private:
676         ShellCompletionWriter    bashWriter_;
677         std::vector<std::string> modules_;
678 };
679
680 HelpExportCompletion::HelpExportCompletion(
681         const CommandLineHelpModuleImpl &helpModule)
682     : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
683 {
684 }
685
686 void HelpExportCompletion::startModuleExport()
687 {
688     bashWriter_.startCompletions();
689 }
690
691 void HelpExportCompletion::exportModuleHelp(
692         const CommandLineModuleInterface &module,
693         const std::string                 & /*tag*/,
694         const std::string                 & /*displayName*/)
695 {
696     modules_.push_back(module.name());
697     {
698         CommandLineHelpContext context(&bashWriter_);
699         // We use the display name to pass the name of the module to the
700         // completion writer.
701         context.setModuleDisplayName(module.name());
702         module.writeHelp(context);
703     }
704 }
705
706 void HelpExportCompletion::finishModuleExport()
707 {
708     CommandLineCommonOptionsHolder optionsHolder;
709     optionsHolder.initOptions();
710     bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
711     bashWriter_.finishCompletions();
712 }
713
714 }   // namespace
715
716 /********************************************************************
717  * CommandLineHelpModuleImpl implementation
718  */
719 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
720         const ProgramContextInterface    &programContext,
721         const std::string                &binaryName,
722         const CommandLineModuleMap       &modules,
723         const CommandLineModuleGroupList &groups)
724     : rootTopic_(new RootHelpTopic(*this)), programContext_(programContext),
725       binaryName_(binaryName), modules_(modules), groups_(groups),
726       context_(NULL), moduleOverride_(NULL), bHidden_(false),
727       outputOverride_(NULL)
728 {
729 }
730
731 void CommandLineHelpModuleImpl::exportHelp(HelpExportInterface *exporter) const
732 {
733     // TODO: Would be nicer to have the file names supplied by the build system
734     // and/or export a list of files from here.
735     const char *const program = binaryName_.c_str();
736
737     exporter->startModuleExport();
738     CommandLineModuleMap::const_iterator module;
739     for (module = modules_.begin(); module != modules_.end(); ++module)
740     {
741         if (module->second->shortDescription() != NULL)
742         {
743             const char *const moduleName = module->first.c_str();
744             std::string       tag(formatString("%s-%s", program, moduleName));
745             std::string       displayName(formatString("%s %s", program, moduleName));
746             exporter->exportModuleHelp(*module->second, tag, displayName);
747         }
748     }
749     exporter->finishModuleExport();
750
751     exporter->startModuleGroupExport();
752     CommandLineModuleGroupList::const_iterator group;
753     for (group = groups_.begin(); group != groups_.end(); ++group)
754     {
755         exporter->exportModuleGroup((*group)->title(), (*group)->modules());
756     }
757     exporter->finishModuleGroupExport();
758 }
759
760 /********************************************************************
761  * CommandLineHelpModule
762  */
763
764 CommandLineHelpModule::CommandLineHelpModule(
765         const ProgramContextInterface    &programContext,
766         const std::string                &binaryName,
767         const CommandLineModuleMap       &modules,
768         const CommandLineModuleGroupList &groups)
769     : impl_(new Impl(programContext, binaryName, modules, groups))
770 {
771 }
772
773 CommandLineHelpModule::~CommandLineHelpModule()
774 {
775 }
776
777 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
778         const CommandLineModuleInterface &module) const
779 {
780     return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
781 }
782
783 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
784 {
785     impl_->rootTopic_->addSubTopic(move(topic));
786 }
787
788 void CommandLineHelpModule::setShowHidden(bool bHidden)
789 {
790     impl_->bHidden_ = bHidden;
791 }
792
793 void CommandLineHelpModule::setModuleOverride(
794         const CommandLineModuleInterface &module)
795 {
796     impl_->moduleOverride_ = &module;
797 }
798
799 void CommandLineHelpModule::setOutputRedirect(File *output)
800 {
801     impl_->outputOverride_ = output;
802 }
803
804 int CommandLineHelpModule::run(int argc, char *argv[])
805 {
806     // Add internal topics lazily here.
807     addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)));
808
809     const char *const exportFormats[] = { "man", "html", "completion" };
810     std::string       exportFormat;
811     Options           options(NULL, NULL);
812     options.addOption(StringOption("export").store(&exportFormat)
813                           .enumValue(exportFormats));
814     CommandLineParser(&options).parse(&argc, argv);
815     if (!exportFormat.empty())
816     {
817         boost::scoped_ptr<HelpExportInterface> exporter;
818         if (exportFormat == "man")
819         {
820             exporter.reset(new HelpExportMan(*impl_));
821         }
822         else if (exportFormat == "html")
823         {
824             exporter.reset(new HelpExportHtml(*impl_));
825         }
826         else if (exportFormat == "completion")
827         {
828             exporter.reset(new HelpExportCompletion(*impl_));
829         }
830         else
831         {
832             GMX_THROW(NotImplementedError("This help format is not implemented"));
833         }
834         impl_->exportHelp(exporter.get());
835         return 0;
836     }
837
838     File *outputFile = &File::standardOutput();
839     if (impl_->outputOverride_ != NULL)
840     {
841         outputFile = impl_->outputOverride_;
842     }
843     HelpLinks                                 links(eHelpOutputFormat_Console);
844     initProgramLinks(&links, *impl_);
845     boost::scoped_ptr<CommandLineHelpContext> context(
846             new CommandLineHelpContext(outputFile,
847                                        eHelpOutputFormat_Console, &links));
848     context->setShowHidden(impl_->bHidden_);
849     if (impl_->moduleOverride_ != NULL)
850     {
851         context->setModuleDisplayName(impl_->programContext_.displayName());
852         impl_->moduleOverride_->writeHelp(*context);
853         return 0;
854     }
855     impl_->context_ = context.get();
856
857     HelpManager helpManager(*impl_->rootTopic_, context->writerContext());
858     try
859     {
860         for (int i = 1; i < argc; ++i)
861         {
862             helpManager.enterTopic(argv[i]);
863         }
864     }
865     catch (const InvalidInputError &ex)
866     {
867         fprintf(stderr, "%s\n", ex.what());
868         return 2;
869     }
870     helpManager.writeCurrentTopic();
871     return 0;
872 }
873
874 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
875 {
876     const HelpWriterContext &writerContext = context.writerContext();
877     // TODO: Implement.
878     if (writerContext.outputFormat() != eHelpOutputFormat_Console)
879     {
880         return;
881     }
882     writerContext.writeTextBlock(
883             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
884     // TODO: More information.
885 }
886
887 } // namespace gmx