///////////////////////////////////////////////////////////////////////////
// senf::console::Executor
-prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & command,
+prefix_ void senf::console::Executor::operator()(ParseCommandInfo const & command,
std::ostream & output)
{
SENF_LOG(( "Executing: " << command ));
try {
switch(command.builtin()) {
case ParseCommandInfo::NoBuiltin :
- traverseToCommand(command.commandPath())(output, command.arguments());
+ traverseCommand(command.commandPath())(output, command.arguments());
break;
case ParseCommandInfo::BuiltinCD :
}
else {
oldCwd_ = cwd_;
- cwd_ = traverseTo(command.arguments().begin()[0]).thisptr();
+ cwd_ = traverseDirectory(command.arguments().begin()[0]).thisptr();
}
}
break;
case ParseCommandInfo::BuiltinLS : {
- DirectoryNode const & dir (
- command.arguments().empty() ? cwd() : traverseTo(command.arguments().begin()[0]));
+ DirectoryNode const & dir ( command.arguments()
+ ? traverseDirectory(command.arguments().begin()[0])
+ : cwd() );
for (DirectoryNode::child_iterator i (dir.children().begin());
i != dir.children().end(); ++i) {
output << i->first;
case ParseCommandInfo::BuiltinPUSHD :
dirstack_.push_back(cwd_);
if ( command.arguments() )
- cwd_ = traverseTo(command.arguments().begin()[0]).thisptr();
+ cwd_ = traverseDirectory(command.arguments().begin()[0]).thisptr();
break;
case ParseCommandInfo::BuiltinPOPD :
throw ExitException();
case ParseCommandInfo::BuiltinHELP :
- try {
- GenericNode & node (
- command.arguments()
- ? cwd().traverse(
- boost::make_iterator_range(
- boost::make_transform_iterator(command.arguments().begin()[0].begin(), TraverseTokens()),
- boost::make_transform_iterator(command.arguments().begin()[0].end(), TraverseTokens())))
- : cwd() );
- node.help(output);
- output << std::flush;
- }
- catch (UnknownNodeNameException &) {
- output << "invalid path" << std::endl;
- }
+ GenericNode const & node (command.arguments()
+ ? traverseNode(command.arguments().begin()[0])
+ : cwd());
+ output << prettyName(typeid(node)) << " at " << node.path() << "\n\n";
+ node.help(output);
+ output << std::flush;
break;
+
}
}
+ catch (InvalidPathException &) {
+ output << "invalid path" << std::endl;
+ }
catch (InvalidDirectoryException &) {
output << "invalid directory" << std::endl;
}
catch (InvalidCommandException &) {
output << "invalid command" << std::endl;
}
- return true;
+}
+
+prefix_ senf::console::GenericNode &
+senf::console::Executor::traverseNode(ParseCommandInfo::argument_value_type const & path)
+{
+ try {
+ return cwd().traverse(
+ boost::make_iterator_range(
+ boost::make_transform_iterator(path.begin(), TraverseTokens()),
+ boost::make_transform_iterator(path.end(), TraverseTokens())));
+ }
+ catch (std::bad_cast &) {
+ throw InvalidPathException();
+ }
+ catch (UnknownNodeNameException &) {
+ throw InvalidPathException();
+ }
}
prefix_ senf::console::DirectoryNode &
-senf::console::Executor::traverseTo (ParseCommandInfo::argument_value_type const & path)
+senf::console::Executor::traverseDirectory(ParseCommandInfo::argument_value_type const & path)
{
try {
- return dynamic_cast<DirectoryNode&>(
- cwd().traverse(
- boost::make_iterator_range(
- boost::make_transform_iterator(path.begin(), TraverseTokens()),
- boost::make_transform_iterator(path.end(), TraverseTokens()))));
+ return dynamic_cast<DirectoryNode&>( traverseNode(path) );
}
catch (std::bad_cast &) {
throw InvalidDirectoryException();
}
- catch (UnknownNodeNameException &) {
+ catch (InvalidPathException &) {
throw InvalidDirectoryException();
}
}
prefix_ senf::console::CommandNode &
-senf::console::Executor::traverseToCommand(ParseCommandInfo::CommandPathRange const & path)
+senf::console::Executor::traverseCommand(ParseCommandInfo::CommandPathRange const & path)
{
try {
return dynamic_cast<CommandNode &>( cwd().traverse(path) );
namespace senf {
namespace console {
- /** \brief
+ /** \brief Execute config/console commands
+
+ The Executor interprets parsed config/console commands and executes them. It manages the
+ current execution context (current directory, directory stack and so on).
+
+ The executor is normally called directly by the parser callback for each command.
+
+ Executing the built-in 'exit' command will throw Executor::ExitException. This exception
+ (which is not derived from std::exception since it's not a real exception) must be handled
+ by the caller.
+
+ All directories are managed using weak pointers. If any of the directories expires (current
+ directory, directory stack, last directory) it will be replaced with the root
+ directory. Directories expire when they are destructed or when they are detached from the
+ config tree root node.
*/
class Executor
: boost::noncopyable
typedef boost::iterator_range< ParseCommandInfo::argument_iterator> Arguments;
- struct ExitException {}; // NOT derived from std::exception !
+ struct ExitException {}; ///< Thrown by built-in 'exit' command
///////////////////////////////////////////////////////////////////////////
//\/name Structors and default members
///\}
///////////////////////////////////////////////////////////////////////////
- bool operator()(ParseCommandInfo const & command, std::ostream & output);
- DirectoryNode & cwd() const;
+ void operator()(ParseCommandInfo const & command, std::ostream & output);
+ ///< Execute command
+ /**< Output will be written to \a output. */
+ DirectoryNode & cwd() const; ///< Current working directory
protected:
private:
- DirectoryNode & traverseTo(ParseCommandInfo::argument_value_type const & path);
- CommandNode & traverseToCommand(ParseCommandInfo::CommandPathRange const & path);
+ GenericNode & traverseNode(ParseCommandInfo::argument_value_type const & path);
+ DirectoryNode & traverseDirectory(ParseCommandInfo::argument_value_type const & path);
+ CommandNode & traverseCommand(ParseCommandInfo::CommandPathRange const & path);
+ struct InvalidPathException {};
struct InvalidDirectoryException {};
struct InvalidCommandException {};
throw UnknownNodeNameException() << ": '" << name << "'";
GenericNode::ptr node (i->second);
children_.erase(i);
+ node->parent_ = 0;
return node;
}
prefix_ void senf::console::DirectoryNode::add(GenericNode::ptr node)
{
BOOST_ASSERT( ! node->parent() );
+ if (node->name().empty()) {
+ node->name("unnamed");
+ SENF_LOG((senf::log::MESSAGE)("Adding 'unnamed' node"));
+ }
if (children_.find(node->name()) != children_.end()) {
unsigned suffix (0);
std::string newName;
do {
++suffix;
- newName = node->name() + boost::lexical_cast<std::string>(suffix);
+ newName = node->name() + "-" + boost::lexical_cast<std::string>(suffix);
} while (children_.find(newName) != children_.end());
- name(*node, newName);
+ SENF_LOG((senf::log::MESSAGE)("Uniquifying node '" << node->name() << "' to '"
+ << newName << "'"));
+ node->name(newName);
}
children_.insert(std::make_pair(node->name(),node));
node->parent_ = this;
name_ = name;
}
-prefix_ void senf::console::GenericNode::name(GenericNode & node, std::string const & name)
-{
- node.name_ = name;
-}
-
prefix_ boost::shared_ptr<senf::console::DirectoryNode> senf::console::GenericNode::parent()
const
{
prefix_ senf::console::GenericNode::ptr senf::console::GenericNode::unlink()
{
- SENF_ASSERT( parent() );
- return parent()->remove(name());
+ if (parent_)
+ return parent()->remove(name());
+ else
+ return thisptr();
}
prefix_ void senf::console::GenericNode::help(std::ostream & output)
///////////////////////////////////////////////////////////////////////////
// senf::console::DirectoryNode
-prefix_ std::auto_ptr<senf::console::DirectoryNode>
-senf::console::DirectoryNode::create()
+prefix_ senf::console::DirectoryNode::ptr senf::console::DirectoryNode::create()
{
- return std::auto_ptr<DirectoryNode>(new DirectoryNode());
+ return ptr(new DirectoryNode());
}
prefix_ senf::console::DirectoryNode &
prefix_ senf::console::DirectoryNode &
senf::console::DirectoryNode::mkdir(std::string const & name)
{
- std::auto_ptr<DirectoryNode> node (create());
- return add(name, node);
+ return add(name, create());
}
prefix_ senf::console::DirectoryNode::ChildrenRange senf::console::DirectoryNode::children()
: fn_ (fn)
{}
-prefix_ std::auto_ptr<senf::console::SimpleCommandNode>
+prefix_ senf::console::SimpleCommandNode::ptr
senf::console::SimpleCommandNode::create(Function const & fn)
{
- return std::auto_ptr<SimpleCommandNode>(new SimpleCommandNode(fn));
+ return ptr(new SimpleCommandNode(fn));
}
prefix_ senf::console::SimpleCommandNode &
template <class NodeType>
prefix_ NodeType & senf::console::DirectoryNode::add(std::string const & name,
- std::auto_ptr<NodeType> node)
-{
- GenericNode::ptr p (node);
- p->name(name);
- add(p);
- return static_cast<NodeType &>(*p);
-}
-
-template <class NodeType>
-prefix_ NodeType & senf::console::DirectoryNode::add(std::string const & name,
boost::shared_ptr<NodeType> node)
{
SENF_ASSERT( ! node->parent() );
#include <boost/type_traits/remove_reference.hpp>
#include "../Utils/Exception.hh"
#include "../Utils/mpl.hh"
+#include "../Utils/Logger/SenfLog.hh"
#include "Parse.hh"
//#include "Node.mpp"
class DirectoryNode;
class CommandNode;
- /** \brief
+ /** \brief Config/console node tree base-class
+
+ GenericNode is the base class of all node objects. There are two basic node types derived
+ from GenericNode: DirectoryNode and CommandNode.
+
+ All nodes are dynamically allocated and automatically managed using reference counting.
+
+ All nodes are normally linked into a single tree which root node is
+ senf::console::root(). Nodes may however be orphaned (not linked to the tree) either
+ directly (the node has no parent) or indirectly (the node has a parent but is part of an
+ orphaned subtree which is not linked to the root node).
+
+ Every active (non-orphaned) node (except the root() node) has a non-empty node name. This
+ name is assigned to the node when adding the node to the tree.
*/
class GenericNode
: public boost::enable_shared_from_this<GenericNode>
{
+ SENF_LOG_CLASS_AREA();
public:
///////////////////////////////////////////////////////////////////////////
// Types
virtual ~GenericNode();
- std::string const & name() const;
- boost::shared_ptr<DirectoryNode> parent() const;
+ std::string const & name() const; ///< Node name
+ boost::shared_ptr<DirectoryNode> parent() const; ///< Parent node
+ /**< May be null, if the node is the root node or if it is
+ not linked to the tree */
- std::string path() const;
+ std::string path() const; ///< Node path
+ /**< The node path is built by joining the names of all
+ parent nodes with '/' chars. */
- ptr unlink();
+ ptr unlink(); ///< Remove node from it's parent directory
+ /**< You may either discard the return value and thereby
+ dispose the node or may re-attach the node at some
+ other place using DirectoryNode::add(). */
- bool active() const;
+ bool active() const; ///< \c true, if node is attached to the root() node
- void help(std::ostream & output) const;
+ void help(std::ostream & output) const; /// Write help info to \a output
- ptr thisptr();
- cptr thisptr() const;
+ ptr thisptr(); ///< Get smart pointer to node
+ cptr thisptr() const; ///< Get smart pointer to node (const)
protected:
GenericNode();
void name(std::string const & name);
- static void name(GenericNode & node, std::string const & name);
- void parent(DirectoryNode * parent);
+#ifndef DOXYGEN
private:
+#else
+ public:
+#endif
virtual void v_help(std::ostream & output) const = 0;
+ ///< Provide help information
+ /**< This member must be implemented in derived classes
+ to provide node specific help information. */
+ private:
std::string name_;
DirectoryNode * parent_;
- friend class intrusive_refcount_base;
friend class DirectoryNode;
};
class SimpleCommandNode;
+ /** \brief Internal: Node creation helper traits
+
+ This class is used internally to find out the type of node to create for a specific argument
+ type.
+ */
template <class Object>
struct NodeCreateTraits
{
};
};
- /** \brief
- ///\fixme Provide a default name for added nodes if 'name' is empty ?
+ /** \brief Config/console tree directory node
+
+ This node type provides the internal and root nodes of the tree. It allows to add arbitrary
+ children and supports directory traversal.
+
+ Nodes are normally not instantiated manually but are created by the DirectoryNode via
+ mkdir() or add(). Special add() members however allow externally allocated node objects.
+
+ Nodes may be added to the tree only once, otherwise chaos will ensue. Since nodes are always
+ managed dynamically, there is a special ObjectDirectory proxy template which provides a
+ DirectoryNode facade. ObjectDirectory is used if a class wants to manage it's own directory
+ as a data member.
+
+ Every node is assigned a (new) name when it is added to a directory. If the directory
+ already has an entry of that name, the name is made unique by appending a suffix of the form
+ '-n' where n is a number starting at 1. If the name is empty, int is set to 'unnamed' and
+ then uniquified as above. Automatically providing unique names simplifies adding
+ configuration/console support to generic components.
*/
class DirectoryNode : public GenericNode
{
+ SENF_LOG_CLASS_AREA();
typedef std::map<std::string, GenericNode::ptr> ChildMap;
-
public:
///////////////////////////////////////////////////////////////////////////
// Types
///\name Structors and default members
///\{
- static std::auto_ptr<DirectoryNode> create();
+ static ptr create(); ///< Create node object.
+ /**< You should normally use either mkdir() or
+ ObjectDirectory instead of create() */
///\}
///////////////////////////////////////////////////////////////////////////
///\{
template <class NodeType>
- NodeType & add(std::string const & name, std::auto_ptr<NodeType> node);
-
- template <class NodeType>
NodeType & add(std::string const & name, boost::shared_ptr<NodeType> node);
+ ///< Add node to tree
+ /**< Adds the \a node to the tree as a child of \a this
+ node. The node is given the name \a name. If a node of
+ that name already exists, a numeric suffix of the form
+ '-n' is added to the name until the name is unique. If
+ \a name is empty, it is set to 'unnamed'. */
template <class Object>
typename NodeCreateTraits<Object>::NodeType & add (std::string const & name,
Object const & ob);
+ ///< Generic child node factory
+ /**< This member is used to create a new child node of the
+ current directory. The type of node created depends on
+ the type of argument passed.
+
+ The node type is selected by the NodeCreateTraits
+ class. To allow adding a specific node type, you need
+ to provide an overload for
+ <tt>senf_console_add_node</tt> which must be visible at
+ when you register the new node.
+ \code
+ MyNodeType & senf_console_add_node(
+ DirectoryNode & dir,
+ std::string const & name,
+ MySpecialObject const & ob,
+ int)
+ {
+ return dir.add(name, MyNodeType::create(ob));
+ }
+ \endcode
+ (Do not forget the last unnamed 'int' parameter which
+ is not used but serves to disambiguate the
+ overloads). */
GenericNode::ptr remove(std::string const & name);
+ ///< Remove node \a name from the tree
+ /**< The returned pointer may either be discarded, which
+ will automatically dispose the removed node, or it may
+ be saved and/or re-attached at some other place in the
+ tree. */
DirectoryNode & operator[](std::string const & name) const;
+ ///< Get directory child node
+ /**< \throws UnknownNodeNameException if a child \a name
+ does not exist.
+ \throws std::bad_cast if the child \a name is not a
+ directory node. */
+
CommandNode & operator()(std::string const & name) const;
+ ///< Get command child node
+ /**< \throws UnknownNodeNameException if a child \a name
+ does not exist
+ \throws std::bad_cast if the child \a name is not a
+ command node. */
+
GenericNode & get(std::string const & name) const;
+ ///< Get child node
+ /**< \throws UnknownNodeNameException if a child \a name
+ does not exist */
DirectoryNode & mkdir(std::string const & name);
+ ///< Create sub-directory node
ChildrenRange children() const;
+ ///< Return iterator range over all children.
+ /**< The returned range is sorted by child name. */
///\}
///////////////////////////////////////////////////////////////////////////
template <class ForwardRange>
GenericNode & traverse(ForwardRange const & range);
+ ///< Traverse node path starting at this node
+ /**< The <tt>FordwareRange::value_type</tt> must be
+ (convertible to) std::string. Each range element
+ constitutes a step along the node traversal.
+
+ If the range starts with an empty element, the
+ traversal is started at the root() node, otherwise it
+ is started at \a this node. The traversal supports '.',
+ '..' and ignores further empty elements. */
DirectoryNode & doc(std::string const & doc);
+ ///< Set node documentation
ptr thisptr();
cptr thisptr() const;
BOOST_TYPEOF_REGISTER_TYPE(DirectoryNode);
+ /// Exception: Unknown node name
struct UnknownNodeNameException : public senf::Exception
{ UnknownNodeNameException() : senf::Exception("Unknown node name") {}};
- // We need this specialization since we cannot passe auto_ptr via const & !!
- template <class Type>
- struct NodeCreateTraits< std::auto_ptr<Type> >
- {};
-
+#ifndef DOXYGEN
template <class Type>
struct NodeCreateTraits< boost::shared_ptr<Type> >
{};
+#endif
+
+ /** \brief Config/console tree command node
+
+ The CommandNode is the base-class for the tree leaf nodes. Concrete command node
+ implementations are derived from this class.
- /** \brief
+ To execute a command, CommandNode::operator()() is called. This abstract virtual function
+ must be implemented in a derived class.
*/
class CommandNode : public GenericNode
{
+ SENF_LOG_CLASS_AREA();
public:
///////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////
virtual void operator()(std::ostream & output, Arguments const & arguments) = 0;
+ ///< Called to execute the command
+ /**< \param[in] output stream where result messages may be
+ written to
+ \param[in] arguments command arguments. This is a
+ range of ranges of ArgumentToken instances. */
ptr thisptr();
cptr thisptr() const;
private:
};
- /** \brief
+ /** \brief Most simple CommandNode implementation
+
+ This CommandNode implementation simply forwards the \a output and \a arguments arguments to
+ an arbitrary callback.
*/
class SimpleCommandNode : public CommandNode
{
+ SENF_LOG_CLASS_AREA();
public:
///////////////////////////////////////////////////////////////////////////
// Types
+ typedef boost::shared_ptr<SimpleCommandNode> ptr;
+ typedef boost::shared_ptr<SimpleCommandNode const> cptr;
+ typedef boost::weak_ptr<SimpleCommandNode> weak_ptr;
+
typedef boost::function<void (std::ostream &, Arguments const &)> Function;
///////////////////////////////////////////////////////////////////////////
ptr thisptr();
cptr thisptr() const;
- static std::auto_ptr<SimpleCommandNode> create(Function const & fn);
+ static ptr create(Function const & fn);
SimpleCommandNode & doc(std::string const & doc);
std::string doc_;
};
+#ifndef DOXYGEN
template <class Function>
SimpleCommandNode & senf_console_add_node(DirectoryNode & node, std::string const & name,
Function const & fn, ...);
+#endif
BOOST_TYPEOF_REGISTER_TYPE(SimpleCommandNode);
template <class Owner>
prefix_ senf::console::ObjectDirectory<Owner>::ObjectDirectory(Owner * owner)
- : node_ (DirectoryNode::create().release()), owner_ (owner)
+ : node_ (DirectoryNode::create()), owner_ (owner)
{}
template <class Owner>
namespace senf {
namespace console {
+ /** \brief Internal: Node creation helper traits (ObjectDirectory proxy)
+
+ This class is used like NodeCreateTraits to customize the child node creation. This trait
+ class is used however by the ObjectDirectory proxy.
+ */
template <class Owner, class Object>
struct OwnerNodeCreateTraits
{
};
};
+ /** \brief Internal: Marker base class for all ObjectDirectory proxies
+ */
struct ObjectDirectoryBase {};
- /** \brief
+ /** \brief DirectoryNode member proxy
+
+ ObjectDirectory is used whenever a class wants to manage it's own directory. The class
+ allows to declare the directory as a public member object. This allows the user of the class
+ to register the directory in the command tree. By using the proxy, the node is automatically
+ detached from the tree (and thereby destroyed) when the object (and thereby the proxy) is
+ destroyed.
+
+ \code
+ class MyClass
+ {
+ public:
+ ObjectDirectory<MyClass> configDir;
+
+ MyClass() : configDir(this)
+ {
+ configDIr.add(...);
+ }
+ };
+ \endcode
+
+ The ObjectDirectory proxy implements 'add()' to add new children to the proxied
+ DirectoryNode. All add() variants supported by DirectoryNode are supported by
+ ObjectDirectory.
+
+ \idea This proxy could be made obsolete by allowing to allocate node objects
+ statically. This could be achieved by moving back to an intrusive_ptr implementation for
+ normal pointing needs with an added twist: Give each node a smart_ptr member pointing to
+ itself with a null deleter. This allows to create weak_ptr's to the nodes which will
+ automatically expire when the node is deleted (either statically or by the
+ intrusive_ptr).
*/
template <class Owner>
class ObjectDirectory : public ObjectDirectoryBase
template <class Object>
typename OwnerNodeCreateTraits<Owner, Object>::NodeType & add(std::string const & name,
Object const & ob);
+ ///< Create new child node
+ /**< Adds a new child node to the (proxied)
+ DirectoryNode. How the node is added is configured
+ using the OwnerNodeCreateTraits template. The default
+ implementation just forwards the call to the proxied
+ directory node. */
- DirectoryNode & node() const;
+ DirectoryNode & node() const; ///< Access the proxied DirectoryNode
protected:
private:
- static SimpleCommandNode & create(DirectoryNode & node, Owner * owner,
- std::string const & name,
- SimpleCommandNode::Function const & fn);
-
DirectoryNode::ptr node_;
Owner * owner_;
};
+#ifndef DOXYGEN
template <class Owner, class Function>
SimpleCommandNode & senf_console_add_node(
DirectoryNode & node, Owner & owner, std::string const & name, Function const & fn);
DirectoryNode & senf_console_add_node(
DirectoryNode & dir, std::string const & name, Node const & node, int,
typename boost::enable_if< boost::is_convertible<Node*, ObjectDirectoryBase*> >::type * = 0);
+#endif
+
}}
///////////////////////////////hh.e////////////////////////////////////////
namespace detail { struct ParserAccess; }
- /** \brief
+ /** \brief Single argument token
+
+ All command arguments are split into tokens by the parser. Each token is returned as an
+ ArgumentToken instance.
*/
class ArgumentToken
{
public:
- std::string const & value() const;
+ std::string const & value() const; ///< String value of token
+ /**< This value is properly unquoted */
protected:
friend class detail::ParserAccess;
};
+ /** \brief Console command
- /** \brief
+ Every command parsed is returned in a ParseCommandInfo instance. This information is purely
+ taken from the parser, no semantic information is attached at this point, the config/console
+ is not involved in any why. ParseCommandInfo consist of
+
+ \li the type of command: builtin or normal command represented by a possibly relative path
+ into the command tree.
+ \li the command
+ \li the arguments. Every argument consists of a range of ArgumentToken instances.
*/
class ParseCommandInfo
{
BuiltinEXIT,
BuiltinHELP };
- BuiltinCommand builtin() const;
- CommandPathRange commandPath() const;
- ArgumentsRange arguments() const;
- TokensRange tokens() const;
-
+ BuiltinCommand builtin() const; ///< Command type
+ /**< \returns \c NoBuiltin, if the command is an ordinary
+ command, otherwise the id of the builtin command */
+ CommandPathRange commandPath() const; ///< Command path
+ /**< This is the path to the command if it is not a builtin
+ command. Every element of the returned range
+ constitutes one path element. If the first element is
+ empty, the path is an absolute path, otherwise it is
+ relative. If the last element is an empty string, the
+ path ends in a '/' char. */
+ ArgumentsRange arguments() const; ///< Command arguments
+ /**< The returned range contains one token range for each
+ agument. */
+ TokensRange tokens() const; ///< All argument tokens
+ /**< The returned range contains \e all argument tokens in a
+ single range not divided into separate arguments. */
protected:
private:
friend class detail::ParserAccess;
};
+ /**< \brief Output ParseCommandInfo instance
+ \related ParseCommandInfo
+ */
std::ostream & operator<<(std::ostream & stream, ParseCommandInfo const & info);
- /** \brief
+ /** \brief Parse commands
+
+ This class implements a parser for the console/config language. It supports parsing strings
+ as well as files. For every parsed command, a callback function is called.
+
+ \implementation The implementation is based on Boost.Spirit. See the file \ref Parse.ih for
+ the formal language grammar.
+
+ \implementation Parsing an arbitrary iostream is not supported since arbitrary streams are
+ not seekable. If this is needed, it can however be provided using stream iterators and
+ some special iterator adaptors from Boost.Spirit. However, the amount of backtracking
+ needs to be analyzed before this is viable.
+
+ \todo Implement more detailed error reporting and error recovery.
*/
class CommandParser
: boost::noncopyable
///@}
///////////////////////////////////////////////////////////////////////////
- bool parse(std::string command, Callback cb);
- bool parseFile(std::string filename, Callback cb);
+ bool parse(std::string command, Callback cb); ///< Parse string
+ bool parseFile(std::string filename, Callback cb); ///< Parse file
+ /**< \throws SystemException if the file cannot be
+ read. */
private:
struct Impl;
namespace console {
namespace detail {
+#ifndef DOXYGEN
+
struct append_action
{
template <class T, class Value>
};
};
+#endif
+
}}}
///////////////////////////////ih.e////////////////////////////////////////
class Client;
- /** \brief
- ///\fixme Use special non-blocking streambuf
+ /** \brief Interactive console server
+
+ This class provides an interactive console TCP server.
+
+ \todo Add readline support
+ \todo Add interactivity detection using timeout
*/
class Server
: boost::noncopyable
~Server();
static Server & start(senf::INet4SocketAddress const & address);
+ ///< Start server on given IPv4 address/port
static Server & start(senf::INet6SocketAddress const & address);
+ ///< Start server on given IPv6 address/port
- void name(std::string const & name);
+ void name(std::string const & name); ///< Set server name
+ /**< This information is used in the prompt string. */
protected:
friend class Client;
};
- /** \brief
+ /** \brief Server client instance
+
+ Whenever a new client connects, a new instance of this class is created. This class shows a
+ command prompt, receives the commands, parses them and then passes (using a CommandParser)
+ and passes the commands to an Executor instance.
\fixme Fix Client::clientData implementation
- Remove the 'dup' needed here so we don't close the same fd twice (see Client constructor)
- Make output non-blocking
- Don't register a new ReadHelper every round
+ \fixme Remove the 'dup' needed here so we don't close the same fd twice (see Client
+ constructor)
+ \fixme Make output non-blocking (use a non-blocking/discarding streambuf) and possibly set
+ socket send buffer size
+ \fixme Don't register a new ReadHelper every round
*/
class Client
: public senf::intrusive_refcount
~Client();
- void stopClient();
+ void stopClient(); ///< Stop the client
+ /**< This will close the client socket. */
protected:
}
};
+ void shutdownServer(std::ostream &,
+ senf::console::CommandNode::Arguments const &)
+ {
+ senf::Scheduler::instance().terminate();
+ throw senf::console::Executor::ExitException();
+ }
+
}
int main(int, char const **)
{
senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >();
- senf::console::root().doc("This is the console test application");
- senf::console::root().mkdir("network")
+ senf::console::root()
+ .doc("This is the console test application");
+ senf::console::root()
+ .mkdir("network")
.doc("Network related settings");
- senf::console::root()["network"].mkdir("eth0")
+ senf::console::root()["network"]
+ .mkdir("eth0")
.doc("Ethernet device eth0");
- senf::console::root().mkdir("server");
- senf::console::root()["network"].add("route", &fn)
+ senf::console::root()
+ .mkdir("server");
+ senf::console::root()["server"]
+ .add("shutdown", &shutdownServer)
+ .doc("Terminate server application");
+ senf::console::root()["network"]
+ .add("route", &fn)
.doc("Example of a directly registered function");
TestObject test;
- senf::console::root().add("testob", test.dir)
+ senf::console::root()
+ .add("testob", test.dir)
.doc("Example of an instance directory");
senf::console::Server::start( senf::INet4SocketAddress("127.0.0.1:23232") )