: boost::noncopyable
{
SENF_LOG_CLASS_AREA();
- SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
+ SENF_LOG_DEFAULT_LEVEL( senf::log::VERBOSE );
public:
///////////////////////////////////////////////////////////////////////////
// Types
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
GenericNode::ptr node (i->second);
children_.erase(i);
node->parent_ = 0;
+ node->name_.clear();
return node;
}
/** \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
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>
'-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
{
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
{
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();
}
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
{
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
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:
#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>
All command arguments are split into tokens by the parser. Each token is returned as an
ArgumentToken instance.
+
+ \ingroup console_parser
*/
class ArgumentToken
{
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
{
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
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. */
needs to be analyzed before this is viable.
\todo Implement more detailed error reporting and error recovery.
+
+ \ingroup console_parser
*/
class CommandParser
: boost::noncopyable
special_p ("/(){};"),
// Characters which are returned as punctuation tokens
- punctuation_p (",="),
+ // (only allowed within '()')
+ punctuation_p (",=/{};"),
// Whitespace characters
space_p (" \t\n\r"),
"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\";"
"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"
senf::console::Server::start(handle);
SENF_LOG((Server::SENFLogArea)(log::NOTICE)(
"Console server started at " << address ));
- return *instance_;
+ return instance();
}
prefix_ senf::console::Server &
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)
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)
\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
///< 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. */
Server(ServerHandle handle);
static void start(ServerHandle handle);
+ static boost::scoped_ptr<Server> & instancePtr();
void newClient(Scheduler::EventId event);
void removeClient(Client & client);
Clients clients_;
std::string name_;
- static boost::scoped_ptr<Server> instance_;
-
friend class Client;
};
\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,
<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
addtogroup
aListCollection
alloc
-API
+api
arg
argc
args
+ArgumentToken
argv
async
Augustin
CIDR
ClientSocketHandle
CloneSource
+CommandNode
+CommandParser
CommunicationPolicy
CommunicationPolicyBase
ConcretePacket
conf
-CONFIG
+config
ConnectedCommunicationPolicy
ConnectedRawV
ConnectedUDPv
defgroup
deque
dil
+dir
+DirectoryNode
disableChecksum
dl
DNS
FileBody
filebody
FileHandle
+FileTarget
findNext
findPrev
fixme
fixvariant
+flurgle
fokus
foo
FooParser
ih
impl
INet
+infoUrl
ingroup
init
initHeadSize
IPX
isock
iterator
+jens
join
key
li
MACAddressParser
mainpage
mixin
+mkdir
MPEGDVBBundle
multicast
+mycommand
+mydir
MyList
MyParser
+myserver
MyVariant
MyVariantPolicy
MyVector
namespace
+nbar
nc
netcat
NETwork
nextPacketType
NIS
NoAddressingPolicy
+NoBuiltin
noinit
noroute
nothrow
+ObjectDirectory
offene
Ok
onRequest
param
ParseArray
parsecollection
+ParseCommandInfo
ParseHelpers
parseint
ParseInt
PassiveSocketWriter
PassiveSource
png
+POPD
ppi
pre
prev
protocolbundle
protocolbundles
protocolType
+PUSHD
py
QueueingDiscipline
queueSize
ScopeId
screenshot
sec
+seekable
senf
senfscons
+ServerLog
+serverlog
ServerSocketHandle
setBegin
setEnd
SocketAddress
SocketHandle
SocketProtocol
+SomeClass
+someClass
SomeEvent
SomeEventArg
someField
SomePacketType
SomeParser
SomeRegistryTag
+someserver
+someServer
SomeTag
someVector
+src
ssi
std
stefan
unthrottles
unthrottling
Utils
+var
VectorN
Ver
vlanId
VLanId
VoidPacketParser
www
+xxxx
+xxxxxx
xyz