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