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