Sort all includes in src/gromacs
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinemodulemanager.cpp
index 1bdb8a7508c912754deccced96db463901aec359..8fee8aa10e28ca0cef593bdd1b1d1a8ffc6410e5 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
- * David van der Spoel, Berk Hess, Erik Lindahl, and including many
- * others, as listed in the AUTHORS file in the top-level source
- * directory and at http://www.gromacs.org.
+ * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
  *
  * GROMACS is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * \author Teemu Murtola <teemu.murtola@gmail.com>
  * \ingroup module_commandline
  */
+#include "gmxpre.h"
+
 #include "cmdlinemodulemanager.h"
 
+#include "config.h"
+
 #include <cstdio>
 
-#include <map>
 #include <string>
 #include <utility>
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "gromacs/commandline/cmdlinehelpcontext.h"
+#include "gromacs/commandline/cmdlineinit.h"
 #include "gromacs/commandline/cmdlinemodule.h"
-#include "gromacs/onlinehelp/helpformat.h"
-#include "gromacs/onlinehelp/helpmanager.h"
-#include "gromacs/onlinehelp/helptopic.h"
-#include "gromacs/onlinehelp/helpwritercontext.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/commandline/cmdlineparser.h"
+#include "gromacs/commandline/cmdlineprogramcontext.h"
+#include "gromacs/legacyheaders/copyrite.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/options.h"
+#include "gromacs/utility/basenetwork.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/programinfo.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "cmdlinehelpmodule.h"
+#include "cmdlinemodulemanager-impl.h"
+
 namespace gmx
 {
 
-//! Container type for mapping module names to module objects.
-typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
-
 namespace
 {
 
+//! \addtogroup module_commandline
+//! \{
+
 /********************************************************************
- * RootHelpTopic
+ * CMainCommandLineModule
  */
 
-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
- * Help topic that forms the root of the help tree for the help subcommand.
- *
- * \ingroup module_commandline
+/*! \brief
+ * Implements a CommandLineModuleInterface, given a function with C/C++ main()
+ * signature.
  */
-class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
+class CMainCommandLineModule : public CommandLineModuleInterface
 {
     public:
+        //! \copydoc gmx::CommandLineModuleManager::CMainFunction
+        typedef CommandLineModuleManager::CMainFunction CMainFunction;
+
         /*! \brief
-         * Creates a root help topic.
+         * Creates a wrapper module for the given main function.
          *
-         * \param[in] modules  List of modules for to use for module listings.
+         * \param[in] name             Name for the module.
+         * \param[in] shortDescription One-line description for the module.
+         * \param[in] mainFunction     Main function to wrap.
          *
-         * Does not throw.
+         * Does not throw.  This is essential for correct implementation of
+         * CommandLineModuleManager::runAsMainCMain().
          */
-        explicit RootHelpTopic(const CommandLineModuleMap &modules)
-            : modules_(modules)
+        CMainCommandLineModule(const char *name, const char *shortDescription,
+                               CMainFunction mainFunction)
+            : name_(name), shortDescription_(shortDescription),
+              mainFunction_(mainFunction)
         {
         }
 
-        virtual void writeHelp(const HelpWriterContext &context) const;
-
-    private:
-        void printModuleList(const HelpWriterContext &context) const;
-
-        const CommandLineModuleMap &modules_;
-
-        GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
-};
-
-void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
-{
-    if (context.outputFormat() != eHelpOutputFormat_Console)
-    {
-        // TODO: Implement once the situation with Redmine issue #969 is more
-        // clear.
-        GMX_THROW(NotImplementedError(
-                          "Root help is not implemented for this output format"));
-    }
-    writeBasicHelpTopic(context, *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(context);
-    context.writeTextBlock(
-            "For additional help on a command, use '[PROGRAM] help <command>'");
-    writeSubTopicList(context,
-                      "\nAdditional help is available on the following topics:");
-    context.writeTextBlock(
-            "To access the help, use '[PROGRAM] help <topic>'.");
-}
-
-void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
-{
-    if (context.outputFormat() != eHelpOutputFormat_Console)
-    {
-        // TODO: Implement once the situation with Redmine issue #969 is more
-        // clear.
-        GMX_THROW(NotImplementedError(
-                          "Module list is not implemented for this output format"));
-    }
-    int maxNameLength = 0;
-    CommandLineModuleMap::const_iterator module;
-    for (module = modules_.begin(); module != modules_.end(); ++module)
-    {
-        int nameLength = static_cast<int>(module->first.length());
-        if (module->second->shortDescription() != NULL
-            && nameLength > maxNameLength)
+        virtual const char *name() const
         {
-            maxNameLength = nameLength;
+            return name_;
         }
-    }
-    File              &file = context.outputFile();
-    TextTableFormatter formatter;
-    formatter.addColumn(NULL, maxNameLength + 1, false);
-    formatter.addColumn(NULL, 72 - maxNameLength, true);
-    formatter.setFirstColumnIndent(4);
-    file.writeLine();
-    file.writeLine("Available commands:");
-    for (module = modules_.begin(); module != modules_.end(); ++module)
-    {
-        const char *name        = module->first.c_str();
-        const char *description = module->second->shortDescription();
-        if (description != NULL)
+        virtual const char *shortDescription() const
         {
-            formatter.clear();
-            formatter.addColumnLine(0, name);
-            formatter.addColumnLine(1, description);
-            file.writeString(formatter.formatRow());
+            return shortDescription_;
         }
-    }
-}
 
-/********************************************************************
- * 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 void init(CommandLineModuleSettings * /*settings*/)
         {
         }
-
-        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
+        virtual int run(int argc, char *argv[])
         {
-            return NULL;
+            return mainFunction_(argc, argv);
+        }
+        virtual void writeHelp(const CommandLineHelpContext &context) const
+        {
+            writeCommandLineHelpCMain(context, name_, mainFunction_);
         }
-        virtual void writeHelp(const HelpWriterContext &context) const;
 
     private:
-        const CommandLineModuleInterface &module_;
-
-        GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
+        const char             *name_;
+        const char             *shortDescription_;
+        CMainFunction           mainFunction_;
 };
 
-void ModuleHelpTopic::writeHelp(const HelpWriterContext &context) const
-{
-    module_.writeHelp(context);
-}
+//! \}
 
 }   // namespace
 
 /********************************************************************
- * CommandLineHelpModule
+ * CommandLineCommonOptionsHolder
  */
 
