Fix documentation
g0dil [Sat, 29 Mar 2008 22:43:20 +0000 (22:43 +0000)]
Console: Complete the ObjectDirectory proxy interface
Console: Add additional special chars as punctuation chars
Console: Documentation

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@770 270642c3-0616-0410-b53a-bc976706d245

13 files changed:
Console/Executor.hh
Console/Mainpage.dox
Console/Node.cc
Console/Node.hh
Console/ObjectDirectory.cti
Console/ObjectDirectory.hh
Console/Parse.hh
Console/Parse.ih
Console/Parse.test.cc
Console/Server.cc
Console/Server.hh
doclib/html-munge.xsl
senf.dict

index d089083..48d62ba 100644 (file)
@@ -58,7 +58,7 @@ namespace console {
         : boost::noncopyable
     {
         SENF_LOG_CLASS_AREA();
-        SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
+        SENF_LOG_DEFAULT_LEVEL( senf::log::VERBOSE );
     public:
         ///////////////////////////////////////////////////////////////////////////
         // Types
index f430b75..50cc0e7 100644 (file)
     configure, control and manipulate a running application in any way. Additionally this library
     provides support for configuration files and command line parsing which can be used with or
     without the network console.
+
+    \section console_intro Introduction
+
+    There are two components to the Config/console framework:
+
+    \li Building the node tree by registering objects and callbacks
+    \li Utilizing the config/console framework by writing configuration files or using the
+        interactive console.
+
+    Basic data structure of the console and config framework is the config/console node tree. This
+    tree. This tree works like a file-system. Commands are added to this tree and can then be called
+    from configuration files or from the interactive console.
+
+    To get started using the config/console library, see
+    \li \ref node_tree
+    \li \ref console_parser
+
+    \section console_example Example
+
+    The following example shows a \e very short summary on how to integrate the config/console
+    library. See above links for more:
+
+    \code
+    // Define callback function.
+    void mycommand(std::ostream & os, senf::console::Arguments const & args)
+    {
+        // ...
+        os << "!! Important message ...\n";
+    }
+
+    int main(int, char**)
+    {
+        // Provide global documentation
+        senf::console::root()
+            .doc("This is someServer server");
+
+        // Add a command
+        senf::console::root()
+            .add("mycommand", &mycommand)
+            .doc("mycommand <foo> [<bar>]\n\n"
+                 "If <bar> is given, flurgle the <foo>, otherwise burgle it");
+
+        // Start the interactive console server
+        senf::console::Server::start(senf::INet4SocketAddress(senf::INet4Address::None, 23232u))
+            .name("someServer");
+    }
+    \endcode
+
+    after this registration, the console can be accessed easily via telnet:
+    
+    <pre>
+    $ telnet localhost 23232
+    Trying 127.0.0.1...
+    Connected to localhost.
+    Escape character is '^]'
+    xxxx-xx-xx xx:xx:xx.xxxxxx-0000 [NOTICE][senf::console::Server] Registered new client 0xxxxxxx
+    someServer:/# ls
+    mycommand
+    someServer:/# mycommand
+    !! Important message  ...
+    someServer:/# exit
+    xxxx-xx-xx xx:xx:xx.xxxxxx-0000 [NOTICE][senf::console::Server] Disposing client 0xxxxxxx
+    Connection closed by foreign host.
+    $
+    </pre>
  */
 
 \f
index f74a37b..5e1e427 100644 (file)
@@ -74,6 +74,7 @@ senf::console::DirectoryNode::remove(std::string const & name)
     GenericNode::ptr node (i->second);
     children_.erase(i);
     node->parent_ = 0;
+    node->name_.clear();
     return node;
 }
 
index a1cd759..cadc057 100644 (file)
 /** \file
     \brief Node public header */
 
+/** \defgroup node_tree The console/config file-system node tree
+    
+    The console/config node tree is the central data-structure of the library. Into this tree, all
+    commands and parameters are entered. The tree is then exposed using a file-system like
+    interface.
+    
+    \autotoc
+
+    \section console_tree The tree
+
+    \subsection console_nodes Node types
+
+    The console/config library tree consists of two basic node types:
+    
+    \li senf::console::DirectoryNode provides internal nodes with an arbitrary number of children
+    \li senf::console::CommandNode describes a command entry in the tree
+
+    senf::console::CommandNode is the base-class of all command nodes of which there are several,
+    depending on the type of command.
+
+    There is a single root node, the senf::console::DirectoryNode called senf::console::root(). From
+    this node, the tree is traversed.
+
+    All nodes are allocated on the heap and are managed using a smart pointer.
+    
+    \subsection console_manipulate Manipulating the node tree
+
+    There are several ways to add nodes to the tree:
+
+    \li A senf::console::DirectoryNode can be added using senf::console::DirectoryNode::mkdir().
+    \li An arbitrary node can be created and then (possibly later) added to the tree using the
+        corresponding senf::console::DirectoryNode::add() overload.
+    \li A senf::console::CommandNode is normally added to the tree by directly adding a callback
+        using one of the overloaded senf::console::DirectoryNode::add() members.
+
+    When directly adding a node callback, the type of node added depends on the type of
+    callback. The callback types which can be added are listed at \ref console_callbacks.
+    
+    \code
+    void callback(std::ostream & os, senf::console::Arguments const & args) { ... }
+    // ...
+    myDirectory.add("foo",&callback);
+    \endcode
+
+    Every node is identified among it's siblings by it's name. The name of the node is set when
+    adding the node to the tree. If the name is empty or non-unique, a unique name will be
+    automatically provided.
+
+    To remove a node from the tree, just use the nodes senf::console::GenericNode::unlink()
+    member. This call removes the node from it's parent and returns a (smart) node pointer.
+
+    \li If you ignore the return value, the node (and it's children) will be deleted.
+    \li Alternatively, you may store away the node and re-attach it later.
+    \li An node (or subtree) can be moved to a different place by unlinking the node at it's old
+        place and re-adding it at it's new location.
+    \li To rename a node, unlink and re-add it with a different name.
+
+    \code
+    myDirectory.add("bar", myDirectory("foo").unlink());
+    \endcode
+
+    \subsection console_node_param Assigning additional node parameters
+
+    Depending on the node type added, additional node parameters may be set. For example, every node
+    has a documentation parameter which is used by the online-help system. To assign these
+    parameters, the node exposes corresponding member functions. Since
+    senf::console::DirectoryNode::add() returns the newly added node by reference, additional
+    parameters may just be added to the end of the add command:
+    \code
+    myDirectory.add("foo",&fooCallback).doc("The foo method");
+    \endcode
+    Since the parameter setters all return the node reference, additional parameters may just be
+    added to the end of the command.
+    
+    \subsection console_tree_traverse Traversing the tree
+
+    The simplest way to access tree elements is to save the return value of the
+    senf::console::DirectoryNode::add() members. However, saving the reference will not ensure, that
+    the node is not removed. If the node might be removed from the tree, you should use a smart
+    pointer (either <tt>ptr</tt> or <tt>weak_ptr</tt>) to hold the node.
+
+    Another possibility is to traverse the tree explicitly. For this purpose, the operators '[]' and
+    '()' have been overloaded in senf::console::DirectoryNode.
+    \code
+    senf::console::root()["myDirectory"]("foo")
+    \endcode
+    The '[]' operator will return a senf::console::DirectoryNode whereas '()' will return a
+    senf::console::CommandNode. If the node is not found or is not of the correct type, an exception
+    will be raised.
+
+    \section console_object_dir Assigning a directory to an object instance
+
+    Most objects will register several commands. So it makes sense for these objects to manage their
+    own directory. Since directories are however allocated on the heap, they cannot be directly
+    added to a class. To facilitate this usage, the senf::console::ObjectDirectory is used. This
+    class provides a senf::console::DirectoryNode facade. Internally, it automatically creates a
+    senf::console::DirectoryNode to which all calls are forwarded.
+
+    The senf::console::ObjectDirectory member should be declared public. This allows the user of the
+    class to add the node to the tree.
+
+    \section console_long_example Example
+
+    The following is a more complete example. It uses most of the features you will be using from
+    the console library.
+
+    \code
+    // Define callback function.
+    void mycommand(std::ostream & os, senf::console::Arguments const & args)
+    {
+        // ...
+        os << "!! Important message ...\n";
+    }
+
+    class SomeClass
+    {
+    public:
+        // Declare a directory node (proxy) for use by this class. This must be public so we can add
+        // it to the node tree later.
+        senf::console::ObjectDirectory<SomeClass> dir;
+
+        SomeClass() : dir(this) 
+        {
+            // You may document the directory here or later when adding it to the tree
+            dir.doc("Manager for something");
+
+            // Add a member function (the pointer-to-member is automatically bound to this instance)
+            dir.add("member", &SomeClass::member)
+                .doc("Do the member operation");
+        }
+
+        void member(std::ostream & os, senf::console::Arguments const & args)
+        {
+            // ...
+        }
+    };
+
+    int main(int, char**)
+    {
+        // Provide global documentation
+        senf::console::root()
+            .doc("This is someServer server");
+
+        // Add a new directory to the root and document it. All the mutators return the node object
+        // itself so operations can be chained.
+        senf::console::DirectoryNode & mydir (
+                .mkdir("myserver")
+                .doc("My server specific directory"));
+
+        // Add a command to that directory
+        mydir.add("mycommand", &mycommand)
+            .doc("mycommand <foo> [<bar>]\n\n"
+                 "If <bar> is given, flurgle the <foo>, otherwise burgle it");
+
+        // Create a SomeClass instance and add it's directory.
+        SomeClass someClass;
+        mydir.add("someClass", someClass.dir);
+
+        // Start the interactive console server
+        senf::console::Server::start(senf::INet4SocketAddress(senf::INet4Address::None, 23232u))
+            .name("someServer");
+    }
+    \endcode
+ */
+
 #ifndef HH_Node_
 #define HH_Node_ 1
 
@@ -65,6 +230,8 @@ namespace console {
 
         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.
+
+        \ingroup node_tree
       */
     class GenericNode 
         : public boost::enable_shared_from_this<GenericNode>
@@ -168,6 +335,8 @@ namespace console {
         '-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.
+
+        \ingroup node_tree
       */
     class DirectoryNode : public GenericNode
     {
@@ -319,6 +488,8 @@ namespace console {
 
         To execute a command, CommandNode::operator()() is called. This abstract virtual function
         must be implemented in a derived class.
+
+        \ingroup node_tree
       */
     class CommandNode : public GenericNode
     {
@@ -351,11 +522,15 @@ namespace console {
     private:
     };
 
+    typedef CommandNode::Arguments Arguments;
+
     /** \brief Most simple CommandNode implementation
 
         This CommandNode implementation simply forwards the \a output and \a arguments arguments to
         an arbitrary callback.
-      */
+        \ingroup node_tree
+     */
     class SimpleCommandNode : public CommandNode
     {
         SENF_LOG_CLASS_AREA();
index 75b9c9f..c3f3e0d 100644 (file)
@@ -67,6 +67,59 @@ senf::console::ObjectDirectory<Owner>::add(std::string const & name, Object cons
 }
 
 template <class Owner>
+prefix_ senf::console::GenericNode::ptr
+senf::console::ObjectDirectory<Owner>::remove(std::string const & name)
+{
+   return node().remove(name);
+}
+
+template <class Owner>
+prefix_ senf::console::DirectoryNode &
+senf::console::ObjectDirectory<Owner>::operator[](std::string const & name)
+    const
+{
+    return node()[name];
+}
+
+template <class Owner>
+prefix_ senf::console::CommandNode &
+senf::console::ObjectDirectory<Owner>::operator()(std::string const & name)
+    const
+{
+    return node()(name);
+}
+
+template <class Owner>
+prefix_ senf::console::GenericNode &
+senf::console::ObjectDirectory<Owner>::get(std::string const & name)
+    const
+{
+    return node().get(name);
+}
+
+template <class Owner>
+prefix_ senf::console::DirectoryNode &
+senf::console::ObjectDirectory<Owner>::mkdir(std::string const & name)
+{
+    return node().mkdir(name);
+}
+
+template <class Owner>
+prefix_ senf::console::DirectoryNode::ChildrenRange
+senf::console::ObjectDirectory<Owner>::children()
+    const
+{
+    return node().children();
+}
+
+template <class Owner>
+prefix_ senf::console::DirectoryNode &
+senf::console::ObjectDirectory<Owner>::doc(std::string const & doc)
+{
+    return node.doc(doc);
+}
+
+template <class Owner>
 prefix_ senf::console::DirectoryNode & senf::console::ObjectDirectory<Owner>::node()
     const
 {
index bb8008f..1845a06 100644 (file)
@@ -95,6 +95,8 @@ namespace console {
             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).
+
+        \ingroup node_tree
       */
     template <class Owner>
     class ObjectDirectory : public ObjectDirectoryBase
@@ -127,6 +129,20 @@ namespace console {
 
         DirectoryNode & node() const;   ///< Access the proxied DirectoryNode
 
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Proxied members (see DirectoryNode)
+        ///\{
+
+        GenericNode::ptr remove(std::string const & name);
+        DirectoryNode & operator[](std::string const & name) const;
+        CommandNode & operator()(std::string const & name) const;
+        GenericNode & get(std::string const & name) const;
+        DirectoryNode & mkdir(std::string const & name);
+        DirectoryNode::ChildrenRange children() const;
+        DirectoryNode & doc(std::string const & doc);
+
+        ///\}
+
     protected:
 
     private:
index 8684f80..1dc7498 100644 (file)
 #ifndef HH_Parse_
 #define HH_Parse_ 1
 
+/** \defgroup console_parser The console/config parser
+
+    The console/config library defines a simple language used to interact with the console or to
+    configure the application.  The parser is not concerned about interpreting commands or
+    arguments, checking that a command exists or managing directories. The parser just takes the
+    input and parses it.
+
+    \autotoc
+
+    \section console_language The Language
+
+    The config/console language is used in configuration files and interactively at the
+    console. Some features of the language are more useful in config files, others at the
+    interactive console but the language is the same in both cases.
+
+    Let's start with a sample of the config/console language. The following is written as a
+    configuration file
+    \code
+    # My someserver configuration file
+    
+    /server/port 1234;
+    
+    /logger/targets {
+        console {
+            accept senf::log::Debug IMPORTANT;
+            accept server::ServerLog CRITICAL;
+        }
+
+        provide serverlog senf::log::FileTarget "/var/log/server.log";
+        serverlog {
+            reject senf::log::Debug senf::Console::Server NOTICE;
+            accept senf::log::Debug NOTICE;
+            accept server::ServerLog;
+        }
+    }
+
+    /server/stuffing (UDPPacket x"01 02 03 04");
+    /server/allow_hosts 10.1.2.3               # our internal server
+                        10.2.3.4 10.4.3.5      # client workstations
+        ;
+
+    /help/infoUrl "http://senf.j32.de/src/doc";
+    \endcode
+
+    The interactive syntax is the same with some notes:
+    \li All commands must be complete on a single line. This includes grouping constructs which must
+        be closed on the same line they are opened.
+    \li The last ';' is optional. However, multiple commands may be entered on a single line when
+        they are separated by ';'.
+    \li An empty line on the interactive console will repeat the last command.
+
+    The language consists of a small number of syntactic entities:
+
+    \subsection console_special_chars Special characters
+
+    These are characters, which have a special meaning. Some are used internally, others are just
+    returned as punctuation tokens
+    
+    <table class="senf">
+    <tr><td>/</td><td>path component separator</td></tr>
+    <tr><td>( )</td><td>argument grouping</td></tr>
+    <tr><td>{ }</td><td>directory grouping</td></tr>
+    <tr><td>;</td><td>command terminator</td></tr>
+    <tr><td>, =</td><td>punctuation tokens</td></tr>
+    </table>
+
+    \subsection console_basic Basic elements
+
+    A <b>word</b> is \e any sequence of consecutive characters which does not include any special
+    character. Examples for words are thus
+    <pre>
+    12.34
+    jens@fokus.fraunhofer.de
+    eth0
+    1>2
+    </pre>
+
+    The following are \e not valid words:
+    <pre>
+    a/b/c
+    a,b
+    </pre>
+
+    A <b>string literal</b> is just that: A double-quoted string (C/C++ style) possibly with
+    embedded escape chars:
+    <pre>
+    "\"foo\nbar\""
+    "\x04test"
+    </pre>
+    
+    A <b>hex-string literal</b> is used to represent binary data. It looks like a string which has
+    only hexadecimal bytes or whitespace as contents (comments and newlines are Ok when not read
+    from the interactive console)
+    <pre>
+    x"01 02 03 0405"
+    x"01 02   # ID header
+      0405    # payload
+      "
+    </pre>
+
+    A <b>token</b> is a \e word, \e string or \e hex-string, or a single special character (that's
+    true, any special character is allowed as a token). '(' and ')' must be properly nested.
+
+    A <b>path</b> is a sequence of \e words separated by '/' (and optional whitespace). A path may
+    have an optional initial and/or a terminating '/'.
+    <pre>
+    a/b/c
+    foo / bar /
+    /server
+    </pre>
+
+    \subsection console_statements Statements
+    
+    There are several types of statements:
+    \li The bulk of all statements are \e path statements
+    \li There are some \e built-in statements which are mostly useful at the interactive console
+    \li A special form of statement is the <em>directory group</em>
+
+    A <b>path</b> statement consists of a (possibly relative) path followed by any number of
+    arguments and terminated with a ';' (or end-of-input)
+    <pre>
+    /path/to/command arg1 "arg2" (complex=(1 2) another) ;
+    </pre>
+    Every argument is either
+    \li A single word, string or hex-string
+    \li or a parenthesized list of tokens.
+
+    So above command has three arguments: 'arg1', 'arg2' (a single token each) and one argument with
+    the 7 tokens 'complex', '=', '(', '1', '2', ')', 'another'. The interpretation of the arguments
+    is completely up to the command.
+
+    A <b>built-in</b> statement is one of
+    
+    <table class="senf">
+    <tr><td>\c cd \e path</td><td>Change current directory</td></tr>
+    <tr><td>\c ls [ \e path ]</td><td>List contents of \e path or current directory</td></tr>
+    <tr><td>\c exit</td><td>Exit interactive console</td></tr>
+    <tr><td>\c help [ \e path ]</td><td>Show help for \e path or current directory</td></tr>
+    </table>
+
+    A <b>directory group</b> statement is a block of statements all executed relatively to a fixed
+    directory.
+    <pre>
+    /some/path {
+        statement ;
+        . . .
+    }
+    </pre>
+    At the beginning of the block, the current directory is saved and the directory is changed to
+    the given directory. All commands are executed and at the end of the block, the saved directory
+    is restored.
+
+    \section console_parse_api The parser API
+
+    The senf::console::CommandParser is responsible for taking text input and turning it into a
+    sequence of senf::console::ParseCommandInfo structures. The structures are returned by passing
+    them successively to a callback function.
+
+    Every statement is returned as a senf::console::ParseCommandInfo instance. Directory groups are
+    handled specially: They are divided into two special built-in commands called PUSHD and POPD.
+ */
+
 // Custom includes
 #include <string>
 #include <vector>
@@ -46,6 +208,8 @@ namespace console {
 
         All command arguments are split into tokens by the parser. Each token is returned as an
         ArgumentToken instance. 
+
+        \ingroup console_parser
       */
     class ArgumentToken
     {
@@ -69,10 +233,12 @@ namespace console {
         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
+        \li the type of command: built-in 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.
+
+        \ingroup console_parser
       */
     class ParseCommandInfo
     {
@@ -106,9 +272,9 @@ namespace console {
 
         BuiltinCommand builtin() const; ///< Command type
                                         /**< \returns \c NoBuiltin, if the command is an ordinary
-                                             command, otherwise the id of the builtin command */
+                                             command, otherwise the id of the built-in command */
         CommandPathRange commandPath() const; ///< Command path
-                                        /**< This is the path to the command if it is not a builtin
+                                        /**< This is the path to the command if it is not a built-in
                                              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
@@ -116,7 +282,7 @@ namespace console {
                                              path ends in a '/' char. */
         ArgumentsRange arguments() const; ///< Command arguments
                                         /**< The returned range contains one token range for each
-                                             agument. */
+                                             argument. */
         TokensRange tokens() const;     ///< All argument tokens
                                         /**< The returned range contains \e all argument tokens in a
                                              single range not divided into separate arguments. */
@@ -165,6 +331,8 @@ namespace console {
             needs to be analyzed before this is viable.
 
         \todo Implement more detailed error reporting and error recovery.
+
+        \ingroup console_parser
       */
     class CommandParser
         : boost::noncopyable
index 0de5ade..9a62aa7 100644 (file)
@@ -140,7 +140,8 @@ namespace detail {
                 special_p ("/(){};"),
 
                 // Characters which are returned as punctuation tokens
-                punctuation_p (",="),
+                // (only allowed within '()')
+                punctuation_p (",=/{};"),
 
                 // Whitespace characters
                 space_p (" \t\n\r"),
index 491866a..9d465b7 100644 (file)
@@ -95,7 +95,7 @@ BOOST_AUTO_UNIT_TEST(commandGrammar)
         "doo / bii / doo arg"
         "                flab::blub"
         "                123.434>a"
-        "                (a,b,c (huhu))"
+        "                (a,b;c (huhu/{haha}))"
         "                \"foo\\\"bar\" #\n"
         "                x\"01 02 # Inner comment\n"
         "                   0304\";"
@@ -118,10 +118,14 @@ BOOST_AUTO_UNIT_TEST(commandGrammar)
                        "pushWord( a )\n"
                        "pushPunctuation( , )\n"
                        "pushWord( b )\n"
-                       "pushPunctuation( , )\n"
+                       "pushPunctuation( ; )\n"
                        "pushWord( c )\n"
                        "pushPunctuation( ( )\n"
                        "pushWord( huhu )\n"
+                       "pushPunctuation( / )\n"
+                       "pushPunctuation( { )\n"
+                       "pushWord( haha )\n"
+                       "pushPunctuation( } )\n"
                        "pushPunctuation( ) )\n"
                        "closeGroup()\n"
                        "pushArgument( foo\"bar )\n"
index 29f18cf..4d6eb2a 100644 (file)
@@ -67,7 +67,7 @@ senf::console::Server::start(senf::INet4SocketAddress const & address)
     senf::console::Server::start(handle);
     SENF_LOG((Server::SENFLogArea)(log::NOTICE)( 
                  "Console server started at " << address ));
-    return *instance_;
+    return instance();
 }
 
 prefix_ senf::console::Server &
@@ -77,15 +77,31 @@ senf::console::Server::start(senf::INet6SocketAddress const & address)
     senf::console::Server::start(handle);
     SENF_LOG((Server::SENFLogArea)(log::NOTICE)( 
                  "Console server started at " << address ));
-    return *instance_;
+    return instance();
 }
 
-boost::scoped_ptr<senf::console::Server> senf::console::Server::instance_;
+prefix_ senf::console::Server & senf::console::Server::instance()
+{
+    SENF_ASSERT( instancePtr() );
+    return *instancePtr();
+}
+
+prefix_ boost::scoped_ptr<senf::console::Server> & senf::console::Server::instancePtr()
+{
+    // We cannot make 'instance' a global or class-static variable, since it will then be destructed
+    // at an unknown time which may fail if the scheduler or the file-handle pool allocators have
+    // already been destructed.
+    static boost::scoped_ptr<senf::console::Server> instance;
+    return instance;
+}
 
 prefix_ void senf::console::Server::start(ServerHandle handle)
 {
-    SENF_ASSERT( ! instance_ );
-    instance_.reset(new Server(handle));
+    // Uah .... ensure the scheduler is created before the instance pointer so it get's destructed
+    // AFTER it.
+    (void) senf::Scheduler::instance();
+    SENF_ASSERT( ! instancePtr() );
+    instancePtr().reset(new Server(handle));
 }
 
 prefix_ senf::console::Server::Server(ServerHandle handle)
@@ -133,7 +149,7 @@ prefix_ senf::console::Client::~Client()
 prefix_ void senf::console::Client::stopClient()
 {
     // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER removeClient RETURNS
-    Server::instance_->removeClient(*this);
+    Server::instance().removeClient(*this);
 }
 
 prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr helper)
index 7bca747..3b7a93e 100644 (file)
@@ -56,6 +56,13 @@ namespace console {
 
         \todo Add readline support
         \todo Add interactivity detection using timeout
+        \idea To support blocking commands, we could give the Client 'suspend()' and 'resume()'
+            members. suspend() would probably throw some kind of exception to transfer control back
+            to the Client instance. on resume(), the command would be called again, maybe setting
+            some flag or something. Example for use: Host name resolution: Here we can just built
+            our own little host-name cache. When the name is not found, we ask the resolver to
+            resolve it and call 'resume' when the name is found. Since it is in the cache now, the
+            command will now complete.
       */
     class Server
         : boost::noncopyable
@@ -76,6 +83,7 @@ namespace console {
                                         ///< Start server on given IPv4 address/port
         static Server & start(senf::INet6SocketAddress const & address);
                                         ///< Start server on given IPv6 address/port
+        static Server & instance();
 
         void name(std::string const & name); ///< Set server name
                                         /**< This information is used in the prompt string. */
@@ -86,6 +94,7 @@ namespace console {
         Server(ServerHandle handle);
 
         static void start(ServerHandle handle);
+        static boost::scoped_ptr<Server> & instancePtr();
 
         void newClient(Scheduler::EventId event);
         void removeClient(Client & client);
@@ -96,8 +105,6 @@ namespace console {
         Clients clients_;
         std::string name_;
         
-        static boost::scoped_ptr<Server> instance_;
-        
         friend class Client;
     };
     
@@ -109,8 +116,6 @@ namespace console {
 
         \fixme Fix Client::clientData implementation
         \fixme Don't register a new ReadHelper every round
-        \fixme Ensure, that output errors (or any errors) in the console don't terminate the
-            application
      */
     class Client
         : public senf::intrusive_refcount, 
index 734ca8b..05e0d95 100644 (file)
       <ul>\r
         <xsl:for-each select="following::h2|following::h3|following::h4">\r
           <xsl:element name="li">\r
-            <xsl:attribute name="class"><xsl:value-of select="concat('level_',local-name())"/></xsl:attribute>\r
-            <b><xsl:call-template name="section-number"/></b>\r
+            <xsl:attribute name="class">\r
+              <xsl:value-of select="concat('level_',local-name())"/>\r
+            </xsl:attribute>\r
+            <b><xsl:call-template name="section-number"/><xsl:text> </xsl:text></b>\r
             <xsl:element name="a">\r
-              <xsl:attribute name="href"><xsl:value-of select="concat('#',a/@name)"/></xsl:attribute>\r
+              <xsl:choose>\r
+                <xsl:when test="a/@name">\r
+                  <xsl:attribute name="href">\r
+                    <xsl:value-of select="concat('#',a/@name)"/>\r
+                  </xsl:attribute>\r
+                </xsl:when>\r
+                <xsl:otherwise>\r
+                  <xsl:attribute name="href">\r
+                    <xsl:text>#autotoc-</xsl:text>\r
+                    <xsl:call-template name="section-number"/>\r
+                  </xsl:attribute>\r
+                </xsl:otherwise>\r
+              </xsl:choose>\r
               <xsl:value-of select="string(current())"/>\r
             </xsl:element>\r
           </xsl:element>\r
   </xsl:template>\r
 \r
   <xsl:template name="section-number">\r
-    <xsl:number level="any" from="h1" count="h2"/>\r
+    <xsl:number level="any" from="div[@id='autotoc']" count="h2"/>\r
     <xsl:text>.</xsl:text>\r
     <xsl:if test="self::h3|self::h4">\r
       <xsl:number level="any" from="h2" count="h3"/>\r
       <xsl:number level="any" from="h3" count="h4"/>\r
       <xsl:text>.</xsl:text>\r
     </xsl:if>\r
-    <xsl:text> </xsl:text>\r
   </xsl:template>\r
   \r
   <xsl:template match="h2|h3|h4">\r
     <xsl:copy>\r
       <xsl:call-template name="copy-attributes"/>\r
-      <xsl:if test="preceding::div[@id='autotoc']">\r
-        <xsl:call-template name="section-number"/>\r
-      </xsl:if>\r
-      <xsl:apply-templates/>\r
+      <xsl:choose>\r
+        <xsl:when test="preceding::div[@id='autotoc']">\r
+          <xsl:call-template name="section-number"/>\r
+          <xsl:text> </xsl:text>\r
+          <xsl:choose>\r
+            <xsl:when test="a">\r
+              <xsl:apply-templates/>\r
+            </xsl:when>\r
+            <xsl:otherwise>\r
+              <xsl:element name="a">\r
+                <xsl:attribute name="class"><xsl:text>anchor</xsl:text></xsl:attribute>\r
+                <xsl:attribute name="name">\r
+                  <xsl:text>autotoc-</xsl:text>\r
+                  <xsl:call-template name="section-number"/>\r
+                </xsl:attribute>\r
+                <xsl:apply-templates/>\r
+              </xsl:element>\r
+            </xsl:otherwise>\r
+          </xsl:choose>\r
+        </xsl:when>\r
+        <xsl:otherwise>\r
+          <xsl:apply-templates/>\r
+        </xsl:otherwise>\r
+      </xsl:choose>\r
     </xsl:copy>\r
   </xsl:template>\r
-  \r
+\r
   <!-- Build dia image-map from special div/span elements -->\r
   <xsl:template match="div[@class='diamap']">\r
     <xsl:element name="map">\r
index dc47055..05fa799 100644 (file)
--- a/senf.dict
+++ b/senf.dict
@@ -16,10 +16,11 @@ AddressParser
 addtogroup
 aListCollection
 alloc
-API
+api
 arg
 argc
 args
+ArgumentToken
 argv
 async
 Augustin
@@ -45,11 +46,13 @@ checksumPresent
 CIDR
 ClientSocketHandle
 CloneSource
+CommandNode
+CommandParser
 CommunicationPolicy
 CommunicationPolicyBase
 ConcretePacket
 conf
-CONFIG
+config
 ConnectedCommunicationPolicy
 ConnectedRawV
 ConnectedUDPv
@@ -68,6 +71,8 @@ defaultInit
 defgroup
 deque
 dil
+dir
+DirectoryNode
 disableChecksum
 dl
 DNS
@@ -113,10 +118,12 @@ FFFF
 FileBody
 filebody
 FileHandle
+FileTarget
 findNext
 findPrev
 fixme
 fixvariant
+flurgle
 fokus
 foo
 FooParser
@@ -153,6 +160,7 @@ ifndef
 ih
 impl
 INet
+infoUrl
 ingroup
 init
 initHeadSize
@@ -179,6 +187,7 @@ IPv
 IPX
 isock
 iterator
+jens
 join
 key
 li
@@ -195,14 +204,19 @@ MACAddress
 MACAddressParser
 mainpage
 mixin
+mkdir
 MPEGDVBBundle
 multicast
+mycommand
+mydir
 MyList
 MyParser
+myserver
 MyVariant
 MyVariantPolicy
 MyVector
 namespace
+nbar
 nc
 netcat
 NETwork
@@ -213,9 +227,11 @@ nextPacketRange
 nextPacketType
 NIS
 NoAddressingPolicy
+NoBuiltin
 noinit
 noroute
 nothrow
+ObjectDirectory
 offene
 Ok
 onRequest
@@ -252,6 +268,7 @@ PacketTypeNotRegistered
 param
 ParseArray
 parsecollection
+ParseCommandInfo
 ParseHelpers
 parseint
 ParseInt
@@ -275,6 +292,7 @@ PassiveSink
 PassiveSocketWriter
 PassiveSource
 png
+POPD
 ppi
 pre
 prev
@@ -282,6 +300,7 @@ PriorityJoin
 protocolbundle
 protocolbundles
 protocolType
+PUSHD
 py
 QueueingDiscipline
 queueSize
@@ -309,8 +328,11 @@ scons
 ScopeId
 screenshot
 sec
+seekable
 senf
 senfscons
+ServerLog
+serverlog
 ServerSocketHandle
 setBegin
 setEnd
@@ -325,6 +347,8 @@ skipline
 SocketAddress
 SocketHandle
 SocketProtocol
+SomeClass
+someClass
 SomeEvent
 SomeEventArg
 someField
@@ -335,8 +359,11 @@ SomePacketParser
 SomePacketType
 SomeParser
 SomeRegistryTag
+someserver
+someServer
 SomeTag
 someVector
+src
 ssi
 std
 stefan
@@ -374,10 +401,13 @@ unthrottled
 unthrottles
 unthrottling
 Utils
+var
 VectorN
 Ver
 vlanId
 VLanId
 VoidPacketParser
 www
+xxxx
+xxxxxx
 xyz