3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2009, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
33 * Implements gmx::CommandLineModuleManager.
35 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36 * \ingroup module_commandline
38 #include "cmdlinemodulemanager.h"
46 #include "gromacs/commandline/cmdlinemodule.h"
47 #include "gromacs/onlinehelp/helpformat.h"
48 #include "gromacs/onlinehelp/helpmanager.h"
49 #include "gromacs/onlinehelp/helptopic.h"
50 #include "gromacs/onlinehelp/helpwritercontext.h"
51 #include "gromacs/utility/file.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/gmxassert.h"
54 #include "gromacs/utility/programinfo.h"
55 #include "gromacs/utility/stringutil.h"
60 //! Container type for mapping module names to module objects.
61 typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
66 /********************************************************************
72 static const char name[];
73 static const char title[];
74 static const char *const text[];
77 // The first two are not used.
78 const char RootHelpText::name[] = "";
79 const char RootHelpText::title[] = "";
80 const char *const RootHelpText::text[] = {
81 "Usage: [PROGRAM] <command> [<args>]",
85 * Help topic that forms the root of the help tree for the help subcommand.
87 * \ingroup module_commandline
89 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
93 * Creates a root help topic.
95 * \param[in] modules List of modules for to use for module listings.
99 explicit RootHelpTopic(const CommandLineModuleMap &modules)
104 virtual void writeHelp(const HelpWriterContext &context) const;
107 void printModuleList(const HelpWriterContext &context) const;
109 const CommandLineModuleMap &modules_;
111 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
114 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
116 if (context.outputFormat() != eHelpOutputFormat_Console)
118 // TODO: Implement once the situation with Redmine issue #969 is more
120 GMX_THROW(NotImplementedError(
121 "Root help is not implemented for this output format"));
123 writeBasicHelpTopic(context, *this, helpText());
124 // TODO: If/when this list becomes long, it may be better to only print
125 // "common" commands here, and have a separate topic (e.g.,
126 // "help commands") that prints the full list.
127 printModuleList(context);
128 context.writeTextBlock(
129 "For additional help on a command, use '[PROGRAM] help <command>'");
130 writeSubTopicList(context,
131 "\nAdditional help is available on the following topics:");
132 context.writeTextBlock(
133 "To access the help, use '[PROGRAM] help <topic>'.");
136 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
138 if (context.outputFormat() != eHelpOutputFormat_Console)
140 // TODO: Implement once the situation with Redmine issue #969 is more
142 GMX_THROW(NotImplementedError(
143 "Module list is not implemented for this output format"));
145 int maxNameLength = 0;
146 CommandLineModuleMap::const_iterator module;
147 for (module = modules_.begin(); module != modules_.end(); ++module)
149 int nameLength = static_cast<int>(module->first.length());
150 if (module->second->shortDescription() != NULL
151 && nameLength > maxNameLength)
153 maxNameLength = nameLength;
156 File &file = context.outputFile();
157 TextTableFormatter formatter;
158 formatter.addColumn(NULL, maxNameLength + 1, false);
159 formatter.addColumn(NULL, 72 - maxNameLength, true);
160 formatter.setFirstColumnIndent(4);
162 file.writeLine("Available commands:");
163 for (module = modules_.begin(); module != modules_.end(); ++module)
165 const char *name = module->first.c_str();
166 const char *description = module->second->shortDescription();
167 if (description != NULL)
170 formatter.addColumnLine(0, name);
171 formatter.addColumnLine(1, description);
172 file.writeString(formatter.formatRow());
177 /********************************************************************
182 * Help topic wrapper for a command-line module.
184 * This class implements HelpTopicInterface such that it wraps a
185 * CommandLineModuleInterface, allowing subcommand "help <command>"
186 * to produce the help for "<command>".
188 * \ingroup module_commandline
190 class ModuleHelpTopic : public HelpTopicInterface
193 //! Constructs a help topic for a specific module.
194 explicit ModuleHelpTopic(const CommandLineModuleInterface &module)
199 virtual const char *name() const { return module_.name(); }
200 virtual const char *title() const { return NULL; }
201 virtual bool hasSubTopics() const { return false; }
202 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
206 virtual void writeHelp(const HelpWriterContext &context) const;
209 const CommandLineModuleInterface &module_;
211 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
214 void ModuleHelpTopic::writeHelp(const HelpWriterContext &context) const
216 module_.writeHelp(context);
221 /********************************************************************
222 * CommandLineHelpModule
226 * Command-line module for producing help.
228 * This module implements the 'help' subcommand that is automatically added by
229 * CommandLineModuleManager.
231 * \ingroup module_commandline
233 class CommandLineHelpModule : public CommandLineModuleInterface
237 * Creates a command-line help module.
239 * \param[in] modules List of modules for to use for module listings.
240 * \throws std::bad_alloc if out of memory.
242 explicit CommandLineHelpModule(const CommandLineModuleMap &modules);
245 * Adds a top-level help topic.
247 * \param[in] topic Help topic to add.
248 * \throws std::bad_alloc if out of memory.
250 void addTopic(HelpTopicPointer topic);
252 virtual const char *name() const { return "help"; }
253 virtual const char *shortDescription() const
255 return "Print help information";
258 virtual int run(int argc, char *argv[]);
259 virtual void writeHelp(const HelpWriterContext &context) const;
261 //! Prints usage message to stderr.
262 void printUsage() const;
265 CompositeHelpTopicPointer rootTopic_;
267 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
270 CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
271 : rootTopic_(new RootHelpTopic(modules))
275 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
277 rootTopic_->addSubTopic(move(topic));
280 int CommandLineHelpModule::run(int argc, char *argv[])
282 HelpWriterContext context(&File::standardOutput(),
283 eHelpOutputFormat_Console);
284 HelpManager helpManager(*rootTopic_, context);
287 for (int i = 1; i < argc; ++i)
289 helpManager.enterTopic(argv[i]);
292 catch (const InvalidInputError &ex)
294 fprintf(stderr, "%s\n", ex.what());
297 helpManager.writeCurrentTopic();
298 fprintf(stderr, "\n");
302 void CommandLineHelpModule::writeHelp(const HelpWriterContext &context) const
304 context.writeTextBlock(
305 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
306 // TODO: More information.
309 void CommandLineHelpModule::printUsage() const
311 HelpWriterContext context(&File::standardError(),
312 eHelpOutputFormat_Console);
313 rootTopic_->writeHelp(context);
316 /********************************************************************
317 * CommandLineModuleManager::Impl
321 * Private implementation class for CommandLineModuleManager.
323 * \ingroup module_commandline
325 class CommandLineModuleManager::Impl
330 * Initializes the implementation class.
332 * \param[in] programInfo Program information for the running binary.
334 explicit Impl(const ProgramInfo &programInfo);
337 * Finds a module that matches a name.
339 * \param[in] name Module name to find.
340 * \returns Iterator to the found module, or
341 * \c modules_.end() if not found.
345 CommandLineModuleMap::const_iterator
346 findModuleByName(const std::string &name) const;
348 * Finds a module that the name of the binary.
350 * \param[in] programInfo Program information object to use.
351 * \throws std::bad_alloc if out of memory.
352 * \returns Iterator to the found module, or
353 * \c modules_.end() if not found.
355 * Checks whether the program is invoked through a symlink whose name
356 * is different from ProgramInfo::realBinaryName(), and if so, checks
357 * if a module name matches the name of the symlink.
359 * Note that the \p programInfo parameter is currently not necessary
360 * (as the program info object is also contained as a member), but it
361 * clarifies the control flow.
363 CommandLineModuleMap::const_iterator
364 findModuleFromBinaryName(const ProgramInfo &programInfo) const;
367 * Maps module names to module objects.
369 * Owns the contained modules.
371 CommandLineModuleMap modules_;
372 //! Information about the currently running program.
373 const ProgramInfo &programInfo_;
375 * Module that implements help for the binary.
377 * The pointed module is owned by the \a modules_ container.
379 CommandLineHelpModule *helpModule_;
382 CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
383 : programInfo_(programInfo)
387 CommandLineModuleMap::const_iterator
388 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
390 // TODO: Accept unambiguous prefixes?
391 return modules_.find(name);
394 CommandLineModuleMap::const_iterator
395 CommandLineModuleManager::Impl::findModuleFromBinaryName(
396 const ProgramInfo &programInfo) const
398 std::string binaryName = programInfo.invariantProgramName();
399 if (binaryName == programInfo.realBinaryName())
401 return modules_.end();
403 if (binaryName.compare(0, 2, "g_") == 0)
405 binaryName.erase(0, 2);
407 return findModuleByName(binaryName);
410 /********************************************************************
411 * CommandLineModuleManager
414 CommandLineModuleManager::CommandLineModuleManager(const ProgramInfo &programInfo)
415 : impl_(new Impl(programInfo))
417 impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
418 addModule(CommandLineModulePointer(impl_->helpModule_));
421 CommandLineModuleManager::~CommandLineModuleManager()
425 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
427 GMX_ASSERT(impl_->modules_.find(module->name()) == impl_->modules_.end(),
428 "Attempted to register a duplicate module name");
429 HelpTopicPointer helpTopic(new ModuleHelpTopic(*module));
430 impl_->modules_.insert(std::make_pair(std::string(module->name()),
432 addHelpTopic(move(helpTopic));
435 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
437 impl_->helpModule_->addTopic(move(topic));
440 int CommandLineModuleManager::run(int argc, char *argv[])
443 CommandLineModuleMap::const_iterator module
444 = impl_->findModuleFromBinaryName(impl_->programInfo_);
445 if (module == impl_->modules_.end())
449 impl_->helpModule_->printUsage();
452 module = impl_->findModuleByName(argv[1]);
455 if (module == impl_->modules_.end())
457 fprintf(stderr, "Unknown command: '%s'\n\n", argv[1]);
458 impl_->helpModule_->printUsage();
461 return module->second->run(argc - argOffset, argv + argOffset);