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