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