2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * 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::CommandLineModuleManager.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_commandline
42 #include "cmdlinemodulemanager.h"
50 #include "gromacs/legacyheaders/copyrite.h"
52 #include "gromacs/commandline/cmdlinemodule.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/utility/file.h"
58 #include "gromacs/utility/exceptions.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/programinfo.h"
61 #include "gromacs/utility/stringutil.h"
66 //! Container type for mapping module names to module objects.
67 typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
72 /********************************************************************
78 static const char name[];
79 static const char title[];
80 static const char *const text[];
83 // The first two are not used.
84 const char RootHelpText::name[] = "";
85 const char RootHelpText::title[] = "";
86 const char *const RootHelpText::text[] = {
87 "Usage: [PROGRAM] <command> [<args>]",
91 * Help topic that forms the root of the help tree for the help subcommand.
93 * \ingroup module_commandline
95 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
99 * Creates a root help topic.
101 * \param[in] modules List of modules for to use for module listings.
105 explicit RootHelpTopic(const CommandLineModuleMap &modules)
110 virtual void writeHelp(const HelpWriterContext &context) const;
113 void printModuleList(const HelpWriterContext &context) const;
115 const CommandLineModuleMap &modules_;
117 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
120 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
122 if (context.outputFormat() != eHelpOutputFormat_Console)
124 // TODO: Implement once the situation with Redmine issue #969 is more
126 GMX_THROW(NotImplementedError(
127 "Root help is not implemented for this output format"));
129 writeBasicHelpTopic(context, *this, helpText());
130 // TODO: If/when this list becomes long, it may be better to only print
131 // "common" commands here, and have a separate topic (e.g.,
132 // "help commands") that prints the full list.
133 printModuleList(context);
134 context.writeTextBlock(
135 "For additional help on a command, use '[PROGRAM] help <command>'");
136 writeSubTopicList(context,
137 "\nAdditional help is available on the following topics:");
138 context.writeTextBlock(
139 "To access the help, use '[PROGRAM] help <topic>'.");
142 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
144 if (context.outputFormat() != eHelpOutputFormat_Console)
146 // TODO: Implement once the situation with Redmine issue #969 is more
148 GMX_THROW(NotImplementedError(
149 "Module list is not implemented for this output format"));
151 int maxNameLength = 0;
152 CommandLineModuleMap::const_iterator module;
153 for (module = modules_.begin(); module != modules_.end(); ++module)
155 int nameLength = static_cast<int>(module->first.length());
156 if (module->second->shortDescription() != NULL
157 && nameLength > maxNameLength)
159 maxNameLength = nameLength;
162 File &file = context.outputFile();
163 TextTableFormatter formatter;
164 formatter.addColumn(NULL, maxNameLength + 1, false);
165 formatter.addColumn(NULL, 72 - maxNameLength, true);
166 formatter.setFirstColumnIndent(4);
168 file.writeLine("Available commands:");
169 for (module = modules_.begin(); module != modules_.end(); ++module)
171 const char *name = module->first.c_str();
172 const char *description = module->second->shortDescription();
173 if (description != NULL)
176 formatter.addColumnLine(0, name);
177 formatter.addColumnLine(1, description);
178 file.writeString(formatter.formatRow());
183 /********************************************************************
188 * Help topic wrapper for a command-line module.
190 * This class implements HelpTopicInterface such that it wraps a
191 * CommandLineModuleInterface, allowing subcommand "help <command>"
192 * to produce the help for "<command>".
194 * \ingroup module_commandline
196 class ModuleHelpTopic : public HelpTopicInterface
199 //! Constructs a help topic for a specific module.
200 explicit ModuleHelpTopic(const CommandLineModuleInterface &module)
205 virtual const char *name() const { return module_.name(); }
206 virtual const char *title() const { return NULL; }
207 virtual bool hasSubTopics() const { return false; }
208 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
212 virtual void writeHelp(const HelpWriterContext &context) const;
215 const CommandLineModuleInterface &module_;
217 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
220 void ModuleHelpTopic::writeHelp(const HelpWriterContext &context) const
222 module_.writeHelp(context);
227 /********************************************************************
228 * CommandLineHelpModule
232 * Command-line module for producing help.
234 * This module implements the 'help' subcommand that is automatically added by
235 * CommandLineModuleManager.
237 * \ingroup module_commandline
239 class CommandLineHelpModule : public CommandLineModuleInterface
243 * Creates a command-line help module.
245 * \param[in] modules List of modules for to use for module listings.
246 * \throws std::bad_alloc if out of memory.
248 explicit CommandLineHelpModule(const CommandLineModuleMap &modules);
251 * Adds a top-level help topic.
253 * \param[in] topic Help topic to add.
254 * \throws std::bad_alloc if out of memory.
256 void addTopic(HelpTopicPointer topic);
258 virtual const char *name() const { return "help"; }
259 virtual const char *shortDescription() const
261 return "Print help information";
264 virtual int run(int argc, char *argv[]);
265 virtual void writeHelp(const HelpWriterContext &context) const;
267 //! Prints usage message to stderr.
268 void printUsage() const;
271 CompositeHelpTopicPointer rootTopic_;
273 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
276 CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
277 : rootTopic_(new RootHelpTopic(modules))
281 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
283 rootTopic_->addSubTopic(move(topic));
286 int CommandLineHelpModule::run(int argc, char *argv[])
288 HelpWriterContext context(&File::standardOutput(),
289 eHelpOutputFormat_Console);
290 HelpManager helpManager(*rootTopic_, context);
293 for (int i = 1; i < argc; ++i)
295 helpManager.enterTopic(argv[i]);
298 catch (const InvalidInputError &ex)
300 fprintf(stderr, "%s\n", ex.what());
303 helpManager.writeCurrentTopic();
307 void CommandLineHelpModule::writeHelp(const HelpWriterContext &context) const
309 context.writeTextBlock(
310 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
311 // TODO: More information.
314 void CommandLineHelpModule::printUsage() const
316 HelpWriterContext context(&File::standardError(),
317 eHelpOutputFormat_Console);
318 rootTopic_->writeHelp(context);
321 /********************************************************************
322 * CommandLineModuleManager::Impl
326 * Private implementation class for CommandLineModuleManager.
328 * \ingroup module_commandline
330 class CommandLineModuleManager::Impl
335 * Initializes the implementation class.
337 * \param[in] programInfo Program information for the running binary.
339 explicit Impl(const ProgramInfo &programInfo);
342 * Finds a module that matches a name.
344 * \param[in] name Module name to find.
345 * \returns Iterator to the found module, or
346 * \c modules_.end() if not found.
350 CommandLineModuleMap::const_iterator
351 findModuleByName(const std::string &name) const;
353 * Finds a module that the name of the binary.
355 * \param[in] programInfo Program information object to use.
356 * \throws std::bad_alloc if out of memory.
357 * \returns Iterator to the found module, or
358 * \c modules_.end() if not found.
360 * Checks whether the program is invoked through a symlink whose name
361 * is different from ProgramInfo::realBinaryName(), and if so, checks
362 * if a module name matches the name of the symlink.
364 * Note that the \p programInfo parameter is currently not necessary
365 * (as the program info object is also contained as a member), but it
366 * clarifies the control flow.
368 CommandLineModuleMap::const_iterator
369 findModuleFromBinaryName(const ProgramInfo &programInfo) const;
372 * Maps module names to module objects.
374 * Owns the contained modules.
376 CommandLineModuleMap modules_;
377 //! Information about the currently running program.
378 const ProgramInfo &programInfo_;
380 * Module that implements help for the binary.
382 * The pointed module is owned by the \a modules_ container.
384 CommandLineHelpModule *helpModule_;
387 CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
388 : programInfo_(programInfo), helpModule_(NULL)
392 CommandLineModuleMap::const_iterator
393 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
395 // TODO: Accept unambiguous prefixes?
396 return modules_.find(name);
399 CommandLineModuleMap::const_iterator
400 CommandLineModuleManager::Impl::findModuleFromBinaryName(
401 const ProgramInfo &programInfo) const
403 std::string binaryName = programInfo.invariantProgramName();
404 if (binaryName == programInfo.realBinaryName())
406 return modules_.end();
408 if (binaryName.compare(0, 2, "g_") == 0)
410 binaryName.erase(0, 2);
412 return findModuleByName(binaryName);
415 /********************************************************************
416 * CommandLineModuleManager
419 CommandLineModuleManager::CommandLineModuleManager(const ProgramInfo &programInfo)
420 : impl_(new Impl(programInfo))
422 impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
423 addModule(CommandLineModulePointer(impl_->helpModule_));
426 CommandLineModuleManager::~CommandLineModuleManager()
430 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
432 GMX_ASSERT(impl_->modules_.find(module->name()) == impl_->modules_.end(),
433 "Attempted to register a duplicate module name");
434 HelpTopicPointer helpTopic(new ModuleHelpTopic(*module));
435 impl_->modules_.insert(std::make_pair(std::string(module->name()),
437 addHelpTopic(move(helpTopic));
440 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
442 impl_->helpModule_->addTopic(move(topic));
445 int CommandLineModuleManager::run(int argc, char *argv[])
448 CommandLineModuleMap::const_iterator module
449 = impl_->findModuleFromBinaryName(impl_->programInfo_);
450 if (module == impl_->modules_.end())
454 impl_->helpModule_->printUsage();
458 module = impl_->findModuleByName(argv[1]);
461 if (module == impl_->modules_.end())
463 fprintf(stderr, "Unknown command: '%s'\n\n", argv[1]);
464 impl_->helpModule_->printUsage();
468 int rc = module->second->run(argc - argOffset, argv + argOffset);