-/*! \internal \brief
- * Command-line module for producing help.
- *
- * This module implements the 'help' subcommand that is automatically added by
- * CommandLineModuleManager.
- *
- * \ingroup module_commandline
- */
-class CommandLineHelpModule : public CommandLineModuleInterface
-{
-    public:
-        /*! \brief
-         * Creates a command-line help module.
-         *
-         * \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.
-         *
-         * \param[in] topic  Help topic to add.
-         * \throws    std::bad_alloc if out of memory.
-         */
-        void addTopic(HelpTopicPointer topic);
-
-        virtual const char *name() const { return "help"; }
-        virtual const char *shortDescription() const
-        {
-            return "Print help information";
-        }
-
-        virtual int run(int argc, char *argv[]);
-        virtual void writeHelp(const HelpWriterContext &context) const;
-
-        //! Prints usage message to stderr.
-        void printUsage() const;
-
-    private:
-        CompositeHelpTopicPointer   rootTopic_;
-
-        GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
-};
-
-CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
-    : rootTopic_(new RootHelpTopic(modules))
+CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder()
+    : options_(NULL, NULL), bHelp_(false), bHidden_(false),
+      bQuiet_(false), bVersion_(false), bCopyright_(true),
+      niceLevel_(19), debugLevel_(0)
 {
+    binaryInfoSettings_.copyright(true);
 }
 
-void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
+CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder()
 {
-    rootTopic_->addSubTopic(move(topic));
 }
 
