2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016, 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 != NULL, "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 ConstArrayRef<const char *> 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_ != NULL)
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() != NULL
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(NULL, maxNameLength + 1, false);
409 formatter.addColumn(NULL, 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 != NULL)
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 NULL; }
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() != NULL)
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 while (linksFile.readLineTrimmed(&line))
558 links_.addLink("[REF]." + line + "[ref]",
559 formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
561 links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
564 initProgramLinks(&links_, helpModule);
567 void HelpExportReStructuredText::startModuleExport()
571 outputRedirector_->openTextOutputFile("fragments/byname.rst")));
572 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
573 binaryName_.c_str(), binaryName_.c_str(),
574 RootHelpText::title));
577 outputRedirector_->openTextOutputFile("conf-man.py")));
578 manPagesFile_->writeLine("man_pages = [");
581 void HelpExportReStructuredText::exportModuleHelp(
582 const ICommandLineModule &module,
583 const std::string &tag,
584 const std::string &displayName)
586 TextOutputStreamPointer file
587 = outputRedirector_->openTextOutputFile("onlinehelp/" + tag + ".rst");
588 TextWriter writer(file);
589 writer.writeLine(formatString(".. _%s:", displayName.c_str()));
590 if (0 == displayName.compare(binaryName_ + " mdrun"))
592 // Make an extra link target for the convenience of
593 // MPI-specific documentation
594 writer.writeLine(".. _mdrun_mpi:");
596 writer.ensureEmptyLine();
598 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
599 context.enterSubSection(displayName);
600 context.setModuleDisplayName(displayName);
601 module.writeHelp(context);
603 writer.ensureEmptyLine();
604 writer.writeLine(".. only:: man");
606 writer.writeLine(" See also");
607 writer.writeLine(" --------");
609 writer.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
611 writer.writeLine(" More information about |Gromacs| is available at <http://www.gromacs.org/>.");
614 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
615 displayName.c_str(), tag.c_str(),
616 module.shortDescription()));
617 manPagesFile_->writeLine(
618 formatString(" ('onlinehelp/%s', '%s', \"%s\", '', 1),",
619 tag.c_str(), tag.c_str(), module.shortDescription()));
622 void HelpExportReStructuredText::finishModuleExport()
627 manPagesFile_->writeLine(
628 formatString(" ('onlinehelp/%s', '%s', '%s', '', 1)",
629 binaryName_.c_str(), binaryName_.c_str(),
630 RootHelpText::title));
631 manPagesFile_->writeLine("]");
632 manPagesFile_->close();
633 manPagesFile_.reset();
636 void HelpExportReStructuredText::startModuleGroupExport()
640 outputRedirector_->openTextOutputFile("fragments/bytopic.rst")));
643 outputRedirector_->openTextOutputFile("fragments/bytopic-man.rst")));
646 void HelpExportReStructuredText::exportModuleGroup(
648 const ModuleGroupContents &modules)
650 indexFile_->ensureEmptyLine();
651 indexFile_->writeLine(title);
652 indexFile_->writeLine(std::string(std::strlen(title), '^'));
653 manPagesFile_->ensureEmptyLine();
654 manPagesFile_->writeLine(title);
655 manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
657 ModuleGroupContents::const_iterator module;
658 for (module = modules.begin(); module != modules.end(); ++module)
660 const std::string &tag(module->first);
661 std::string displayName(tag);
662 // TODO: This does not work if the binary name would contain a dash,
663 // but that is not currently the case.
664 const size_t dashPos = displayName.find('-');
665 GMX_RELEASE_ASSERT(dashPos != std::string::npos,
666 "There should always be at least one dash in the tag");
667 displayName[dashPos] = ' ';
668 indexFile_->writeLine(formatString(":doc:`%s </onlinehelp/%s>`\n %s",
669 displayName.c_str(), tag.c_str(),
671 manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n %s",
677 void HelpExportReStructuredText::finishModuleGroupExport()
681 manPagesFile_->close();
682 manPagesFile_.reset();
685 void HelpExportReStructuredText::exportTopic(const IHelpTopic &topic)
687 const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
688 TextWriter writer(outputRedirector_->openTextOutputFile(path));
689 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_,
691 HelpManager manager(topic, context.writerContext());
692 manager.writeCurrentTopic();
696 /********************************************************************
697 * HelpExportCompletion
701 * Implements export for command-line completion.
703 * \ingroup module_commandline
705 class HelpExportCompletion : public IHelpExport
708 //! Initializes completion exporter.
709 explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
711 virtual void startModuleExport();
712 virtual void exportModuleHelp(
713 const ICommandLineModule &module,
714 const std::string &tag,
715 const std::string &displayName);
716 virtual void finishModuleExport();
718 virtual void startModuleGroupExport() {}
719 virtual void exportModuleGroup(const char * /*title*/,
720 const ModuleGroupContents & /*modules*/) {}
721 virtual void finishModuleGroupExport() {}
723 virtual void exportTopic(const IHelpTopic & /*topic*/) {}
726 ShellCompletionWriter bashWriter_;
727 std::vector<std::string> modules_;
730 HelpExportCompletion::HelpExportCompletion(
731 const CommandLineHelpModuleImpl &helpModule)
732 : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
736 void HelpExportCompletion::startModuleExport()
738 bashWriter_.startCompletions();
741 void HelpExportCompletion::exportModuleHelp(
742 const ICommandLineModule &module,
743 const std::string & /*tag*/,
744 const std::string & /*displayName*/)
746 modules_.emplace_back(module.name());
748 CommandLineHelpContext context(&bashWriter_);
749 // We use the display name to pass the name of the module to the
750 // completion writer.
751 context.setModuleDisplayName(module.name());
752 module.writeHelp(context);
756 void HelpExportCompletion::finishModuleExport()
758 CommandLineCommonOptionsHolder optionsHolder;
759 optionsHolder.initOptions();
760 bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
761 bashWriter_.finishCompletions();
766 /********************************************************************
767 * CommandLineHelpModuleImpl implementation
770 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
771 const IProgramContext &programContext,
772 const std::string &binaryName,
773 const CommandLineModuleMap &modules,
774 const CommandLineModuleGroupList &groups)
775 : rootTopic_(*this), programContext_(programContext),
776 binaryName_(binaryName), modules_(modules), groups_(groups),
777 context_(NULL), moduleOverride_(NULL), bHidden_(false),
778 outputRedirector_(&defaultFileOutputRedirector())
782 std::unique_ptr<IHelpExport>
783 CommandLineHelpModuleImpl::createExporter(const std::string &format,
784 IFileOutputRedirector *redirector)
788 return std::unique_ptr<IHelpExport>(
789 new HelpExportReStructuredText(*this, redirector));
791 else if (format == "completion")
793 return std::unique_ptr<IHelpExport>(
794 new HelpExportCompletion(*this));
796 GMX_THROW(NotImplementedError("This help format is not implemented"));
799 void CommandLineHelpModuleImpl::exportHelp(IHelpExport *exporter)
801 // TODO: Would be nicer to have the file names supplied by the build system
802 // and/or export a list of files from here.
803 const char *const program = binaryName_.c_str();
805 exporter->startModuleExport();
806 CommandLineModuleMap::const_iterator module;
807 for (module = modules_.begin(); module != modules_.end(); ++module)
809 if (module->second->shortDescription() != NULL)
811 const char *const moduleName = module->first.c_str();
812 std::string tag(formatString("%s-%s", program, moduleName));
813 std::string displayName(formatString("%s %s", program, moduleName));
814 exporter->exportModuleHelp(*module->second, tag, displayName);
817 exporter->finishModuleExport();
819 exporter->startModuleGroupExport();
820 CommandLineModuleGroupList::const_iterator group;
821 for (group = groups_.begin(); group != groups_.end(); ++group)
823 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
825 exporter->finishModuleGroupExport();
827 rootTopic_.exportHelp(exporter);
833 /********************************************************************
834 * ModificationCheckingFileOutputStream
837 class ModificationCheckingFileOutputStream : public TextOutputStream
840 ModificationCheckingFileOutputStream(
842 IFileOutputRedirector *redirector)
843 : path_(path), redirector_(redirector)
847 virtual void write(const char *str) { contents_.write(str); }
850 const std::string &newContents = contents_.toString();
851 // TODO: Redirect these for unit tests.
852 if (File::exists(path_, File::returnFalseOnError))
854 const std::string originalContents_
855 = TextReader::readFileToString(path_);
856 if (originalContents_ == newContents)
861 TextWriter writer(redirector_->openTextOutputFile(path_));
862 writer.writeString(newContents);
867 StringOutputStream contents_;
868 IFileOutputRedirector *redirector_;
871 /********************************************************************
872 * ModificationCheckingFileOutputRedirector
875 class ModificationCheckingFileOutputRedirector : public IFileOutputRedirector
878 explicit ModificationCheckingFileOutputRedirector(
879 IFileOutputRedirector *redirector)
880 : redirector_(redirector)
884 virtual TextOutputStream &standardOutput()
886 return redirector_->standardOutput();
888 virtual TextOutputStreamPointer openTextOutputFile(const char *filename)
890 return TextOutputStreamPointer(
891 new ModificationCheckingFileOutputStream(filename, redirector_));
895 IFileOutputRedirector *redirector_;
900 /********************************************************************
901 * CommandLineHelpModule
904 CommandLineHelpModule::CommandLineHelpModule(
905 const IProgramContext &programContext,
906 const std::string &binaryName,
907 const CommandLineModuleMap &modules,
908 const CommandLineModuleGroupList &groups)
909 : impl_(new Impl(programContext, binaryName, modules, groups))
913 CommandLineHelpModule::~CommandLineHelpModule()
917 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
918 const ICommandLineModule &module) const
920 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
923 void CommandLineHelpModule::addTopic(HelpTopicPointer topic, bool bExported)
925 impl_->rootTopic_.addTopic(std::move(topic), bExported);
928 void CommandLineHelpModule::setShowHidden(bool bHidden)
930 impl_->bHidden_ = bHidden;
933 void CommandLineHelpModule::setModuleOverride(
934 const ICommandLineModule &module)
936 impl_->moduleOverride_ = &module;
939 void CommandLineHelpModule::setOutputRedirector(
940 IFileOutputRedirector *output)
942 impl_->outputRedirector_ = output;
945 int CommandLineHelpModule::run(int argc, char *argv[])
947 // Add internal topics lazily here.
948 addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);
950 const char *const exportFormats[] = { "rst", "completion" };
951 std::string exportFormat;
953 options.addOption(StringOption("export").store(&exportFormat)
954 .enumValue(exportFormats));
955 CommandLineParser(&options).parse(&argc, argv);
956 if (!exportFormat.empty())
958 ModificationCheckingFileOutputRedirector redirector(impl_->outputRedirector_);
959 const std::unique_ptr<IHelpExport> exporter(
960 impl_->createExporter(exportFormat, &redirector));
961 impl_->exportHelp(exporter.get());
965 TextOutputStream &outputFile = impl_->outputRedirector_->standardOutput();
966 TextWriter writer(&outputFile);
967 HelpLinks links(eHelpOutputFormat_Console);
968 initProgramLinks(&links, *impl_);
969 CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, &links,
971 context.setShowHidden(impl_->bHidden_);
972 if (impl_->moduleOverride_ != NULL)
974 context.setModuleDisplayName(impl_->programContext_.displayName());
975 impl_->moduleOverride_->writeHelp(context);
978 impl_->context_ = &context;
980 HelpManager helpManager(impl_->rootTopic_, context.writerContext());
983 for (int i = 1; i < argc; ++i)
985 helpManager.enterTopic(argv[i]);
988 catch (const InvalidInputError &ex)
990 fprintf(stderr, "%s\n", ex.what());
993 helpManager.writeCurrentTopic();
997 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
999 const HelpWriterContext &writerContext = context.writerContext();
1001 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
1005 writerContext.writeTextBlock(
1006 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
1007 // TODO: More information.