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