diff --git a/source/cppassist/CMakeLists.txt b/source/cppassist/CMakeLists.txt
index 89e05c5..5a87b6f 100644
--- a/source/cppassist/CMakeLists.txt
+++ b/source/cppassist/CMakeLists.txt
@@ -101,7 +101,6 @@ set(headers
     ${include_path}/cmdline/ArgumentParser.h
     ${include_path}/cmdline/ArgumentParser.inl
     ${include_path}/cmdline/CommandLineProgram.h
-    ${include_path}/cmdline/CommandLineAction.h
     ${include_path}/cmdline/CommandLineCommand.h
     ${include_path}/cmdline/CommandLineOption.h
     ${include_path}/cmdline/CommandLineSwitch.h
@@ -130,7 +129,6 @@ set(sources
 
     ${source_path}/cmdline/ArgumentParser.cpp
     ${source_path}/cmdline/CommandLineProgram.cpp
-    ${source_path}/cmdline/CommandLineAction.cpp
     ${source_path}/cmdline/CommandLineCommand.cpp
     ${source_path}/cmdline/CommandLineOption.cpp
     ${source_path}/cmdline/CommandLineSwitch.cpp
diff --git a/source/cppassist/include/cppassist/cmdline/CommandLineAction.h b/source/cppassist/include/cppassist/cmdline/CommandLineAction.h
deleted file mode 100644
index 79186e3..0000000
--- a/source/cppassist/include/cppassist/cmdline/CommandLineAction.h
+++ /dev/null
@@ -1,388 +0,0 @@
-
-#pragma once
-
-
-#include <string>
-#include <vector>
-
-#include <cppassist/cppassist_api.h>
-
-
-namespace cppassist
-{
-
-
-class CommandLineCommand;
-class CommandLineOption;
-class CommandLineSwitch;
-class CommandLineParameter;
-
-
-/**
-*  @brief
-*    Command line action
-*
-*    Specifies an action that can be invoked on the command line.
-*    An action is defined by a set of items (commands, options,
-*    switches, and parameters), which may be present on the command line.
-*    If all non-optional commands, switches, and options are found,
-*    the action is invoked.
-*
-*    Example:
-*      `myapp list <category> [<type>] [--show-details]`
-*
-*    @code
-*      CommandLineAction actionList("list", "List objects");
-*
-*      CommandLineCommand cmdList("list")
-*      actionList.add(&cmdList);
-*
-*      CommandLineParameter paramCategory(
-*          "category",
-*          CommandLineParameter::NonOptional
-*      );
-*      actionList.add(&paramCategory);
-*
-*      CommandLineParameter paramType("type", CommandLineParameter::Optional);
-*      actionList.add(&paramType);
-*
-*      CommandLineSwitch swHelp(
-*          "--show-details", "-d",
-*          "Show detailed object information",
-*          CommandLineSwitch::Optional
-*      );
-*      actionList.add(&swHelp);
-*    @endcode
-*/
-class CPPASSIST_API CommandLineAction
-{
-public:
-    /**
-    *  @brief
-    *    Constructor
-    *
-    *  @param[in] name
-    *    Program name
-    *  @param[in] description
-    *    Description text
-    */
-    CommandLineAction(const std::string & name = "", const std::string & description = "");
-
-    /**
-    *  @brief
-    *    Destructor
-    */
-    virtual ~CommandLineAction();
-
-    /**
-    *  @brief
-    *    Get action name
-    *
-    *  @return
-    *    Action name
-    */
-    const std::string & name() const;
-
-    /**
-    *  @brief
-    *    Set action name
-    *
-    *  @param[in] name
-    *    Action name
-    */
-    void setName(const std::string & name);
-
-    /**
-    *  @brief
-    *    Get description
-    *
-    *  @return
-    *    Description text
-    */
-    const std::string & description() const;
-
-    /**
-    *  @brief
-    *    Set description
-    *
-    *  @param[in] description
-    *    Description text
-    */
-    void setDescription(const std::string & description);
-
-    /**
-    *  @brief
-    *    Get commands
-    *
-    *  @return
-    *    List of commands
-    */
-    const std::vector<CommandLineCommand *> & commands() const;
-
-    /**
-    *  @brief
-    *    Get command by name
-    *
-    *  @param[in] name
-    *    Command name
-    *
-    *  @return
-    *    Pointer to command, null on error
-    */
-    CommandLineCommand * getCommand(const std::string & name) const;
-
-    /**
-    *  @brief
-    *    Add command
-    *
-    *  @param[in] command
-    *    Command line command (must NOT be null!)
-    */
-    void add(CommandLineCommand * command);
-
-    /**
-    *  @brief
-    *    Get options
-    *
-    *  @return
-    *    List of options
-    */
-    const std::vector<CommandLineOption *> & options() const;
-
-    /**
-    *  @brief
-    *    Get option by name
-    *
-    *  @param[in] name
-    *    Option name
-    *
-    *  @return
-    *    Pointer to option, null on error
-    */
-    CommandLineOption * getOption(const std::string & name) const;
-
-    /**
-    *  @brief
-    *    Add option
-    *
-    *  @param[in] option
-    *    Command line option (must NOT be null!)
-    */
-    void add(CommandLineOption * option);
-
-    /**
-    *  @brief
-    *    Get switches
-    *
-    *  @return
-    *    List of switches
-    */
-    const std::vector<CommandLineSwitch *> & switches() const;
-
-    /**
-    *  @brief
-    *    Get switch by name
-    *
-    *  @param[in] name
-    *    Switch name
-    *
-    *  @return
-    *    Pointer to switch, null on error
-    */
-    CommandLineSwitch * getSwitch(const std::string & name) const;
-
-    /**
-    *  @brief
-    *    Add switch
-    *
-    *  @param[in] sw
-    *    Command line switch (must NOT be null!)
-    */
-    void add(CommandLineSwitch * sw);
-
-    /**
-    *  @brief
-    *    Get parameters
-    *
-    *  @return
-    *    List of parameters
-    */
-    const std::vector<CommandLineParameter *> & parameters() const;
-
-    /**
-    *  @brief
-    *    Get parameter by name
-    *
-    *  @param[in] name
-    *    Parameter name
-    *
-    *  @return
-    *    Pointer to parameter, null on error
-    */
-    CommandLineParameter * getParameter(const std::string & name) const;
-
-    /**
-    *  @brief
-    *    Get parameter by index
-    *
-    *  @param[in] index
-    *    Parameter index
-    *
-    *  @return
-    *    Pointer to parameter, null on error
-    */
-    CommandLineParameter * getParameter(size_t index) const;
-
-    /**
-    *  @brief
-    *    Add parameter
-    *
-    *  @param[in] parameter
-    *    Command line parameter (must NOT be null!)
-    */
-    void add(CommandLineParameter * parameter);
-
-    /**
-    *  @brief
-    *    Check if optional parameters are allowed by the action
-    *
-    *  @return
-    *    `true` if optional parameters are allowed, else `false`
-    */
-    bool optionalParametersAllowed() const;
-
-    /**
-    *  @brief
-    *    Set if optional parameters are allowed by the action
-    *
-    *  @param[in] allowed
-    *    `true` if optional parameters are allowed, else `false`
-    */
-    void setOptionalParametersAllowed(bool allowed);
-
-    /**
-    *  @brief
-    *    Get optional parameter name
-    *
-    *  @return
-    *    Parameter name (e.g., "path")
-    */
-    const std::string & optionalParameterName() const;
-
-    /**
-    *  @brief
-    *    Set optional parameter name
-    *
-    *  @param[in] name
-    *    Parameter name (e.g., "path")
-    */
-    void setOptionalParameterName(const std::string & name);
-
-    /**
-    *  @brief
-    *    Get usage text
-    *
-    *  @return
-    *    Usage text
-    */
-    std::string usage() const;
-
-    /**
-    *  @brief
-    *    Reset state (forget all information from previous parsings)
-    */
-    void reset();
-
-    /**
-    *  @brief
-    *    Parse command line
-    *
-    *  @param[in] argc
-    *    Number of arguments
-    *  @param[in] argv
-    *    List of arguments
-    */
-    void parse(int argc, char * argv[]);
-
-    /**
-    *  @brief
-    *    Check if there were any errors during parsing
-    *
-    *  @return
-    *    `true` if errors have been found, else `false`
-    */
-    bool hasErrors() const;
-
-    /**
-    *  @brief
-    *    Get errors
-    *
-    *  @return
-    *    List of parsing errors
-    */
-    const std::vector<std::string> & errors() const;
-
-    /**
-    *  @brief
-    *    Check if action has been activated
-    *
-    *  @return
-    *    `true` if activated, else `false`
-    */
-    bool activated() const;
-
-    /**
-    *  @brief
-    *    Get optional parameters specified on the command line
-    *
-    *  @return
-    *    List of optional parameters
-    */
-    const std::vector<std::string> & optionalParameters() const;
-
-    /**
-    *  @brief
-    *    Execute action
-    *
-    *  @return
-    *    Error code (0 on success)
-    */
-    virtual int execute();
-
-
-protected:
-    /**
-    *  @brief
-    *    Check if this action has been activated
-    *
-    *  @return
-    *    `true` if activated, else `false`
-    */
-    bool checkActivated();
-
-    /**
-    *  @brief
-    *    Checks for errors and collects error messages
-    *
-    *  @see
-    *    errors
-    */
-    void checkErrors();
-
-
-protected:
-    std::string                         m_name;                      ///< Action name
-    std::string                         m_description;               ///< Description text
-    std::vector<CommandLineCommand *>   m_commands;                  ///< List of commands
-    std::vector<CommandLineOption *>    m_options;                   ///< List of options
-    std::vector<CommandLineSwitch *>    m_switches;                  ///< List of switches
-    std::vector<CommandLineParameter *> m_parameters;                ///< List of parameters
-    bool                                m_optionalParametersAllowed; ///< `true` if optional parameters are allowed, else `false`
-    std::string                         m_optionalParameterName;     ///< Name for optional parameters
-    bool                                m_activated;                 ///< `true` if activated, else `false`
-    std::vector<std::string>            m_optionalParameters;        ///< List of optional parameters
-    std::vector<std::string>            m_errors;                    ///< List of parsing errors
-};
-
-
-} // namespace cppassist
diff --git a/source/cppassist/include/cppassist/cmdline/CommandLineProgram.h b/source/cppassist/include/cppassist/cmdline/CommandLineProgram.h
index fd9698d..3fd11fd 100644
--- a/source/cppassist/include/cppassist/cmdline/CommandLineProgram.h
+++ b/source/cppassist/include/cppassist/cmdline/CommandLineProgram.h
@@ -12,7 +12,11 @@ namespace cppassist
 {
 
 
-class CommandLineAction;
+class CommandLineProgram;
+class CommandLineCommand;
+class CommandLineOption;
+class CommandLineSwitch;
+class CommandLineParameter;
 
 
 /**
@@ -108,7 +112,7 @@ class CPPASSIST_API CommandLineProgram
     *  @return
     *    List of actions
     */
-    const std::vector<CommandLineAction *> & actions() const;
+    const std::vector<CommandLineProgram *> & actions() const;
 
     /**
     *  @brief
@@ -120,7 +124,7 @@ class CPPASSIST_API CommandLineProgram
     *  @return
     *    Pointer to action, null on error
     */
-    CommandLineAction * getAction(const std::string & name) const;
+    CommandLineProgram * getAction(const std::string & name) const;
 
     /**
     *  @brief
@@ -129,7 +133,175 @@ class CPPASSIST_API CommandLineProgram
     *  @param[in] action
     *    Command line action (must NOT be null!)
     */
-    void add(CommandLineAction * action);
+    void add(CommandLineProgram * action);
+
+    /**
+    *  @brief
+    *    Get commands
+    *
+    *  @return
+    *    List of commands
+    */
+    const std::vector<CommandLineCommand *> & commands() const;
+
+    /**
+    *  @brief
+    *    Get command by name
+    *
+    *  @param[in] name
+    *    Command name
+    *
+    *  @return
+    *    Pointer to command, null on error
+    */
+    CommandLineCommand * getCommand(const std::string & name) const;
+
+    /**
+    *  @brief
+    *    Add command
+    *
+    *  @param[in] command
+    *    Command line command (must NOT be null!)
+    */
+    void add(CommandLineCommand * command);
+
+    /**
+    *  @brief
+    *    Get options
+    *
+    *  @return
+    *    List of options
+    */
+    const std::vector<CommandLineOption *> & options() const;
+
+    /**
+    *  @brief
+    *    Get option by name
+    *
+    *  @param[in] name
+    *    Option name
+    *
+    *  @return
+    *    Pointer to option, null on error
+    */
+    CommandLineOption * getOption(const std::string & name) const;
+
+    /**
+    *  @brief
+    *    Add option
+    *
+    *  @param[in] option
+    *    Command line option (must NOT be null!)
+    */
+    void add(CommandLineOption * option);
+
+    /**
+    *  @brief
+    *    Get switches
+    *
+    *  @return
+    *    List of switches
+    */
+    const std::vector<CommandLineSwitch *> & switches() const;
+
+    /**
+    *  @brief
+    *    Get switch by name
+    *
+    *  @param[in] name
+    *    Switch name
+    *
+    *  @return
+    *    Pointer to switch, null on error
+    */
+    CommandLineSwitch * getSwitch(const std::string & name) const;
+
+    /**
+    *  @brief
+    *    Add switch
+    *
+    *  @param[in] sw
+    *    Command line switch (must NOT be null!)
+    */
+    void add(CommandLineSwitch * sw);
+
+    /**
+    *  @brief
+    *    Get parameters
+    *
+    *  @return
+    *    List of parameters
+    */
+    const std::vector<CommandLineParameter *> & parameters() const;
+
+    /**
+    *  @brief
+    *    Get parameter by name
+    *
+    *  @param[in] name
+    *    Parameter name
+    *
+    *  @return
+    *    Pointer to parameter, null on error
+    */
+    CommandLineParameter * getParameter(const std::string & name) const;
+
+    /**
+    *  @brief
+    *    Get parameter by index
+    *
+    *  @param[in] index
+    *    Parameter index
+    *
+    *  @return
+    *    Pointer to parameter, null on error
+    */
+    CommandLineParameter * getParameter(size_t index) const;
+
+    /**
+    *  @brief
+    *    Add parameter
+    *
+    *  @param[in] parameter
+    *    Command line parameter (must NOT be null!)
+    */
+    void add(CommandLineParameter * parameter);
+
+    /**
+    *  @brief
+    *    Check if optional parameters are allowed by the action
+    *
+    *  @return
+    *    `true` if optional parameters are allowed, else `false`
+    */
+    bool optionalParametersAllowed() const;
+
+    /**
+    *  @brief
+    *    Set if optional parameters are allowed by the action
+    *
+    *  @param[in] allowed
+    *    `true` if optional parameters are allowed, else `false`
+    */
+    void setOptionalParametersAllowed(bool allowed);
+
+    /**
+    *  @brief
+    *    Get optional parameter name
+    *
+    *  @return
+    *    Parameter name (e.g., "path")
+    */
+    const std::string & optionalParameterName() const;
+
+    /**
+    *  @brief
+    *    Set optional parameter name
+    *
+    *  @param[in] name
+    *    Parameter name (e.g., "path")
+    */
+    void setOptionalParameterName(const std::string & name);
 
     /**
     *  @brief
@@ -148,7 +320,7 @@ class CPPASSIST_API CommandLineProgram
     *    options for that action are given.
     *    You can change this behaviour by overriding this function.
     */
-    virtual std::string help(CommandLineAction * forAction = nullptr) const;
+    virtual std::string help(CommandLineProgram * forAction = nullptr) const;
 
     /**
     *  @brief
@@ -163,6 +335,15 @@ class CPPASSIST_API CommandLineProgram
     */
     virtual void print(const std::string & msg);
 
+    /**
+    *  @brief
+    *    Get usage text
+    *
+    *  @return
+    *    Usage text
+    */
+    std::string usage() const;
+
     /**
     *  @brief
     *    Reset state (forget all information from previous parsings)
@@ -217,7 +398,7 @@ class CPPASSIST_API CommandLineProgram
     *  @return
     *    Error code (0 on success)
     */
-    virtual int executeAction(CommandLineAction * action);
+    virtual int executeAction(CommandLineProgram * action);
 
     /**
     *  @brief
@@ -235,15 +416,92 @@ class CPPASSIST_API CommandLineProgram
     *  @return
     *    Action, can be null
     */
-    CommandLineAction * selectedAction() const;
+    CommandLineProgram * selectedAction() const;
+
+    /**
+    *  @brief
+    *    Get errors
+    *
+    *  @return
+    *    List of parsing errors
+    */
+    const std::vector<std::string> & errors() const;
+
+    /**
+    *  @brief
+    *    Check if action has been activated
+    *
+    *  @return
+    *    `true` if activated, else `false`
+    */
+    bool activated() const;
+
+    /**
+    *  @brief
+    *    Get optional parameters specified on the command line
+    *
+    *  @return
+    *    List of optional parameters
+    */
+    const std::vector<std::string> & unknownArguments() const;
+
+    /**
+    *  @brief
+    *    Execute action
+    *
+    *  @return
+    *    Error code (0 on success)
+    */
+    virtual int execute();
+
+
+protected:
+    /**
+    *  @brief
+    *    Try to parse arguments
+    *
+    *  @param[in] args
+    *    Arguments to parse.
+    *
+    *  @return
+    *    `true` if parsing resulted in no errors, `false` otherwise
+    */
+    bool tryParse(std::vector<std::string> & args);
+
+    /**
+    *  @brief
+    *    Check if this action has been activated
+    *
+    *  @return
+    *    `true` if activated, else `false`
+    */
+    bool checkActivated();
+
+    /**
+    *  @brief
+    *    Checks for errors and collects error messages
+    *
+    *  @see
+    *    errors
+    */
+    void checkErrors();
 
 
 protected:
-    std::string                        m_name;           ///< Program name
-    std::string                        m_shortDesc;      ///< Short description (e.g., version and license)
-    std::string                        m_description;    ///< Description text
-    std::vector<CommandLineAction *>   m_actions;        ///< List of registered actions
-    CommandLineAction                * m_selectedAction; ///< Action that has been selected (can be null)
+    std::string                         m_name;                      ///< Program name
+    std::string                         m_shortDesc;                 ///< Short description (e.g., version and license)
+    std::string                         m_description;               ///< Description text
+    CommandLineProgram                * m_selectedAction;            ///< Action that has been selected (can be null)
+    std::vector<CommandLineProgram *>   m_actions;                   ///< List of registered actions
+    std::vector<CommandLineCommand *>   m_commands;                  ///< List of commands
+    std::vector<CommandLineOption *>    m_options;                   ///< List of options
+    std::vector<CommandLineSwitch *>    m_switches;                  ///< List of switches
+    std::vector<CommandLineParameter *> m_parameters;                ///< List of parameters
+    bool                                m_optionalParametersAllowed; ///< `true` if optional parameters are allowed, else `false`
+    std::string                         m_optionalParameterName;     ///< Name for optional parameters
+    bool                                m_activated;                 ///< `true` if activated, else `false`
+    std::vector<std::string>            m_unknownArgs;               ///< List of optional parameters
+    std::vector<std::string>            m_errors;                    ///< List of parsing errors
 };
 
 
diff --git a/source/cppassist/source/cmdline/CommandLineAction.cpp b/source/cppassist/source/cmdline/CommandLineAction.cpp
deleted file mode 100644
index 29b0505..0000000
--- a/source/cppassist/source/cmdline/CommandLineAction.cpp
+++ /dev/null
@@ -1,440 +0,0 @@
-
-#include <cppassist/cmdline/CommandLineAction.h>
-
-#include <cppassist/string/manipulation.h>
-#include <cppassist/cmdline/CommandLineCommand.h>
-#include <cppassist/cmdline/CommandLineOption.h>
-#include <cppassist/cmdline/CommandLineSwitch.h>
-#include <cppassist/cmdline/CommandLineParameter.h>
-
-
-namespace cppassist
-{
-
-
-CommandLineAction::CommandLineAction(const std::string & name, const std::string & description)
-: m_name(name)
-, m_description(description)
-, m_optionalParametersAllowed(false)
-, m_optionalParameterName("arg")
-, m_activated(false)
-{
-}
-
-CommandLineAction::~CommandLineAction()
-{
-}
-
-const std::string & CommandLineAction::name() const
-{
-    return m_name;
-}
-
-void CommandLineAction::setName(const std::string & name)
-{
-    m_name = name;
-}
-
-const std::string & CommandLineAction::description() const
-{
-    return m_description;
-}
-
-void CommandLineAction::setDescription(const std::string & description)
-{
-    m_description = description;
-}
-
-const std::vector<CommandLineCommand *> & CommandLineAction::commands() const
-{
-    return m_commands;
-}
-
-CommandLineCommand * CommandLineAction::getCommand(const std::string & name) const
-{
-    for (auto * command : m_commands)
-    {
-        if (command->name() == name)
-        {
-            return command;
-        }
-    }
-
-    return nullptr;
-}
-
-void CommandLineAction::add(CommandLineCommand * command)
-{
-    m_commands.push_back(command);
-}
-
-const std::vector<CommandLineOption *> & CommandLineAction::options() const
-{
-    return m_options;
-}
-
-CommandLineOption * CommandLineAction::getOption(const std::string & name) const
-{
-    for (auto * option : m_options)
-    {
-        if (option->shortName() == name || option->longName() == name)
-        {
-            return option;
-        }
-    }
-
-    return nullptr;
-}
-
-void CommandLineAction::add(CommandLineOption * option)
-{
-    m_options.push_back(option);
-}
-
-const std::vector<CommandLineSwitch *> & CommandLineAction::switches() const
-{
-    return m_switches;
-}
-
-CommandLineSwitch * CommandLineAction::getSwitch(const std::string & name) const
-{
-    for (auto * sw : m_switches)
-    {
-        if (sw->shortName() == name || sw->longName() == name)
-        {
-            return sw;
-        }
-    }
-
-    return nullptr;
-}
-
-void CommandLineAction::add(CommandLineSwitch * sw)
-{
-    m_switches.push_back(sw);
-}
-
-const std::vector<CommandLineParameter *> & CommandLineAction::parameters() const
-{
-    return m_parameters;
-}
-
-CommandLineParameter * CommandLineAction::getParameter(const std::string & name) const
-{
-    for (auto * parameter : m_parameters)
-    {
-        if (parameter->name() == name)
-        {
-            return parameter;
-        }
-    }
-
-    return nullptr;
-}
-
-CommandLineParameter * CommandLineAction::getParameter(size_t index) const
-{
-    if (index < m_parameters.size())
-    {
-        return m_parameters[index];
-    }
-
-    return nullptr;
-}
-
-bool CommandLineAction::optionalParametersAllowed() const
-{
-    return m_optionalParametersAllowed;
-}
-
-void CommandLineAction::setOptionalParametersAllowed(bool allowed)
-{
-    m_optionalParametersAllowed = allowed;
-}
-
-const std::string & CommandLineAction::optionalParameterName() const
-{
-    return m_optionalParameterName;
-}
-
-void CommandLineAction::setOptionalParameterName(const std::string & name)
-{
-    m_optionalParameterName = name;
-}
-
-void CommandLineAction::add(CommandLineParameter * parameter)
-{
-    m_parameters.push_back(parameter);
-}
-
-std::string CommandLineAction::usage() const
-{
-    std::string usage;
-
-    for (auto * command : m_commands)
-    {
-        if (usage.size() > 0) usage += " ";
-
-        usage += command->name();
-    }
-
-    for (auto * sw : m_switches)
-    {
-        if (usage.size() > 0) usage += " ";
-        if (sw->isOptional()) usage += "[";
-        usage += sw->name();
-        if (sw->isOptional()) usage += "]";
-    }
-
-    for (auto * option : m_options)
-    {
-        if (usage.size() > 0) usage += " ";
-        if (option->isOptional()) usage += "[";
-        usage += option->name() + " <" + option->valueName() + ">";
-        if (option->isOptional()) usage += "]";
-    }
-
-    for (auto * param : m_parameters)
-    {
-        if (usage.size() > 0) usage += " ";
-        if (param->isOptional()) usage += "[";
-        usage += "<" + param->name() + ">";
-        if (param->isOptional()) usage += "]";
-    }
-
-    if (m_optionalParametersAllowed)
-    {
-        if (usage.size() > 0) usage += " ";
-        usage += "[<" + m_optionalParameterName + ">]*";
-    }
-
-    return usage;
-}
-
-void CommandLineAction::reset()
-{
-    m_activated = false;
-
-    m_optionalParameters.clear();
-    m_errors.clear();
-
-    for (auto * command : m_commands)
-    {
-        command->setActivated(false);
-    }
-
-    for (auto * sw : m_switches)
-    {
-        sw->setActivated(false);
-        sw->setCount(0);
-    }
-
-    for (auto * option : m_options)
-    {
-        option->setValue("");
-    }
-
-    for (auto * parameter : m_parameters)
-    {
-        parameter->setValue("");
-    }
-}
-
-void CommandLineAction::parse(int argc, char * argv[])
-{
-    std::vector<std::string> args;
-
-    // Reset internal state
-    reset();
-
-    // List arguments
-    for (int i=1; i<argc; i++)
-    {
-        std::string arg = argv[i];
-
-        // Expand grouped options (e.g., "-abc" -> "-a -b -c")
-        if (!string::hasPrefix(arg, "--") && string::hasPrefix(arg, "-") && arg.size() > 2)
-        {
-            for (size_t j=0; j<arg.size()-1; j++)
-            {
-                std::string opt = std::string("-") + arg[j+1];
-                args.push_back(opt);
-            }
-        }
-        else
-        {
-            args.push_back(arg);
-        }
-    }
-
-    // Parse command line
-    size_t numParameters = 0;
-    for (size_t i=0; i<args.size(); i++)
-    {
-        // Get current and next argument
-        std::string arg  = args[i];
-        std::string next = (i+1 < args.size() ? args[i+1] : "");
-
-        // If item starts with "--" or "-", it may be an option or a switch
-        if (string::hasPrefix(arg, "-"))
-        {
-            CommandLineOption * option = getOption(arg);
-            CommandLineSwitch * sw     = getSwitch(arg);
-
-            // Option
-            if (option)
-            {
-                // Check that next item is a valid value (and not empty or an option)
-                if (!next.empty() && !string::hasPrefix(next, "-"))
-                {
-                    option->setValue(next);
-                    i++;
-                }
-                else
-                {
-                    // Error: Value expected
-                    m_errors.push_back("Expected value for '" + arg + "'");
-                }
-            }
-
-            // Switch
-            else if (sw)
-            {
-                sw->setActivated(true);
-                sw->setCount(sw->count() + 1);
-            }
-
-            // Error: Unknown option
-            else
-            {
-                m_errors.push_back("Unknown option '" + arg + "'");
-            }
-        }
-
-        // Otherwise, it may be a command or a parameter
-        else
-        {
-            CommandLineCommand * command = getCommand(arg);
-
-            // Command
-            if (command)
-            {
-                command->setActivated(true);
-            }
-
-            // Parameter
-            else
-            {
-                auto * parameter = getParameter(numParameters);
-
-                // Parameter found
-                if (parameter)
-                {
-                    parameter->setValue(arg);
-                    numParameters++;
-                }
-
-                // Unknown parameter
-                else
-                {
-                    m_optionalParameters.push_back(arg);
-                }
-            }
-        }
-    }
-
-    // Check if this action is activated
-    m_activated = checkActivated();
-
-    // Check for errors
-    checkErrors();
-}
-
-bool CommandLineAction::hasErrors() const
-{
-    return m_errors.size() > 0;
-}
-
-const std::vector<std::string> & CommandLineAction::errors() const
-{
-    return m_errors;
-}
-
-bool CommandLineAction::activated() const
-{
-    return m_activated;
-}
-
-const std::vector<std::string> & CommandLineAction::optionalParameters() const
-{
-    return m_optionalParameters;
-}
-
-int CommandLineAction::execute()
-{
-    // To be implement in derived classes
-    return 0;
-}
-
-bool CommandLineAction::checkActivated()
-{
-    // Check if this action has been activated
-    for (auto * command : m_commands)
-    {
-        if (!command->activated())
-        {
-            return false;
-        }
-    }
-
-    for (auto * sw : m_switches)
-    {
-        if (sw->optional() == CommandLineSwitch::NonOptional && !sw->activated())
-        {
-            return false;
-        }
-    }
-
-    for (auto * option : m_options)
-    {
-        if (option->optional() == CommandLineOption::NonOptional && option->value().empty())
-        {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-void CommandLineAction::checkErrors()
-{
-    // Look for options that miss a value
-    for (auto * option : m_options)
-    {
-        if (option->optional() == CommandLineOption::NonOptional && option->value().empty())
-        {
-            m_errors.push_back("Expected value for '" + option->name() + "'");
-        }
-    }
-
-    // Look for parameters that have not been specified
-    for (auto * parameter : m_parameters)
-    {
-        if (parameter->optional() == CommandLineParameter::NonOptional && parameter->value().empty())
-        {
-            m_errors.push_back("Expected <" + parameter->name() + ">");
-        }
-    }
-
-    // Look for unrecognized parameters
-    if (!m_optionalParametersAllowed)
-    {
-        for (auto parameter : m_optionalParameters)
-        {
-            m_errors.push_back("Unknown parameter '" + parameter + "'");
-        }
-    }
-}
-
-
-} // namespace cppassist
diff --git a/source/cppassist/source/cmdline/CommandLineProgram.cpp b/source/cppassist/source/cmdline/CommandLineProgram.cpp
index 4e583ed..7db6ad6 100644
--- a/source/cppassist/source/cmdline/CommandLineProgram.cpp
+++ b/source/cppassist/source/cmdline/CommandLineProgram.cpp
@@ -5,8 +5,10 @@
 
 #include <cppassist/logging/logging.h>
 #include <cppassist/string/manipulation.h>
-#include <cppassist/cmdline/CommandLineAction.h>
+#include <cppassist/cmdline/CommandLineProgram.h>
+#include <cppassist/cmdline/CommandLineCommand.h>
 #include <cppassist/cmdline/CommandLineOption.h>
+#include <cppassist/cmdline/CommandLineParameter.h>
 #include <cppassist/cmdline/CommandLineSwitch.h>
 
 
@@ -19,6 +21,9 @@ CommandLineProgram::CommandLineProgram(const std::string & name, const std::stri
 , m_shortDesc(shortDesc)
 , m_description(description)
 , m_selectedAction(nullptr)
+, m_optionalParametersAllowed(false)
+, m_optionalParameterName("arg")
+, m_activated(false)
 {
 }
 
@@ -48,6 +53,11 @@ void CommandLineProgram::setShortDesc(const std::string & shortDesc)
 
 const std::string & CommandLineProgram::description() const
 {
+    if (m_description.empty())
+    {
+        return shortDesc();
+    }
+
     return m_description;
 }
 
@@ -56,12 +66,12 @@ void CommandLineProgram::setDescription(const std::string & description)
     m_description = description;
 }
 
-const std::vector<CommandLineAction *> & CommandLineProgram::actions() const
+const std::vector<CommandLineProgram *> & CommandLineProgram::actions() const
 {
     return m_actions;
 }
 
-CommandLineAction * CommandLineProgram::getAction(const std::string & name) const
+CommandLineProgram * CommandLineProgram::getAction(const std::string & name) const
 {
     for (auto * action : m_actions)
     {
@@ -74,12 +84,178 @@ CommandLineAction * CommandLineProgram::getAction(const std::string & name) cons
     return nullptr;
 }
 
-void CommandLineProgram::add(CommandLineAction * action)
+void CommandLineProgram::add(CommandLineProgram * action)
 {
     m_actions.push_back(action);
 }
 
-std::string CommandLineProgram::help(CommandLineAction * forAction) const
+const std::vector<CommandLineCommand *> & CommandLineProgram::commands() const
+{
+    return m_commands;
+}
+
+CommandLineCommand * CommandLineProgram::getCommand(const std::string & name) const
+{
+    for (auto * command : m_commands)
+    {
+        if (command->name() == name)
+        {
+            return command;
+        }
+    }
+
+    return nullptr;
+}
+
+void CommandLineProgram::add(CommandLineCommand * command)
+{
+    m_commands.push_back(command);
+}
+
+const std::vector<CommandLineOption *> & CommandLineProgram::options() const
+{
+    return m_options;
+}
+
+CommandLineOption * CommandLineProgram::getOption(const std::string & name) const
+{
+    for (auto * option : m_options)
+    {
+        if (option->shortName() == name || option->longName() == name)
+        {
+            return option;
+        }
+    }
+
+    return nullptr;
+}
+
+void CommandLineProgram::add(CommandLineOption * option)
+{
+    m_options.push_back(option);
+}
+
+const std::vector<CommandLineSwitch *> & CommandLineProgram::switches() const
+{
+    return m_switches;
+}
+
+CommandLineSwitch * CommandLineProgram::getSwitch(const std::string & name) const
+{
+    for (auto * sw : m_switches)
+    {
+        if (sw->shortName() == name || sw->longName() == name)
+        {
+            return sw;
+        }
+    }
+
+    return nullptr;
+}
+
+void CommandLineProgram::add(CommandLineSwitch * sw)
+{
+    m_switches.push_back(sw);
+}
+
+const std::vector<CommandLineParameter *> & CommandLineProgram::parameters() const
+{
+    return m_parameters;
+}
+
+CommandLineParameter * CommandLineProgram::getParameter(const std::string & name) const
+{
+    for (auto * parameter : m_parameters)
+    {
+        if (parameter->name() == name)
+        {
+            return parameter;
+        }
+    }
+
+    return nullptr;
+}
+
+CommandLineParameter * CommandLineProgram::getParameter(size_t index) const
+{
+    if (index < m_parameters.size())
+    {
+        return m_parameters[index];
+    }
+
+    return nullptr;
+}
+
+bool CommandLineProgram::optionalParametersAllowed() const
+{
+    return m_optionalParametersAllowed;
+}
+
+void CommandLineProgram::setOptionalParametersAllowed(bool allowed)
+{
+    m_optionalParametersAllowed = allowed;
+}
+
+const std::string & CommandLineProgram::optionalParameterName() const
+{
+    return m_optionalParameterName;
+}
+
+void CommandLineProgram::setOptionalParameterName(const std::string & name)
+{
+    m_optionalParameterName = name;
+}
+
+void CommandLineProgram::add(CommandLineParameter * parameter)
+{
+    m_parameters.push_back(parameter);
+}
+
+std::string CommandLineProgram::usage() const
+{
+    std::string usage;
+
+    for (auto * command : m_commands)
+    {
+        if (usage.size() > 0) usage += " ";
+
+        usage += command->name();
+    }
+
+    for (auto * sw : m_switches)
+    {
+        if (usage.size() > 0) usage += " ";
+        if (sw->isOptional()) usage += "[";
+        usage += sw->name();
+        if (sw->isOptional()) usage += "]";
+    }
+
+    for (auto * option : m_options)
+    {
+        if (usage.size() > 0) usage += " ";
+        if (option->isOptional()) usage += "[";
+        usage += option->name() + " <" + option->valueName() + ">";
+        if (option->isOptional()) usage += "]";
+    }
+
+    for (auto * param : m_parameters)
+    {
+        if (usage.size() > 0) usage += " ";
+        if (param->isOptional()) usage += "[";
+        usage += "<" + param->name() + ">";
+        if (param->isOptional()) usage += "]";
+    }
+
+    if (m_optionalParametersAllowed)
+    {
+        if (usage.size() > 0) usage += " ";
+        usage += "[<" + m_optionalParameterName + ">]*";
+    }
+
+    return usage;
+}
+
+std::string CommandLineProgram::help(CommandLineProgram * forAction) const
 {
     std::string msg = "";
 
@@ -112,7 +288,7 @@ std::string CommandLineProgram::help(CommandLineAction * forAction) const
     }
 
     // Choose action(s) to display
-    std::vector<CommandLineAction *> actions;
+    std::vector<CommandLineProgram *> actions;
     if (forAction) actions.push_back(forAction);
     else           actions = m_actions;
 
@@ -181,6 +357,32 @@ void CommandLineProgram::reset()
     }
 
     m_selectedAction = nullptr;
+
+    m_activated = false;
+
+    m_unknownArgs.clear();
+    m_errors.clear();
+
+    for (auto * command : m_commands)
+    {
+        command->setActivated(false);
+    }
+
+    for (auto * sw : m_switches)
+    {
+        sw->setActivated(false);
+        sw->setCount(0);
+    }
+
+    for (auto * option : m_options)
+    {
+        option->setValue("");
+    }
+
+    for (auto * parameter : m_parameters)
+    {
+        parameter->setValue("");
+    }
 }
 
 int CommandLineProgram::execute(int argc, char * argv[])
@@ -189,20 +391,20 @@ int CommandLineProgram::execute(int argc, char * argv[])
     parse(argc, argv);
 
     // Check if a valid action has been selected
-    if (selectedAction() && !hasErrors())
+    if (!hasErrors())
     {
         // Execute action
-        return executeAction(selectedAction());
+        return execute();
     }
 
     // Print help
     print(help(selectedAction()));
 
     // Print errors
-    if (hasErrors() && selectedAction())
+    if (hasErrors())
     {
         // Print error message
-        std::string error = selectedAction()->errors()[0];
+        std::string error = errors()[0];
         print("Error: " + error);
 
         // Indicate error condition
@@ -213,7 +415,7 @@ int CommandLineProgram::execute(int argc, char * argv[])
     return 0;
 }
 
-int CommandLineProgram::executeAction(CommandLineAction * action)
+int CommandLineProgram::executeAction(CommandLineProgram * action)
 {
     if (action)
     {
@@ -230,30 +432,253 @@ void CommandLineProgram::parse(int argc, char * argv[])
         m_name = argv[0];
     }
 
+    // List arguments
+    std::vector<std::string> args;
+
+    for (int i=1; i<argc; i++)
+    {
+        std::string arg = argv[i];
+
+        // Expand grouped options (e.g., "-abc" -> "-a -b -c")
+        if (!string::hasPrefix(arg, "--") && string::hasPrefix(arg, "-") && arg.size() > 2)
+        {
+            for (size_t j=0; j<arg.size()-1; j++)
+            {
+                std::string opt = std::string("-") + arg[j+1];
+                args.push_back(opt);
+            }
+        }
+        else
+        {
+            args.push_back(arg);
+        }
+    }
+
+    // Parse command line
+    tryParse(args);
+
+    // Check if this action is activated
+    m_activated = checkActivated();
+}
+
+bool CommandLineProgram::hasErrors() const
+{
+    if (m_selectedAction)
+    {
+        return m_selectedAction->hasErrors();
+    }
+
+    return m_errors.size() > 0;
+}
+
+const std::vector<std::string> & CommandLineProgram::errors() const
+{
+    if (m_selectedAction)
+    {
+        return m_selectedAction->errors();
+    }
+
+    return m_errors;
+}
+
+bool CommandLineProgram::activated() const
+{
+    return m_activated;
+}
+
+const std::vector<std::string> & CommandLineProgram::unknownArguments() const
+{
+    if (m_selectedAction)
+    {
+        return m_selectedAction->unknownArguments();
+    }
+
+    return m_unknownArgs;
+}
+
+int CommandLineProgram::execute()
+{
+    // To be implement in derived classes
+
+    return executeAction(m_selectedAction);
+}
+
+bool CommandLineProgram::tryParse(std::vector<std::string> & args)
+{
     // Reset status
     reset();
 
-    // Find invoked action
-    for (auto * action : m_actions)
+    // Parse command line
+    size_t numParameters = 0;
+    for (size_t i=0; i<args.size(); i++)
     {
-        // Parse command line
-        action->parse(argc, argv);
+        // Get current and next argument
+        std::string arg  = args[i];
+        std::string next = (i+1 < args.size() ? args[i+1] : "");
+
+        // If item starts with "--" or "-", it may be an option or a switch
+        if (string::hasPrefix(arg, "-"))
+        {
+            CommandLineOption * option = getOption(arg);
+            CommandLineSwitch * sw     = getSwitch(arg);
+
+            // Option
+            if (option)
+            {
+                // Check that next item is a valid value (and not empty or an option)
+                if (!next.empty() && !string::hasPrefix(next, "-"))
+                {
+                    option->setValue(next);
+                    i++;
+                }
+                else
+                {
+                    // Error: Value expected
+                    m_errors.push_back("Expected value for '" + arg + "'");
+                }
+            }
+
+            // Switch
+            else if (sw)
+            {
+                sw->setActivated(true);
+                sw->setCount(sw->count() + 1);
+            }
+
+            // Error: Unknown option
+            else
+            {
+                m_unknownArgs.push_back(arg);
+            }
+        }
 
-        // Action found?
-        if (action->activated())
+        // Otherwise, it may be a command or a parameter
+        else
+        {
+            CommandLineCommand * command = getCommand(arg);
+
+            // Command
+            if (command)
+            {
+                command->setActivated(true);
+            }
+
+            // Parameter
+            else
+            {
+                auto * parameter = getParameter(numParameters);
+
+                // Parameter found
+                if (parameter)
+                {
+                    parameter->setValue(arg);
+                    numParameters++;
+                }
+
+                // Unknown parameter
+                else
+                {
+                    m_unknownArgs.push_back(arg);
+                }
+            }
+        }
+    }
+
+    for (auto action : m_actions)
+    {
+        if (action->tryParse(m_unknownArgs))
         {
             m_selectedAction = action;
-            return;
+            break;
         }
     }
+
+    checkErrors();
+
+    m_activated = checkActivated();
+
+    return m_activated;
 }
 
-bool CommandLineProgram::hasErrors() const
+bool CommandLineProgram::checkActivated()
 {
-    return (m_selectedAction && m_selectedAction->hasErrors());
+    // Check if this action has been activated
+    for (auto * command : m_commands)
+    {
+        if (!command->activated())
+        {
+            return false;
+        }
+    }
+
+    for (auto * sw : m_switches)
+    {
+        if (sw->optional() == CommandLineSwitch::NonOptional && !sw->activated())
+        {
+            return false;
+        }
+    }
+
+    for (auto * option : m_options)
+    {
+        if (option->optional() == CommandLineOption::NonOptional && option->value().empty())
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void CommandLineProgram::checkErrors()
+{
+    // Look for options that miss a value
+    for (auto * option : m_options)
+    {
+        if (option->optional() == CommandLineOption::NonOptional && option->value().empty())
+        {
+            m_errors.push_back("Expected value for '" + option->name() + "'");
+        }
+    }
+
+    // Look for parameters that have not been specified
+    for (auto * parameter : m_parameters)
+    {
+        if (parameter->optional() == CommandLineParameter::NonOptional && parameter->value().empty())
+        {
+            m_errors.push_back("Expected <" + parameter->name() + ">");
+        }
+    }
+
+    // Look for commands that have not been activated
+    for (auto * command : m_commands)
+    {
+        if (!command->activated())
+        {
+            m_errors.push_back("Expected '" + command->name() + "'");
+        }
+    }
+
+    // Look for required switches that have not been activated
+    for (auto * cmdSwitch : m_switches)
+    {
+        if (cmdSwitch->optional() == CommandLineSwitch::NonOptional && !cmdSwitch->activated())
+        {
+            m_errors.push_back("Expected '" + cmdSwitch->name() + "'");
+        }
+    }
+
+    // Look for unrecognized parameters
+    if (!m_optionalParametersAllowed)
+    {
+        for (auto parameter : unknownArguments())
+        {
+            m_errors.push_back("Unknown parameter '" + parameter + "'");
+        }
+    }
 }
 
-CommandLineAction * CommandLineProgram::selectedAction() const
+CommandLineProgram * CommandLineProgram::selectedAction() const
 {
     return m_selectedAction;
 }
diff --git a/source/examples/cmdline/ActionCopy.cpp b/source/examples/cmdline/ActionCopy.cpp
index 4b2c635..f43ff20 100644
--- a/source/examples/cmdline/ActionCopy.cpp
+++ b/source/examples/cmdline/ActionCopy.cpp
@@ -10,7 +10,7 @@ using namespace cppassist;
 
 
 ActionCopy::ActionCopy(Program & program)
-: CommandLineAction("cp", "Copy files")
+: CommandLineProgram("cp", "Copy files")
 , m_program(program)
 , m_commandCopy("cp")
 , m_paramSrc("path", CommandLineParameter::NonOptional)
@@ -34,7 +34,7 @@ int ActionCopy::execute()
     info() << "Let me copy that for you ...";
     info() << "- " << m_paramSrc.value();
 
-    for (auto arg : optionalParameters())
+    for (auto arg : unknownArguments())
     {
         info() << "- " << arg;
     }
diff --git a/source/examples/cmdline/ActionCopy.h b/source/examples/cmdline/ActionCopy.h
index a0f5b80..6178df1 100644
--- a/source/examples/cmdline/ActionCopy.h
+++ b/source/examples/cmdline/ActionCopy.h
@@ -2,7 +2,7 @@
 #pragma once
 
 
-#include <cppassist/cmdline/CommandLineAction.h>
+#include <cppassist/cmdline/CommandLineProgram.h>
 #include <cppassist/cmdline/CommandLineCommand.h>
 #include <cppassist/cmdline/CommandLineParameter.h>
 
@@ -14,7 +14,7 @@ class Program;
 *  @brief
 *    Command 'cp'
 */
-class ActionCopy : public cppassist::CommandLineAction
+class ActionCopy : public cppassist::CommandLineProgram
 {
 public:
     /**
@@ -32,7 +32,7 @@ class ActionCopy : public cppassist::CommandLineAction
     */
     ~ActionCopy();
 
-    // Virtual cppassist::CommandLineAction functions
+    // Virtual cppassist::CommandLineProgram functions
     virtual int execute() override;
 
 
diff --git a/source/examples/cmdline/ActionCount.cpp b/source/examples/cmdline/ActionCount.cpp
index 9f37a8b..79026c0 100644
--- a/source/examples/cmdline/ActionCount.cpp
+++ b/source/examples/cmdline/ActionCount.cpp
@@ -10,7 +10,7 @@ using namespace cppassist;
 
 
 ActionCount::ActionCount(Program & program)
-: CommandLineAction("count", "Count from one number to another")
+: CommandLineProgram("count", "Count from one number to another")
 , m_program(program)
 , m_commandCount("count")
 , m_optionStep("--increment-by", "-i", "step", "Number that is added per iteration", CommandLineOption::Optional)
diff --git a/source/examples/cmdline/ActionCount.h b/source/examples/cmdline/ActionCount.h
index e3403a8..329a543 100644
--- a/source/examples/cmdline/ActionCount.h
+++ b/source/examples/cmdline/ActionCount.h
@@ -2,7 +2,7 @@
 #pragma once
 
 
-#include <cppassist/cmdline/CommandLineAction.h>
+#include <cppassist/cmdline/CommandLineProgram.h>
 #include <cppassist/cmdline/CommandLineCommand.h>
 #include <cppassist/cmdline/CommandLineOption.h>
 #include <cppassist/cmdline/CommandLineParameter.h>
@@ -15,7 +15,7 @@ class Program;
 *  @brief
 *    Command 'count'
 */
-class ActionCount : public cppassist::CommandLineAction
+class ActionCount : public cppassist::CommandLineProgram
 {
 public:
     /**
@@ -33,7 +33,7 @@ class ActionCount : public cppassist::CommandLineAction
     */
     ~ActionCount();
 
-    // Virtual cppassist::CommandLineAction functions
+    // Virtual cppassist::CommandLineProgram functions
     virtual int execute() override;
 
 
diff --git a/source/examples/cmdline/ActionHelp.cpp b/source/examples/cmdline/ActionHelp.cpp
index 380088f..eaba814 100644
--- a/source/examples/cmdline/ActionHelp.cpp
+++ b/source/examples/cmdline/ActionHelp.cpp
@@ -10,7 +10,7 @@ using namespace cppassist;
 
 
 ActionHelp::ActionHelp(Program & program)
-: CommandLineAction("help", "Print help text")
+: CommandLineProgram("help", "Print help text")
 , m_program(program)
 , m_switchHelp("--help", "-h", "Print help text", CommandLineSwitch::NonOptional)
 , m_paramCommand("command", CommandLineParameter::Optional)
@@ -27,7 +27,7 @@ ActionHelp::~ActionHelp()
 
 int ActionHelp::execute()
 {
-    CommandLineAction * forAction = nullptr;
+    CommandLineProgram * forAction = nullptr;
 
     if (!m_paramCommand.value().empty())
     {
diff --git a/source/examples/cmdline/ActionHelp.h b/source/examples/cmdline/ActionHelp.h
index b87f46e..8b0e9f7 100644
--- a/source/examples/cmdline/ActionHelp.h
+++ b/source/examples/cmdline/ActionHelp.h
@@ -2,7 +2,7 @@
 #pragma once
 
 
-#include <cppassist/cmdline/CommandLineAction.h>
+#include <cppassist/cmdline/CommandLineProgram.h>
 #include <cppassist/cmdline/CommandLineSwitch.h>
 #include <cppassist/cmdline/CommandLineParameter.h>
 
@@ -14,7 +14,7 @@ class Program;
 *  @brief
 *    Command 'help'
 */
-class ActionHelp : public cppassist::CommandLineAction
+class ActionHelp : public cppassist::CommandLineProgram
 {
 public:
     /**
@@ -32,7 +32,7 @@ class ActionHelp : public cppassist::CommandLineAction
     */
     ~ActionHelp();
 
-    // Virtual cppassist::CommandLineAction functions
+    // Virtual cppassist::CommandLineProgram functions
     virtual int execute() override;
 
 
diff --git a/source/examples/cmdline/Program.cpp b/source/examples/cmdline/Program.cpp
index ad65501..148b286 100644
--- a/source/examples/cmdline/Program.cpp
+++ b/source/examples/cmdline/Program.cpp
@@ -28,12 +28,12 @@ Program::~Program()
 {
 }
 
-void Program::addDefaultOptionsTo(CommandLineAction & action)
+void Program::addDefaultOptionsTo(CommandLineProgram & action)
 {
     action.add(&m_switchVerbose);
 }
 
-int Program::executeAction(CommandLineAction * action)
+int Program::executeAction(CommandLineProgram * action)
 {
     // Set log level
     int logLevel = LogMessage::Info;
@@ -54,5 +54,7 @@ int Program::executeAction(CommandLineAction * action)
         return action->execute();
     }
 
+    print(help());
+
     return 0;
 }
diff --git a/source/examples/cmdline/Program.h b/source/examples/cmdline/Program.h
index c64f3b5..be7209e 100644
--- a/source/examples/cmdline/Program.h
+++ b/source/examples/cmdline/Program.h
@@ -36,10 +36,10 @@ class Program : public cppassist::CommandLineProgram
     *  @param[in] action
     *    Command line action
     */
-    void addDefaultOptionsTo(cppassist::CommandLineAction & action);
+    void addDefaultOptionsTo(cppassist::CommandLineProgram & action);
 
     // Virtual cppassist::CommandLineProgram functions
-    virtual int executeAction(cppassist::CommandLineAction * action) override;
+    virtual int executeAction(cppassist::CommandLineProgram * action) override;
 
 
 public:
diff --git a/source/examples/cmdline2/main.cpp b/source/examples/cmdline2/main.cpp
index 6b7e596..fc0fa3d 100644
--- a/source/examples/cmdline2/main.cpp
+++ b/source/examples/cmdline2/main.cpp
@@ -4,7 +4,6 @@
 #include <cppassist/logging/logging.h>
 #include <cppassist/cmdline/ArgumentParser.h>
 #include <cppassist/cmdline/CommandLineProgram.h>
-#include <cppassist/cmdline/CommandLineAction.h>
 #include <cppassist/cmdline/CommandLineCommand.h>
 #include <cppassist/cmdline/CommandLineOption.h>
 #include <cppassist/cmdline/CommandLineSwitch.h>
@@ -27,7 +26,7 @@ int main(int argc, char * argv[])
         CommandLineSwitch swVerbose("--verbose", "-v", "Make output more verbose");
 
     // Action: 'help'
-    CommandLineAction actionHelp("help", "Print help text");
+    CommandLineProgram actionHelp("help", "Print help text");
     program.add(&actionHelp);
 
         actionHelp.add(&swVerbose);
@@ -39,7 +38,7 @@ int main(int argc, char * argv[])
         actionHelp.add(&paramCommand);
 
     // Action: 'count'
-    CommandLineAction actionCount("count", "Count from one number to another");
+    CommandLineProgram actionCount("count", "Count from one number to another");
     program.add(&actionCount);
 
         actionCount.add(&swVerbose);
@@ -57,7 +56,7 @@ int main(int argc, char * argv[])
         actionCount.add(&paramTo);
 
     // Action: 'cp'
-    CommandLineAction actionCopy("cp", "Copy files");
+    CommandLineProgram actionCopy("cp", "Copy files");
     program.add(&actionCopy);
 
         actionCopy.add(&swVerbose);
@@ -83,7 +82,7 @@ int main(int argc, char * argv[])
         // Execute 'help'
         if (program.selectedAction() == &actionHelp)
         {
-            CommandLineAction * forAction = nullptr;
+            CommandLineProgram * forAction = nullptr;
 
             if (!paramCommand.value().empty())
             {
@@ -105,7 +104,7 @@ int main(int argc, char * argv[])
             info() << "Let me copy that for you ...";
             info() << "- " << actionCopy.getParameter("path")->value();
 
-            for (auto arg : actionCopy.optionalParameters())
+            for (auto arg : actionCopy.unknownArguments())
             {
                 info() << "- " << arg;
             }
@@ -119,10 +118,10 @@ int main(int argc, char * argv[])
     program.print(program.help(program.selectedAction()));
 
     // Print errors
-    if (program.hasErrors() && program.selectedAction())
+    if (program.hasErrors())
     {
         // Print error message
-        std::string error = program.selectedAction()->errors()[0];
+        std::string error = program.errors()[0];
         program.print("Error: " + error);
 
         return 1;