2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016,2017,2018, 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.
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.
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.
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.
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.
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.
37 * Implements gmx::CommandLineHelpModule.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_commandline
44 #include "cmdlinehelpmodule.h"
50 #include "gromacs/commandline/cmdlinehelpcontext.h"
51 #include "gromacs/commandline/cmdlinehelpwriter.h"
52 #include "gromacs/commandline/cmdlineparser.h"
53 #include "gromacs/onlinehelp/helpformat.h"
54 #include "gromacs/onlinehelp/helpmanager.h"
55 #include "gromacs/onlinehelp/helptopic.h"
56 #include "gromacs/onlinehelp/helpwritercontext.h"
57 #include "gromacs/options/basicoptions.h"
58 #include "gromacs/options/options.h"
59 #include "gromacs/utility/arrayref.h"
60 #include "gromacs/utility/baseversion.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/fileredirector.h"
63 #include "gromacs/utility/gmxassert.h"
64 #include "gromacs/utility/path.h"
65 #include "gromacs/utility/programcontext.h"
66 #include "gromacs/utility/stringstream.h"
67 #include "gromacs/utility/stringutil.h"
68 #include "gromacs/utility/textreader.h"
69 #include "gromacs/utility/textstream.h"
70 #include "gromacs/utility/textwriter.h"
72 #include "shellcompletions.h"
81 /********************************************************************
82 * RootHelpTopic declaration
86 * Help topic that forms the root of the help tree for the help subcommand.
88 * \ingroup module_commandline
90 class RootHelpTopic : public AbstractCompositeHelpTopic
94 * Creates a root help topic.
98 explicit RootHelpTopic(const CommandLineHelpModuleImpl &helpModule)
99 : helpModule_(helpModule)
103 virtual const char *name() const;
104 virtual const char *title() const { return title_.c_str(); }
106 //! Adds a top-level topic and optionally marks it as exported.
107 void addTopic(HelpTopicPointer topic, bool bExported)
111 exportedTopics_.emplace_back(topic->name());
113 addSubTopic(std::move(topic));
115 //! Exports all the top-level topics with the given exporter.
116 void exportHelp(IHelpExport *exporter);
118 virtual void writeHelp(const HelpWriterContext &context) const;
121 // unused because of the writeHelp() override
122 virtual std::string helpText() const { return ""; }
124 CommandLineHelpContext createContext(const HelpWriterContext &context) const;
126 const CommandLineHelpModuleImpl &helpModule_;
128 std::vector<std::string> exportedTopics_;
130 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
135 /********************************************************************
136 * CommandLineHelpModuleImpl declaration
139 class CommandLineHelpModuleImpl
142 CommandLineHelpModuleImpl(const IProgramContext &programContext,
143 const std::string &binaryName,
144 const CommandLineModuleMap &modules,
145 const CommandLineModuleGroupList &groups);
147 std::unique_ptr<IHelpExport> createExporter(
148 const std::string &format,
149 IFileOutputRedirector *redirector);
150 void exportHelp(IHelpExport *exporter);
152 RootHelpTopic rootTopic_;
153 const IProgramContext &programContext_;
154 std::string binaryName_;
155 const CommandLineModuleMap &modules_;
156 const CommandLineModuleGroupList &groups_;
158 CommandLineHelpContext *context_;
159 const ICommandLineModule *moduleOverride_;
162 IFileOutputRedirector *outputRedirector_;
164 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
170 /********************************************************************
175 * Callbacks for exporting help information for command-line modules.
177 * \ingroup module_commandline
182 //! Shorthand for a list of modules contained in a group.
183 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
185 virtual ~IHelpExport() {};
188 * Called once before exporting individual modules.
190 * Can, e.g., open shared output files (e.g., if the output is written
191 * into a single file, or if a separate index is required) and write
194 virtual void startModuleExport() = 0;
196 * Called to export the help for each module.
198 * \param[in] module Module for which the help should be exported.
199 * \param[in] tag Unique tag for the module (gmx-something).
200 * \param[in] displayName Display name for the module (gmx something).
202 virtual void exportModuleHelp(
203 const ICommandLineModule &module,
204 const std::string &tag,
205 const std::string &displayName) = 0;
207 * Called after all modules have been exported.
209 * Can close files opened in startModuleExport(), write footers to them
212 virtual void finishModuleExport() = 0;
215 * Called once before exporting module groups.
217 * Can, e.g., open a single output file for listing all the groups.
219 virtual void startModuleGroupExport() = 0;
221 * Called to export the help for each module group.
223 * \param[in] title Title for the group.
224 * \param[in] modules List of modules in the group.
226 virtual void exportModuleGroup(const char *title,
227 const ModuleGroupContents &modules) = 0;
229 * Called after all module groups have been exported.
231 * Can close files opened in startModuleGroupExport(), write footers to them
234 virtual void finishModuleGroupExport() = 0;
237 * Called to export the help for a top-level topic.
239 * \param[in] topic Topic to export.
241 virtual void exportTopic(const IHelpTopic &topic) = 0;
244 /********************************************************************
245 * RootHelpTopic implementation
250 static const char title[];
251 static const char *const text[];
254 // These are used for the gmx.1 man page.
255 // TODO: Do not hardcode them here, but pass them from the outside to make this
256 // code more generic.
257 const char RootHelpText::title[] = "molecular dynamics simulation suite";
258 const char *const RootHelpText::text[] = {
259 "|Gromacs| is a full-featured suite of programs to perform molecular",
260 "dynamics simulations, i.e., to simulate the behavior of systems with",
261 "hundreds to millions of particles using Newtonian equations of motion.",
262 "It is primarily used for research on proteins, lipids, and polymers, but",
263 "can be applied to a wide variety of chemical and biological research",
267 const char *RootHelpTopic::name() const
269 return helpModule_.binaryName_.c_str();
272 void RootHelpTopic::exportHelp(IHelpExport *exporter)
274 std::vector<std::string>::const_iterator topicName;
275 for (topicName = exportedTopics_.begin();
276 topicName != exportedTopics_.end();
279 const IHelpTopic *topic = findSubTopic(topicName->c_str());
280 GMX_RELEASE_ASSERT(topic != nullptr, "Exported help topic no longer found");
281 exporter->exportTopic(*topic);
283 // For now, the title is only set for the export to make it not appear in
284 // console output, which makes things consistent for 'gmx help' and
285 // 'gmx help <command>'.
286 title_ = RootHelpText::title;
287 exporter->exportTopic(*this);
290 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
293 CommandLineCommonOptionsHolder optionsHolder;
294 CommandLineHelpContext cmdlineContext(createContext(context));
295 cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
296 optionsHolder.initOptions();
297 Options &options = *optionsHolder.options();
298 ArrayRef<const char *const> helpText;
299 if (context.outputFormat() != eHelpOutputFormat_Console)
301 helpText = RootHelpText::text;
303 // TODO: Add <command> [<args>] into the synopsis.
304 CommandLineHelpWriter(options)
305 .setHelpText(helpText)
306 .writeHelp(cmdlineContext);
308 if (context.outputFormat() == eHelpOutputFormat_Console)
310 // TODO: Consider printing a list of "core" commands. Would require someone
311 // to determine such a set...
312 context.paragraphBreak();
313 writeSubTopicList(context,
314 "Additional help is available on the following topics:");
315 context.writeTextBlock("To access the help, use '[PROGRAM] help <topic>'.");
316 context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
320 // TODO: This should not really end up on the HTML page.
321 context.writeTitle(formatString("%s commands", helpModule_.binaryName_.c_str()));
322 context.writeTextBlock(
323 "The following commands are available. Please refer to their "
324 "individual man pages or [TT][PROGRAM] help <command>[tt] "
325 "for further details.");
326 context.writeTextBlock("");
327 context.writeTextBlock(".. include:: /fragments/bytopic-man.rst");
331 CommandLineHelpContext
332 RootHelpTopic::createContext(const HelpWriterContext &context) const
334 if (helpModule_.context_ != nullptr)
336 return CommandLineHelpContext(*helpModule_.context_);
340 return CommandLineHelpContext(context);
344 /********************************************************************
349 * Help topic for listing the commands.
351 * \ingroup module_commandline
353 class CommandsHelpTopic : public IHelpTopic
357 * Creates a command list help topic.
359 * \param[in] helpModule Help module to get module information from.
363 explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
364 : helpModule_(helpModule)
368 virtual const char *name() const { return "commands"; }
369 virtual const char *title() const { return "List of available commands"; }
370 virtual bool hasSubTopics() const { return false; }
371 virtual const IHelpTopic *findSubTopic(const char * /*name*/) const
376 virtual void writeHelp(const HelpWriterContext &context) const;
379 const CommandLineHelpModuleImpl &helpModule_;
381 GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
384 void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
386 if (context.outputFormat() != eHelpOutputFormat_Console)
388 GMX_THROW(NotImplementedError(
389 "Module list is not implemented for this output format"));
391 int maxNameLength = 0;
392 const CommandLineModuleMap &modules = helpModule_.modules_;
393 CommandLineModuleMap::const_iterator module;
394 for (module = modules.begin(); module != modules.end(); ++module)
396 int nameLength = static_cast<int>(module->first.length());
397 if (module->second->shortDescription() != nullptr
398 && nameLength > maxNameLength)
400 maxNameLength = nameLength;
403 context.writeTextBlock(
404 "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
405 "Available commands:");
406 TextWriter &file = context.outputFile();
407 TextTableFormatter formatter;
408 formatter.addColumn(nullptr, maxNameLength + 1, false);
409 formatter.addColumn(nullptr, 72 - maxNameLength, true);
410 formatter.setFirstColumnIndent(4);
411 for (module = modules.begin(); module != modules.end(); ++module)
413 const char *name = module->first.c_str();
414 const char *description = module->second->shortDescription();
415 if (description != nullptr)
418 formatter.addColumnLine(0, name);
419 formatter.addColumnLine(1, description);
420 file.writeString(formatter.formatRow());
423 context.writeTextBlock(
424 "For help on a command, use '[PROGRAM] help <command>'.");
427 /********************************************************************
432 * Help topic wrapper for a command-line module.
434 * This class implements IHelpTopic such that it wraps a
435 * ICommandLineModule, allowing subcommand "help <command>"
436 * to produce the help for "<command>".
438 * \ingroup module_commandline
440 class ModuleHelpTopic : public IHelpTopic
443 //! Constructs a help topic for a specific module.
444 ModuleHelpTopic(const ICommandLineModule &module,
445 const CommandLineHelpModuleImpl &helpModule)
446 : module_(module), helpModule_(helpModule)
450 virtual const char *name() const { return module_.name(); }
451 virtual const char *title() const { return nullptr; }
452 virtual bool hasSubTopics() const { return false; }
453 virtual const IHelpTopic *findSubTopic(const char * /*name*/) const
457 virtual void writeHelp(const HelpWriterContext &context) const;
460 const ICommandLineModule &module_;
461 const CommandLineHelpModuleImpl &helpModule_;
463 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
466 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
468 CommandLineHelpContext context(*helpModule_.context_);
469 const char *const program = helpModule_.binaryName_.c_str();
470 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
471 module_.writeHelp(context);
474 /********************************************************************
475 * HelpExportReStructuredText
479 * Adds hyperlinks to modules within this binary.
481 * \param[in,out] links Links are added here.
482 * \param[in] helpModule Help module to get module information from.
483 * \throws std::bad_alloc if out of memory.
485 * Initializes a HelpLinks object with links to modules defined in
488 * \ingroup module_commandline
490 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
492 const char *const program = helpModule.binaryName_.c_str();
493 CommandLineModuleMap::const_iterator module;
494 for (module = helpModule.modules_.begin();
495 module != helpModule.modules_.end();
498 if (module->second->shortDescription() != nullptr)
500 std::string linkName("[gmx-" + module->first + "]");
501 const char *name = module->first.c_str();
502 std::string reference(
503 formatString(":doc:`%s %s <%s-%s>`", program, name, program, name));
504 std::string displayName(
505 formatString("[TT]%s %s[tt]", program, name));
506 links->addLink(linkName, reference, displayName);
512 * Implements export for web pages as reStructuredText.
514 * \ingroup module_commandline
516 class HelpExportReStructuredText : public IHelpExport
519 //! Initializes reST exporter.
520 HelpExportReStructuredText(
521 const CommandLineHelpModuleImpl &helpModule,
522 IFileOutputRedirector *outputRedirector);
524 virtual void startModuleExport();
525 virtual void exportModuleHelp(
526 const ICommandLineModule &module,
527 const std::string &tag,
528 const std::string &displayName);
529 virtual void finishModuleExport();
531 virtual void startModuleGroupExport();
532 virtual void exportModuleGroup(const char *title,
533 const ModuleGroupContents &modules);
534 virtual void finishModuleGroupExport();
536 virtual void exportTopic(const IHelpTopic &topic);
539 IFileOutputRedirector *outputRedirector_;
540 const std::string &binaryName_;
542 // These never release ownership.
543 std::unique_ptr<TextWriter> indexFile_;
544 std::unique_ptr<TextWriter> manPagesFile_;
547 HelpExportReStructuredText::HelpExportReStructuredText(
548 const CommandLineHelpModuleImpl &helpModule,
549 IFileOutputRedirector *outputRedirector)
550 : outputRedirector_(outputRedirector),
551 binaryName_(helpModule.binaryName_),
552 links_(eHelpOutputFormat_Rst)
554 TextReader linksFile("links.dat");
556 linksFile.setTrimTrailingWhiteSpace(true);
557 while (linksFile.readLine(&line))
559 links_.addLink("[REF]." + line + "[ref]",
560 formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
562 links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
565 initProgramLinks(&links_, helpModule);
568 void HelpExportReStructuredText::startModuleExport()
572 outputRedirector_->openTextOutputFile("fragments/byname.rst")));
573 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
574 binaryName_.c_str(), binaryName_.c_str(),
575 RootHelpText::title));
578 outputRedirector_->openTextOutputFile("conf-man.py")));
579 manPagesFile_->writeLine("man_pages = [");
582 void HelpExportReStructuredText::exportModuleHelp(
583 const ICommandLineModule &module,
584 const std::string &tag,
585 const std::string &displayName)
587 TextOutputStreamPointer file
588 = outputRedirector_->openTextOutputFile("onlinehelp/" + tag + ".rst");
589 TextWriter writer(file);
590 writer.writeLine(formatString(".. _%s:", displayName.c_str()));
591 if (displayName == binaryName_ + " mdrun")
593 // Make an extra link target for the convenience of
594 // MPI-specific documentation
595 writer.writeLine(".. _mdrun_mpi:");
597 writer.ensureEmptyLine();
599 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
600 context.enterSubSection(displayName);
601 context.setModuleDisplayName(displayName);
602 module.writeHelp(context);
604 writer.ensureEmptyLine();
605 writer.writeLine(".. only:: man");
607 writer.writeLine(" See also");
608 writer.writeLine(" --------");
610 writer.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
612 writer.writeLine(" More information about |Gromacs| is available at <http://www.gromacs.org/>.");
615 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
616 displayName.c_str(), tag.c_str(),
617 module.shortDescription()));
618 manPagesFile_->writeLine(
619 formatString(" ('onlinehelp/%s', '%s', \"%s\", '', 1),",
620 tag.c_str(), tag.c_str(), module.shortDescription()));
623 void HelpExportReStructuredText::finishModuleExport()
628 manPagesFile_->writeLine(
629 formatString(" ('onlinehelp/%s', '%s', '%s', '', 1)",
630 binaryName_.c_str(), binaryName_.c_str(),
631 RootHelpText::title));
632 manPagesFile_->writeLine("]");
633 manPagesFile_->close();
634 manPagesFile_.reset();
637 void HelpExportReStructuredText::startModuleGroupExport()
641 outputRedirector_->openTextOutputFile("fragments/bytopic.rst")));
644 outputRedirector_->openTextOutputFile("fragments/bytopic-man.rst")));
647 void HelpExportReStructuredText::exportModuleGroup(
649 const ModuleGroupContents &modules)
651 indexFile_->ensureEmptyLine();
652 indexFile_->writeLine(title);
653 indexFile_->writeLine(std::string(std::strlen(title), '^'));
654 manPagesFile_->ensureEmptyLine();
655 manPagesFile_->writeLine(title);
656 manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
658 ModuleGroupContents::const_iterator module;
659 for (module = modules.begin(); module != modules.end(); ++module)
661 const std::string &tag(module->first);
662 std::string displayName(tag);
663 // TODO: This does not work if the binary name would contain a dash,
664 // but that is not currently the case.
665 const size_t dashPos = displayName.find('-');
666 GMX_RELEASE_ASSERT(dashPos != std::string::npos,
667 "There should always be at least one dash in the tag");
668 displayName[dashPos] = ' ';
669 indexFile_->writeLine(formatString(":doc:`%s </onlinehelp/%s>`\n %s",
670 displayName.c_str(), tag.c_str(),
672 manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n %s",
678 void HelpExportReStructuredText::finishModuleGroupExport()
682 manPagesFile_->close();
683 manPagesFile_.reset();
686 void HelpExportReStructuredText::exportTopic(const IHelpTopic &topic)
688 const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
689 TextWriter writer(outputRedirector_->openTextOutputFile(path));
690 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_,
692 HelpManager manager(topic, context.writerContext());
693 manager.writeCurrentTopic();
697 /********************************************************************
698 * HelpExportCompletion
702 * Implements export for command-line completion.
704 * \ingroup module_commandline
706 class HelpExportCompletion : public IHelpExport
709 //! Initializes completion exporter.
710 explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
712 virtual void startModuleExport();
713 virtual void exportModuleHelp(
714 const ICommandLineModule &module,
715 const std::string &tag,
716 const std::string &displayName);
717 virtual void finishModuleExport();
719 virtual void startModuleGroupExport() {}
720 virtual void exportModuleGroup(const char * /*title*/,
721 const ModuleGroupContents & /*modules*/) {}
722 virtual void finishModuleGroupExport() {}
724 virtual void exportTopic(const IHelpTopic & /*topic*/) {}
727 ShellCompletionWriter bashWriter_;
728 std::vector<std::string> modules_;
731 HelpExportCompletion::HelpExportCompletion(
732 const CommandLineHelpModuleImpl &helpModule)
733 : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
737 void HelpExportCompletion::startModuleExport()
739 bashWriter_.startCompletions();
742 void HelpExportCompletion::exportModuleHelp(
743 const ICommandLineModule &module,
744 const std::string & /*tag*/,
745 const std::string & /*displayName*/)
747 modules_.emplace_back(module.name());
749 CommandLineHelpContext context(&bashWriter_);
750 // We use the display name to pass the name of the module to the
751 // completion writer.
752 context.setModuleDisplayName(module.name());
753 module.writeHelp(context);
757 void HelpExportCompletion::finishModuleExport()
759 CommandLineCommonOptionsHolder optionsHolder;
760 optionsHolder.initOptions();
761 bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
762 bashWriter_.finishCompletions();
767 /********************************************************************
768 * CommandLineHelpModuleImpl implementation
771 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
772 const IProgramContext &programContext,
773 const std::string &binaryName,
774 const CommandLineModuleMap &modules,
775 const CommandLineModuleGroupList &groups)
776 : rootTopic_(*this), programContext_(programContext),
777 binaryName_(binaryName), modules_(modules), groups_(groups),
778 context_(nullptr), moduleOverride_(nullptr), bHidden_(false),
779 outputRedirector_(&defaultFileOutputRedirector())
783 std::unique_ptr<IHelpExport>
784 CommandLineHelpModuleImpl::createExporter(const std::string &format,
785 IFileOutputRedirector *redirector)
789 return std::unique_ptr<IHelpExport>(
790 new HelpExportReStructuredText(*this, redirector));
792 else if (format == "completion")
794 return std::unique_ptr<IHelpExport>(
795 new HelpExportCompletion(*this));
797 GMX_THROW(NotImplementedError("This help format is not implemented"));
800 void CommandLineHelpModuleImpl::exportHelp(IHelpExport *exporter)
802 // TODO: Would be nicer to have the file names supplied by the build system
803 // and/or export a list of files from here.
804 const char *const program = binaryName_.c_str();
806 exporter->startModuleExport();
807 CommandLineModuleMap::const_iterator module;
808 for (module = modules_.begin(); module != modules_.end(); ++module)
810 if (module->second->shortDescription() != nullptr)
812 const char *const moduleName = module->first.c_str();
813 std::string tag(formatString("%s-%s", program, moduleName));
814 std::string displayName(formatString("%s %s", program, moduleName));
815 exporter->exportModuleHelp(*module->second, tag, displayName);
818 exporter->finishModuleExport();
820 exporter->startModuleGroupExport();
821 CommandLineModuleGroupList::const_iterator group;
822 for (group = groups_.begin(); group != groups_.end(); ++group)
824 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
826 exporter->finishModuleGroupExport();
828 rootTopic_.exportHelp(exporter);
834 /********************************************************************
835 * ModificationCheckingFileOutputStream
838 class ModificationCheckingFileOutputStream : public TextOutputStream
841 ModificationCheckingFileOutputStream(
843 IFileOutputRedirector *redirector)
844 : path_(path), redirector_(redirector)
848 virtual void write(const char *str) { contents_.write(str); }
851 const std::string &newContents = contents_.toString();
852 // TODO: Redirect these for unit tests.
853 if (File::exists(path_, File::returnFalseOnError))
855 const std::string originalContents_
856 = TextReader::readFileToString(path_);
857 if (originalContents_ == newContents)
862 TextWriter writer(redirector_->openTextOutputFile(path_));
863 writer.writeString(newContents);
868 StringOutputStream contents_;
869 IFileOutputRedirector *redirector_;
872 /********************************************************************
873 * ModificationCheckingFileOutputRedirector
876 class ModificationCheckingFileOutputRedirector : public IFileOutputRedirector
879 explicit ModificationCheckingFileOutputRedirector(
880 IFileOutputRedirector *redirector)
881 : redirector_(redirector)
885 virtual TextOutputStream &standardOutput()
887 return redirector_->standardOutput();
889 virtual TextOutputStreamPointer openTextOutputFile(const char *filename)
891 return TextOutputStreamPointer(
892 new ModificationCheckingFileOutputStream(filename, redirector_));
896 IFileOutputRedirector *redirector_;
901 /********************************************************************
902 * CommandLineHelpModule
905 CommandLineHelpModule::CommandLineHelpModule(
906 const IProgramContext &programContext,
907 const std::string &binaryName,
908 const CommandLineModuleMap &modules,
909 const CommandLineModuleGroupList &groups)
910 : impl_(new Impl(programContext, binaryName, modules, groups))
914 CommandLineHelpModule::~CommandLineHelpModule()
918 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
919 const ICommandLineModule &module) const
921 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
924 void CommandLineHelpModule::addTopic(HelpTopicPointer topic, bool bExported)
926 impl_->rootTopic_.addTopic(std::move(topic), bExported);
929 void CommandLineHelpModule::setShowHidden(bool bHidden)
931 impl_->bHidden_ = bHidden;
934 void CommandLineHelpModule::setModuleOverride(
935 const ICommandLineModule &module)
937 impl_->moduleOverride_ = &module;
940 void CommandLineHelpModule::setOutputRedirector(
941 IFileOutputRedirector *output)
943 impl_->outputRedirector_ = output;
946 int CommandLineHelpModule::run(int argc, char *argv[])
948 // Add internal topics lazily here.
949 addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);
951 const char *const exportFormats[] = { "rst", "completion" };
952 std::string exportFormat;
954 options.addOption(StringOption("export").store(&exportFormat)
955 .enumValue(exportFormats));
956 CommandLineParser(&options).allowPositionalArguments(true).parse(&argc, argv);
957 if (!exportFormat.empty())
959 ModificationCheckingFileOutputRedirector redirector(impl_->outputRedirector_);
960 const std::unique_ptr<IHelpExport> exporter(
961 impl_->createExporter(exportFormat, &redirector));
962 impl_->exportHelp(exporter.get());
966 TextOutputStream &outputFile = impl_->outputRedirector_->standardOutput();
967 TextWriter writer(&outputFile);
968 HelpLinks links(eHelpOutputFormat_Console);
969 initProgramLinks(&links, *impl_);
970 CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, &links,
972 context.setShowHidden(impl_->bHidden_);
973 if (impl_->moduleOverride_ != nullptr)
975 context.setModuleDisplayName(impl_->programContext_.displayName());
976 impl_->moduleOverride_->writeHelp(context);
979 impl_->context_ = &context;
981 HelpManager helpManager(impl_->rootTopic_, context.writerContext());
984 for (int i = 1; i < argc; ++i)
986 helpManager.enterTopic(argv[i]);
989 catch (const InvalidInputError &ex)
991 fprintf(stderr, "%s\n", ex.what());
994 helpManager.writeCurrentTopic();
998 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
1000 const HelpWriterContext &writerContext = context.writerContext();
1002 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
1006 writerContext.writeTextBlock(
1007 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
1008 // TODO: More information.