-int CommandLineHelpModule::run(int argc, char *argv[])
+void CommandLineCommonOptionsHolder::initOptions()
 {
-    HelpWriterContext context(&File::standardOutput(),
-                              eHelpOutputFormat_Console);
-    HelpManager       helpManager(*rootTopic_, context);
-    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();
-    fprintf(stderr, "\n");
-    return 0;
+    options_.addOption(BooleanOption("h").store(&bHelp_)
+                           .description("Print help and quit"));
+    options_.addOption(BooleanOption("hidden").store(&bHidden_)
+                           .hidden()
+                           .description("Show hidden options in help"));
+    options_.addOption(BooleanOption("quiet").store(&bQuiet_)
+                           .description("Do not print common startup info or quotes"));
+    options_.addOption(BooleanOption("version").store(&bVersion_)
+                           .description("Print extended version information and quit"));
+    options_.addOption(BooleanOption("copyright").store(&bCopyright_)
+                           .description("Print copyright information on startup"));
+    options_.addOption(IntegerOption("nice").store(&niceLevel_)
+                           .description("Set the nicelevel (default depends on command)"));
+    options_.addOption(IntegerOption("debug").store(&debugLevel_)
+                           .hidden().defaultValueIfSet(1)
+                           .description("Write file with debug information, "
+                                        "1: short (default), 2: also x and f"));
 }
 
-void CommandLineHelpModule::writeHelp(const HelpWriterContext &context) const
+bool CommandLineCommonOptionsHolder::finishOptions()
 {
-    context.writeTextBlock(
-            "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
-    // TODO: More information.
+    options_.finish();
+    binaryInfoSettings_.extendedInfo(bVersion_);
+    // The latter condition suppresses the copyright with
+    // -quiet -version.
+    binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
+    return !bVersion_;
 }
 
-void CommandLineHelpModule::printUsage() const
+void CommandLineCommonOptionsHolder::adjustFromSettings(
+        const CommandLineModuleSettings &settings)
 {
-    HelpWriterContext context(&File::standardError(),
-                              eHelpOutputFormat_Console);
-    rootTopic_->writeHelp(context);
+    if (!options_.isSet("nice"))
+    {
+        niceLevel_ = settings.defaultNiceLevel();
+    }
 }
 
 /********************************************************************
@@ -329,13 +210,29 @@ void CommandLineHelpModule::printUsage() const
 class CommandLineModuleManager::Impl
 {
     public:
-
         /*! \brief
          * Initializes the implementation class.
          *
-         * \param[in] programInfo  Program information for the running binary.
+         * \param[in] binaryName     Name of the running binary
+         *     (without Gromacs binary suffix or .exe on Windows).
+         * \param     programContext Program information for the running binary.
+         */
+        Impl(const char *binaryName, CommandLineProgramContext *programContext);
+
+        /*! \brief
+         * Helper method that adds a given module to the module manager.
+         *
+         * \throws    std::bad_alloc if out of memory.
+         */
+        void addModule(CommandLineModulePointer module);
+        /*! \brief
+         * Creates the help module if it does not yet exist.
+         *
+         * \throws    std::bad_alloc if out of memory.
+         *
+         * This method should be called before accessing \a helpModule_.
          */
-        explicit Impl(const ProgramInfo &programInfo);
+        void ensureHelpModuleExists();
 
         /*! \brief
          * Finds a module that matches a name.
@@ -351,41 +248,105 @@ class CommandLineModuleManager::Impl
         /*! \brief
          * Finds a module that the name of the binary.
          *
-         * \param[in] programInfo  Program information object to use.
+         * \param[in] invokedName  Name by which the program was invoked.
          * \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
+         * is different from \a binaryName_, 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.
+         * Note that the \p invokedName parameter is currently not necessary
+         * (as the program context object is also available and provides this
+         * value), but it clarifies the control flow.
          */
         CommandLineModuleMap::const_iterator
-        findModuleFromBinaryName(const ProgramInfo &programInfo) const;
+        findModuleFromBinaryName(const char *invokedName) const;
+
+        /*! \brief
+         * Processes command-line options for the wrapper binary.
+         *
+         * \param[in,out] optionsHolder Common options.
+         * \param[in,out] argc          On input, argc passed to run().
+         *     On output, argc to be passed to the module.
+         * \param[in,out] argv          On input, argv passed to run().
+         *     On output, argv to be passed to the module.
+         * \throws    InvalidInputError if there are invalid options.
+         * \returns   The module that should be run.
+         *
+         * Handles command-line options that affect the wrapper binary
+         * (potentially changing the members of \c this in response to the
+         * options).  Also finds the module that should be run and the
+         * arguments that should be passed to it.
+         */
+        CommandLineModuleInterface *
+        processCommonOptions(CommandLineCommonOptionsHolder *optionsHolder,
+                             int *argc, char ***argv);
 
         /*! \brief
          * Maps module names to module objects.
          *
          * Owns the contained modules.
          */
-        CommandLineModuleMap    modules_;
+        CommandLineModuleMap         modules_;
+        /*! \brief
+         * List of groupings for modules for help output.
+         *
+         * Owns the contained module group data objects.
+         * CommandLineModuleGroup objects point to the data objects contained
+         * here.
+         */
+        CommandLineModuleGroupList   moduleGroups_;
         //! Information about the currently running program.
-        const ProgramInfo      &programInfo_;
+        CommandLineProgramContext   &programContext_;
+        //! Name of the binary.
+        std::string                  binaryName_;
         /*! \brief
          * Module that implements help for the binary.
          *
          * The pointed module is owned by the \a modules_ container.
          */
-        CommandLineHelpModule  *helpModule_;
+        CommandLineHelpModule       *helpModule_;
+        //! If non-NULL, run this module in single-module mode.
+        CommandLineModuleInterface  *singleModule_;
+        //! Stores the value set with setQuiet().
+        bool                         bQuiet_;
+
+    private:
+        GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
 };
 
-CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
-    : programInfo_(programInfo)
+CommandLineModuleManager::Impl::Impl(const char                *binaryName,
+                                     CommandLineProgramContext *programContext)
+    : programContext_(*programContext),
+      binaryName_(binaryName != NULL ? binaryName : ""),
+      helpModule_(NULL), singleModule_(NULL),
+      bQuiet_(false)
+{
+    GMX_RELEASE_ASSERT(binaryName_.find('-') == std::string::npos,
+                       "Help export does not currently work with binary names with dashes");
+}
+
+void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
+{
+    GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
+               "Attempted to register a duplicate module name");
+    ensureHelpModuleExists();
+    HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
+    modules_.insert(std::make_pair(std::string(module->name()),
+                                   move(module)));
+    helpModule_->addTopic(move(helpTopic));
+}
+
+void CommandLineModuleManager::Impl::ensureHelpModuleExists()
 {
+    if (helpModule_ == NULL)
+    {
+        helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
+                                                modules_, moduleGroups_);
+        addModule(CommandLineModulePointer(helpModule_));
+    }
 }
 
 CommandLineModuleMap::const_iterator
@@ -397,72 +358,315 @@ CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
 
 CommandLineModuleMap::const_iterator
 CommandLineModuleManager::Impl::findModuleFromBinaryName(
-        const ProgramInfo &programInfo) const
+        const char *invokedName) const
 {
-    std::string binaryName = programInfo.invariantProgramName();
-    if (binaryName == programInfo.realBinaryName())
+    std::string moduleName = invokedName;
+#ifdef GMX_BINARY_SUFFIX
+    moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
+#endif
+    if (moduleName == binaryName_)
     {
         return modules_.end();
     }
-    if (binaryName.compare(0, 2, "g_") == 0)
+    if (startsWith(moduleName, "g_"))
+    {
+        moduleName.erase(0, 2);
+    }
+    if (startsWith(moduleName, "gmx"))
+    {
+        moduleName.erase(0, 3);
+    }
+    return findModuleByName(moduleName);
+}
+
+CommandLineModuleInterface *
+CommandLineModuleManager::Impl::processCommonOptions(
+        CommandLineCommonOptionsHolder *optionsHolder, int *argc, char ***argv)
+{
+    // Check if we are directly invoking a certain module.
+    CommandLineModuleInterface *module = singleModule_;
+    if (module == NULL)
+    {
+        // Also check for invokation through named symlinks.
+        CommandLineModuleMap::const_iterator moduleIter
+            = findModuleFromBinaryName(programContext_.programName());
+        if (moduleIter != modules_.end())
+        {
+            module = moduleIter->second.get();
+        }
+    }
+
+    // TODO: It would be nice to propagate at least the -quiet option to
+    // the modules so that they can also be quiet in response to this.
+
+    if (module == NULL)
+    {
+        // If not in single-module mode, process options to the wrapper binary.
+        // TODO: Ideally, this could be done by CommandLineParser.
+        int argcForWrapper = 1;
+        while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
+        {
+            ++argcForWrapper;
+        }
+        if (argcForWrapper > 1)
+        {
+            CommandLineParser(optionsHolder->options())
+                .parse(&argcForWrapper, *argv);
+        }
+        // If no action requested and there is a module specified, process it.
+        if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
+        {
+            const char *moduleName = (*argv)[argcForWrapper];
+            CommandLineModuleMap::const_iterator moduleIter
+                = findModuleByName(moduleName);
+            if (moduleIter == modules_.end())
+            {
+                std::string message =
+                    formatString("'%s' is not a GROMACS command.", moduleName);
+                GMX_THROW(InvalidInputError(message));
+            }
+            module = moduleIter->second.get();
+            *argc -= argcForWrapper;
+            *argv += argcForWrapper;
+            // After this point, argc and argv are the same independent of
+            // which path is taken: (*argv)[0] is the module name.
+        }
+    }
+    if (module != NULL)
+    {
+        if (singleModule_ == NULL)
+        {
+            programContext_.setDisplayName(binaryName_ + " " + module->name());
+        }
+        // Recognize the common options also after the module name.
+        // TODO: It could be nicer to only recognize -h/-hidden if module is not
+        // null.
+        CommandLineParser(optionsHolder->options())
+            .skipUnknown(true).parse(argc, *argv);
+    }
+    if (!optionsHolder->finishOptions())
     {
-        binaryName.erase(0, 2);
+        return NULL;
     }
-    return findModuleByName(binaryName);
+    // If no module specified and no other action, show the help.
+    // Also explicitly specifying -h for the wrapper binary goes here.
+    if (module == NULL || optionsHolder->shouldShowHelp())
+    {
+        ensureHelpModuleExists();
+        if (module != NULL)
+        {
+            helpModule_->setModuleOverride(*module);
+        }
+        *argc  = 1;
+        module = helpModule_;
+    }
+    if (module == helpModule_)
+    {
+        helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
+    }
+    return module;
 }
 
 /********************************************************************
  * CommandLineModuleManager
  */
 
