#include "gromacs/commandline/cmdlinemodule.h"
#include "gromacs/onlinehelp/helpformat.h"
+#include "gromacs/onlinehelp/helpmanager.h"
+#include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/utility/file.h"
+#include "gromacs/utility/format.h"
+#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/programinfo.h"
namespace gmx
{
+//! Container type for mapping module names to module objects.
+typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
+
+namespace
+{
+
/********************************************************************
- * CommandLineModuleManager::Impl
+ * RootHelpTopic
*/
+struct RootHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+// The first two are not used.
+const char RootHelpText::name[] = "";
+const char RootHelpText::title[] = "";
+const char *const RootHelpText::text[] = {
+ "Usage: [PROGRAM] <command> [<args>]",
+};
+
/*! \internal \brief
- * Private implementation class for CommandLineModuleManager.
+ * Help topic that forms the root of the help tree for the help subcommand.
*
* \ingroup module_commandline
*/
-class CommandLineModuleManager::Impl
+class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
{
public:
- //! Container for mapping module names to module objects.
- typedef std::map<std::string, CommandLineModulePointer> ModuleMap;
-
- /*! \brief
- * Initializes the implementation class.
- *
- * \param[in] programInfo Program information for the running binary.
- */
- explicit Impl(const ProgramInfo &programInfo);
-
/*! \brief
- * Finds a module that matches a name.
+ * Creates a root help topic.
*
- * \param[in] name Module name to find.
- * \returns Iterator to the found module, or
- * \c modules_.end() if not found.
+ * \param[in] modules List of modules for to use for module listings.
*
* Does not throw.
*/
- ModuleMap::const_iterator findModuleByName(const std::string &name) const;
- /*! \brief
- * Finds a module that the name of the binary.
- *
- * \param[in] programInfo Program information object to use.
- * \throws std::bad_alloc if out of memory.
- * \returns Iterator to the found module, or
- * \c modules_.end() if not found.
- *
- * Checks whether the program is invoked through a symlink whose name
- * is different from ProgramInfo::realBinaryName(), and if so, checks
- * if a module name matches the name of the symlink.
- *
- * Note that the \p programInfo parameter is currently not necessary
- * (as the program info object is also contained as a member), but it
- * clarifies the control flow.
- */
- ModuleMap::const_iterator
- findModuleFromBinaryName(const ProgramInfo &programInfo) const;
-
- //! Prints usage message to stderr.
- void printUsage(bool bModuleList) const;
- //! Prints the list of modules to stderr.
- void printModuleList() const;
+ explicit RootHelpTopic(const CommandLineModuleMap &modules)
+ : modules_(modules)
+ {
+ }
- /*! \brief
- * Maps module names to module objects.
- *
- * Owns the contained modules.
- */
- ModuleMap modules_;
- //! Information about the currently running program.
- const ProgramInfo &programInfo_;
-};
+ virtual void writeHelp(File *file) const;
-CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
- : programInfo_(programInfo)
-{
-}
+ private:
+ void printModuleList(File *file) const;
-CommandLineModuleManager::Impl::ModuleMap::const_iterator
-CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
-{
- // TODO: Accept unambiguous prefixes?
- return modules_.find(name);
-}
+ const CommandLineModuleMap &modules_;
-CommandLineModuleManager::Impl::ModuleMap::const_iterator
-CommandLineModuleManager::Impl::findModuleFromBinaryName(
- const ProgramInfo &programInfo) const
-{
- std::string binaryName = programInfo.invariantProgramName();
- if (binaryName == programInfo.realBinaryName())
- {
- return modules_.end();
- }
- if (binaryName.compare(0, 2, "g_") == 0)
- {
- binaryName.erase(0, 2);
- }
- return findModuleByName(binaryName);
-}
+ GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
+};
-void CommandLineModuleManager::Impl::printUsage(bool bModuleList) const
+void RootHelpTopic::writeHelp(File *file) const
{
- const char *program = programInfo_.programName().c_str();
- fprintf(stderr, "Usage: %s <command> [<args>]\n\n", program);
- if (bModuleList)
- {
- printModuleList();
- }
- else
- {
- fprintf(stderr, "See '%s help' for list of commands.\n", program);
- }
+ writeBasicHelpTopic(file, *this, helpText());
+ // TODO: If/when this list becomes long, it may be better to only print
+ // "common" commands here, and have a separate topic (e.g.,
+ // "help commands") that prints the full list.
+ printModuleList(file);
+ writeHelpTextForConsole(file,
+ "For additional help on a command, use '[PROGRAM] help <command>'");
+ writeSubTopicList(file, "\nAdditional help is available on the following topics:");
+ writeHelpTextForConsole(file,
+ "To access the help, use '[PROGRAM] help <topic>'.");
}
-void CommandLineModuleManager::Impl::printModuleList() const
+void RootHelpTopic::printModuleList(File *file) const
{
int maxNameLength = 0;
- ModuleMap::const_iterator module;
+ CommandLineModuleMap::const_iterator module;
for (module = modules_.begin(); module != modules_.end(); ++module)
{
int nameLength = static_cast<int>(module->first.length());
formatter.addColumn(NULL, maxNameLength + 1, false);
formatter.addColumn(NULL, 72 - maxNameLength, true);
formatter.setFirstColumnIndent(4);
- fprintf(stderr, "The following commands are available:\n");
+ file->writeLine();
+ file->writeLine("Available commands:");
for (module = modules_.begin(); module != modules_.end(); ++module)
{
const char *name = module->first.c_str();
formatter.clear();
formatter.addColumnLine(0, name);
formatter.addColumnLine(1, description);
- fprintf(stderr, "%s", formatter.formatRow().c_str());
+ file->writeString(formatter.formatRow());
}
}
-
/********************************************************************
- * CommandLineHelpModule
+ * ModuleHelpTopic
+ */
+
+/*! \internal \brief
+ * Help topic wrapper for a command-line module.
+ *
+ * This class implements HelpTopicInterface such that it wraps a
+ * CommandLineModuleInterface, allowing subcommand "help <command>"
+ * to produce the help for "<command>".
+ *
+ * \ingroup module_commandline
*/
+class ModuleHelpTopic : public HelpTopicInterface
+{
+ public:
+ //! Constructs a help topic for a specific module.
+ explicit ModuleHelpTopic(const CommandLineModuleInterface &module)
+ : module_(module)
+ {
+ }
+
+ virtual const char *name() const { return module_.name(); }
+ virtual const char *title() const { return NULL; }
+ virtual bool hasSubTopics() const { return false; }
+ virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
+ {
+ return NULL;
+ }
+ virtual void writeHelp(File *file) const;
+
+ private:
+ const CommandLineModuleInterface &module_;
-namespace internal
+ GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
+};
+
+void ModuleHelpTopic::writeHelp(File *file) const
{
+ module_.writeHelp(file);
+}
+
+} // namespace
+
+/********************************************************************
+ * CommandLineHelpModule
+ */
/*! \internal \brief
* Command-line module for producing help.
{
public:
/*! \brief
- * Creates a help module for the given module manager.
+ * Creates a command-line help module.
*
- * \param[in] manager Manager for which this module provides help.
+ * \param[in] modules List of modules for to use for module listings.
+ * \throws std::bad_alloc if out of memory.
+ */
+ explicit CommandLineHelpModule(const CommandLineModuleMap &modules);
+
+ /*! \brief
+ * Adds a top-level help topic.
*
- * Does not throw.
+ * \param[in] topic Help topic to add.
+ * \throws std::bad_alloc if out of memory.
*/
- explicit CommandLineHelpModule(const CommandLineModuleManager &manager);
+ void addTopic(HelpTopicPointer topic);
virtual const char *name() const { return "help"; }
virtual const char *shortDescription() const
}
virtual int run(int argc, char *argv[]);
+ virtual void writeHelp(File *file) const;
+
+ //! Prints usage message to stderr.
+ void printUsage() const;
private:
- const CommandLineModuleManager &manager_;
+ CompositeHelpTopicPointer rootTopic_;
GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
};
-CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleManager &manager)
- : manager_(manager)
+CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
+ : rootTopic_(new RootHelpTopic(modules))
+{
+}
+
+void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
{
+ rootTopic_->addSubTopic(move(topic));
}
int CommandLineHelpModule::run(int argc, char *argv[])
{
- manager_.impl_->printUsage(true);
+ HelpManager helpManager(*rootTopic_);
+ try
+ {
+ for (int i = 1; i < argc; ++i)
+ {
+ helpManager.enterTopic(argv[i]);
+ }
+ }
+ catch (const InvalidInputError &ex)
+ {
+ fprintf(stderr, "%s\n", ex.what());
+ return 2;
+ }
+ helpManager.writeCurrentTopic(&File::standardOutput());
+ fprintf(stderr, "\n");
return 0;
}
-} // namespace internal
+void CommandLineHelpModule::writeHelp(File *file) const
+{
+ writeHelpTextForConsole(file,
+ "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
+ // TODO: More information.
+}
+
+void CommandLineHelpModule::printUsage() const
+{
+ rootTopic_->writeHelp(&File::standardError());
+}
+
+/********************************************************************
+ * CommandLineModuleManager::Impl
+ */
+
+/*! \internal \brief
+ * Private implementation class for CommandLineModuleManager.
+ *
+ * \ingroup module_commandline
+ */
+class CommandLineModuleManager::Impl
+{
+ public:
+
+ /*! \brief
+ * Initializes the implementation class.
+ *
+ * \param[in] programInfo Program information for the running binary.
+ */
+ explicit Impl(const ProgramInfo &programInfo);
+
+ /*! \brief
+ * Finds a module that matches a name.
+ *
+ * \param[in] name Module name to find.
+ * \returns Iterator to the found module, or
+ * \c modules_.end() if not found.
+ *
+ * Does not throw.
+ */
+ CommandLineModuleMap::const_iterator
+ findModuleByName(const std::string &name) const;
+ /*! \brief
+ * Finds a module that the name of the binary.
+ *
+ * \param[in] programInfo Program information object to use.
+ * \throws std::bad_alloc if out of memory.
+ * \returns Iterator to the found module, or
+ * \c modules_.end() if not found.
+ *
+ * Checks whether the program is invoked through a symlink whose name
+ * is different from ProgramInfo::realBinaryName(), and if so, checks
+ * if a module name matches the name of the symlink.
+ *
+ * Note that the \p programInfo parameter is currently not necessary
+ * (as the program info object is also contained as a member), but it
+ * clarifies the control flow.
+ */
+ CommandLineModuleMap::const_iterator
+ findModuleFromBinaryName(const ProgramInfo &programInfo) const;
+
+ //! Prints usage message to stderr.
+ void printUsage(bool bModuleList) const;
+ //! Prints the list of modules to stderr.
+ void printModuleList() const;
+
+ /*! \brief
+ * Maps module names to module objects.
+ *
+ * Owns the contained modules.
+ */
+ CommandLineModuleMap modules_;
+ //! Information about the currently running program.
+ const ProgramInfo &programInfo_;
+ /*! \brief
+ * Module that implements help for the binary.
+ *
+ * The pointed module is owned by the \a modules_ container.
+ */
+ CommandLineHelpModule *helpModule_;
+};
+
+CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
+ : programInfo_(programInfo)
+{
+}
+
+CommandLineModuleMap::const_iterator
+CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
+{
+ // TODO: Accept unambiguous prefixes?
+ return modules_.find(name);
+}
+CommandLineModuleMap::const_iterator
+CommandLineModuleManager::Impl::findModuleFromBinaryName(
+ const ProgramInfo &programInfo) const
+{
+ std::string binaryName = programInfo.invariantProgramName();
+ if (binaryName == programInfo.realBinaryName())
+ {
+ return modules_.end();
+ }
+ if (binaryName.compare(0, 2, "g_") == 0)
+ {
+ binaryName.erase(0, 2);
+ }
+ return findModuleByName(binaryName);
+}
/********************************************************************
* CommandLineModuleManager
CommandLineModuleManager::CommandLineModuleManager(const ProgramInfo &programInfo)
: impl_(new Impl(programInfo))
{
- addModule(CommandLineModulePointer(new internal::CommandLineHelpModule(*this)));
+ impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
+ addModule(CommandLineModulePointer(impl_->helpModule_));
}
CommandLineModuleManager::~CommandLineModuleManager()
{
GMX_ASSERT(impl_->modules_.find(module->name()) == impl_->modules_.end(),
"Attempted to register a duplicate module name");
+ HelpTopicPointer helpTopic(new ModuleHelpTopic(*module));
impl_->modules_.insert(std::make_pair(std::string(module->name()),
move(module)));
+ addHelpTopic(move(helpTopic));
+}
+
+void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
+{
+ impl_->helpModule_->addTopic(move(topic));
}
int CommandLineModuleManager::run(int argc, char *argv[])
{
int argOffset = 0;
- Impl::ModuleMap::const_iterator module
+ CommandLineModuleMap::const_iterator module
= impl_->findModuleFromBinaryName(impl_->programInfo_);
if (module == impl_->modules_.end())
{
if (argc < 2)
{
- fprintf(stderr, "\n");
- impl_->printUsage(false);
+ impl_->helpModule_->printUsage();
return 2;
}
module = impl_->findModuleByName(argv[1]);
}
if (module == impl_->modules_.end())
{
- fprintf(stderr, "\n");
- fprintf(stderr, "Unknown command: '%s'\n", argv[1]);
- impl_->printUsage(true);
+ fprintf(stderr, "Unknown command: '%s'\n\n", argv[1]);
+ impl_->helpModule_->printUsage();
return 2;
}
return module->second->run(argc - argOffset, argv + argOffset);