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