-CommandLineModuleManager::CommandLineModuleManager(const ProgramInfo &programInfo)
-    : impl_(new Impl(programInfo))
+CommandLineModuleManager::CommandLineModuleManager(
+        const char *binaryName, CommandLineProgramContext *programContext)
+    : impl_(new Impl(binaryName, programContext))
 {
-    impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
-    addModule(CommandLineModulePointer(impl_->helpModule_));
 }
 
 CommandLineModuleManager::~CommandLineModuleManager()
 {
 }
 
+void CommandLineModuleManager::setQuiet(bool bQuiet)
+{
+    impl_->bQuiet_ = bQuiet;
+}
+
+void CommandLineModuleManager::setOutputRedirect(File *output)
+{
+    impl_->ensureHelpModuleExists();
+    impl_->helpModule_->setOutputRedirect(output);
+}
+
+void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
+{
+    impl_->singleModule_ = module;
+}
+
 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
 {
-    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));
+    impl_->addModule(move(module));
+}
+
+void CommandLineModuleManager::addModuleCMain(
+        const char *name, const char *shortDescription,
+        CMainFunction mainFunction)
+{
+    CommandLineModulePointer module(
+            new CMainCommandLineModule(name, shortDescription, mainFunction));
+    addModule(move(module));
+}
+
+CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
+        const char *title)
+{
+    const char *const                 binaryName = impl_->binaryName_.c_str();
+    CommandLineModuleGroupDataPointer group(
+            new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
+    impl_->moduleGroups_.push_back(move(group));
+    return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
 }
 
 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
 {
+    impl_->ensureHelpModuleExists();
     impl_->helpModule_->addTopic(move(topic));
 }
 
 int CommandLineModuleManager::run(int argc, char *argv[])
 {
-    int argOffset = 0;
-    CommandLineModuleMap::const_iterator module
-        = impl_->findModuleFromBinaryName(impl_->programInfo_);
-    if (module == impl_->modules_.end())
+    CommandLineModuleInterface    *module;
+    const bool                     bMaster = (gmx_node_rank() == 0);
+    bool                           bQuiet  = impl_->bQuiet_ || !bMaster;
+    CommandLineCommonOptionsHolder optionsHolder;
+    try
+    {
+        optionsHolder.initOptions();
+        module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
+    }
+    catch (const std::exception &)
     {
-        if (argc < 2)
+        bQuiet |= optionsHolder.shouldBeQuiet();
+        if (!bQuiet)
         {
-            impl_->helpModule_->printUsage();
-            return 2;
+            printBinaryInformation(stderr, impl_->programContext_,
+                                   optionsHolder.binaryInfoSettings());
         }
-        module    = impl_->findModuleByName(argv[1]);
-        argOffset = 1;
+        throw;
     }
-    if (module == impl_->modules_.end())
+    bQuiet |= optionsHolder.shouldBeQuiet();
+    if (!bQuiet)
     {
-        fprintf(stderr, "Unknown command: '%s'\n\n", argv[1]);
-        impl_->helpModule_->printUsage();
-        return 2;
+        FILE *out = optionsHolder.startupInfoFile();
+        printBinaryInformation(out, impl_->programContext_,
+                               optionsHolder.binaryInfoSettings());
+        fprintf(out, "\n");
     }
-    return module->second->run(argc - argOffset, argv + argOffset);
+    if (module == NULL)
+    {
+        return 0;
+    }
+
+    CommandLineModuleSettings settings;
+    module->init(&settings);
+    optionsHolder.adjustFromSettings(settings);
+
+    // Open the debug file.
+    if (optionsHolder.debugLevel() > 0)
+    {
+        std::string filename(impl_->programContext_.programName());
+        if (gmx_node_num() > 1)
+        {
+            filename.append(formatString("%d", gmx_node_rank()));
+        }
+        filename.append(".debug");
+
+        fprintf(stderr, "Will write debug log file: %s\n", filename.c_str());
+        gmx_init_debug(optionsHolder.debugLevel(), filename.c_str());
+    }
+#if defined(HAVE_UNISTD_H) && !defined(GMX_NO_NICE) && !defined(__MINGW32__)
+    // Set the nice level unless disabled in the configuration.
+    if (optionsHolder.niceLevel() != 0)
+    {
+        static bool bNiceSet = false; // Only set it once.
+        if (!bNiceSet)
+        {
+            if (nice(optionsHolder.niceLevel()) == -1)
+            {
+                // Do nothing, but use the return value to avoid warnings.
+            }
+            bNiceSet = true;
+        }
+    }
+#endif
+
+    int rc = 0;
+    if (!(module == impl_->helpModule_ && !bMaster))
+    {
+        rc = module->run(argc, argv);
+    }
+    if (!bQuiet)
+    {
+        gmx_thanx(stderr);
+    }
+    return rc;
+}
+
+// static
+int CommandLineModuleManager::runAsMainSingleModule(
+        int argc, char *argv[], CommandLineModuleInterface *module)
+{
+    CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
+    try
+    {
+        CommandLineModuleManager manager(NULL, &programContext);
+        manager.setSingleModule(module);
+        int rc = manager.run(argc, argv);
+        gmx::finalizeForCommandLine();
+        return rc;
+    }
+    catch (const std::exception &ex)
+    {
+        printFatalErrorMessage(stderr, ex);
+        return processExceptionAtExit(ex);
+    }
+}
+
+// static
+int CommandLineModuleManager::runAsMainCMain(
+        int argc, char *argv[], CMainFunction mainFunction)
+{
+    CMainCommandLineModule module(argv[0], NULL, mainFunction);
+    return runAsMainSingleModule(argc, argv, &module);
+}
+
+/********************************************************************
+ * CommandLineModuleGroupData
+ */
+
+void CommandLineModuleGroupData::addModule(const char *name,
+                                           const char *description)
+{
+    CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
+    GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
+                       "Non-existent module added to a group");
+    if (description == NULL)
+    {
+        description = moduleIter->second->shortDescription();
+        GMX_RELEASE_ASSERT(description != NULL,
+                           "Module without a description added to a group");
+    }
+    std::string       tag(formatString("%s-%s", binaryName_, name));
+    modules_.push_back(std::make_pair(tag, description));
+}
+
+/********************************************************************
+ * CommandLineModuleGroup
+ */
+
+void CommandLineModuleGroup::addModule(const char *name)
+{
+    impl_->addModule(name, NULL);
+}
+
+void CommandLineModuleGroup::addModuleWithDescription(const char *name,
+                                                      const char *description)
+{
+    impl_->addModule(name, description);
 }
 
 } // namespace gmx