// $Id$ // // Copyright (C) 2008 // Fraunhofer Institute for Open Communication Systems (FOKUS) // Competence Center NETwork research (NET), St. Augustin, GERMANY // Stefan Bund // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /** \mainpage The Configuration and Runtime Control Library The Console library implements a runtime interactive (network) console which allows to 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. \autotoc \section console_intro Introduction There are three parts to the Config/console library: \li The console/config library is based on a \link node_tree tree of console/config nodes. \endlink \li Besides directories, the node contains command nodes. Commands are based on \link console_commands variables or callbacks.\endlink \li The console/config library is utilized by writing configuration files or interactive commands in \link console_parser the console/config language.\endlink The node tree works like a directory structure. Commands are entered into this directory structure and can be called passing arbitrary arguments. Configuration parameters are just commands which set their respective parameter, however the library allows commands to do much more than just that. \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, int foo, int bar) { // ... os << "!! Important message ...\n"; } namespace kw = senf::console::kw; 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("If is given, flurgle the , otherwise burgle it") .arg("foo") .arg(kw::name = "bar", kw::default_value = 0); // 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:
    $ 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.
    $
    
\section intro_nodes The node tree The basic idea is, that the console/config library manages a directory structure of parameters and auxiliary commands. Parameters are just commands which set a parameter value so everything is either a directory entry (senf::console::DirectoryNode) or a command (senf::console::CommandNode). \see \ref node_tree \section intro_commands Console/config commands The console/config language does not define, how arguments are passed to the commands, it just tokenizes the input and passes the tokens to the commands which then handle the conversion. Since parsing the tokens into something usable is quite tedious and error prone, the library implements automatic argument parsing where the argument tokens are automatically parsed depending on argument types. This enables you to register a command taking an integer argument which will be called with an already parsed integer value (or throw a senf::console::SyntaxErrorException if the conversion fails). This will be the most often used command. \see \ref console_commands \section intro_language The console/config language To call the commands and set parameters, a very simple language is defined. The language is almost declarative (e.g. it does not have any control-flow statements) but is processed imperatively from top to bottom. This is very simple and flexible. Commands are referenced by their path in the node tree. To simplify working with deeply nested directory structures, the current directory may be changed persistently or temporarily for some commands. \code /server/port 1234; /logger/targets/console { accept senf::log::Debug IMPORTANT; accept server::ServerLog CRITICAL; } \endcode \see \ref console_parser */ /** \defgroup console_commands Supported command types The Console/config library supports quite a number of different command types. All these types of command are registered, by passing them to DirectoryNode::add() \autotoc \section console_cmdadd Adding commands and setting attributes Basically, all commands are added using senf::console::DirectoryNode::add(). What exactly happens depends on the type of object added. \code dir.add("name", callback) \endcode will add a command 'name' which will execute 'callback' when called, where 'callback' can be a lot of things as documented in the following chapters. The add call always returns (something which can be used as) a reference to the command node added: \code senf::console::CommandNode & node ( dir.add( ... ) ); \endcode Depending on the object added, you can also bind to a more specific node type (e.g. senf::console::SimpleCommand) if you know the type of node returned. Depending on the type of object added, there are additional attributes which can be set. These attributes are always set by calling them on the return value before saving that value as a node reference. It is \e not guaranteed, you can call these members on the node reference. \code dir.add("name", callback) .doc("The documentation"); \endcode sets the \e doc attribute (if that is available, otherwise this will fail to compile). The attribute members return value is again (something which can be used as) a reference to the command node \code senf::console::CommandNode & node ( dir.add("name", callback) .doc("The documentation") ); \endcode \section console_manualparse Manually parsing command arguments This is the most primitive type of command. It will be called with an output stream and with a senf::console::ParseCommandInfo reference which holds information about the command parsed. From this information the command callback gets a list of arguments or tokens which then can be interpreted in an arbitrary way. \code void fun1(std::ostream & os, senf::console::ParseCommandInfo const & command) { // We take exactly one argument if (command.arguments().size() != 1) raise senf::console::SyntaxErrorException("invalid number of arguments"); senf::console::ParseCommandInfo::TokenRange & argTokens ( command.arguments()[0]); // The argument must have exactly one token if (argTokens.size() != 1) raise senf::console::SyntaxErrorException("argument syntax error"); // Retrieve the token value std::string arg (argTokens[0].value()); // In this example, we just write the argument to the output stream os << arg << std::endl; } \endcode Registering this callback is done by simply adding it. To provide online help, pass it to 'doc()': \code senf::console::root() .add("test1", &fun1) .doc("Usage:\n" " test1 arg\n" "\n" "Echo 'arg' to the console"); \endcode The callback may now be called interactively on the console by it's registered name: \htmlonly
    server:/$ test1
    invalid number of arguments
    server:/$ test1 stefan@j32.de
    stefan@j32.de
    server:/$ test1 (echo me)
    argument syntax error
    server:/$ help test1
    Usage:
        test1 arg

    Echo 'arg' to the console
    server:/$
    
\endhtmlonly As you can see above, the arguments and tokens are returned as boost::iterator_range instances. These behave much like containers: They have \c begin() and \c end() and some other useful members. The parser will have divided the argument tokens into arguments already. This simplifies further parsing. If however you want to access the list of argument tokens as a single list, you can do so using senf::console::ParseCommandInfo::tokens(). Parsing arguments is quite simple but can get very tedious. To simplify this task, the parsing can be delegated to the Console/config library. See the next section. This type of command has only a single attribute, \e doc to set the commands documentation. \section console_autoparse Automatic argument parsing To greatly simplify parsing complex commands, we turn to automatic argument parsing. \subsection console_autoadd Adding automatically parsed commands Automatically parsed commands are registered by just adding a callback which has the correct arguments and return-value defined: \code std::string fun2(std::string const & arg) { return arg; } \endcode This extremely simple callback may be registered by adding it to a senf::console::DirectoryNode. \code senf::console::root() .add("test2", &fun2); \endcode The functionality is now identical to \c test1: \htmlonly
    server:/$ test2
    invalid number of arguments
    server:/$ test2 stefan@j32.de
    stefan@j32.de
    server:/$ test2 (echo me)
    argument syntax error
    server:/$ help test2
    Usage:
        test2 arg11:string
    server:/$
    
\endhtmlonly \subsection command_ostream Accessing the console stream Commands may have an optional first argument of type std::ostream &. This argument is not considered part of the real interface. When the command is executed, the callback will be passed the current console's output stream object in this argument. With this, the callback can output arbitrary messages to the network console. \code void fun3(std::ostream & os, unsigned n, std::string text) { while (n-- > 0) os << text << std::endl; } senf::console::root() .add("test3", &fun3); \endcode This simple command can now be used thus: \htmlonly
    server:/$ test3
    invalid number of arguments
    server:/$ test3 stefan@j32.de
    invalid number of arguments
    server:/$ test3 2 ok
    ok
    ok
    server:/$ help test3
    Usage:
        test3 arg11:int arg12:string
    server:/$
    
\endhtmlonly \subsection command_overload Command overloading Automatically parsed commands can be overloaded: You can register multiple commands under the same name. Each overload is tried in turn until no SyntaxErrorException is raised. \code senf::console::root() .add("test4", &fun3); senf::console::root() .add("test4", &fun2); \endcode And now, we can call \c test4 with one or two args:
    server:/$ test4
    invalid number of arguments
    server:/$ test4 stefan@j32.de
    stefan@j32.de
    server:/$ test4 2 ok
    ok
    ok
    server:/$ help test4
    Usage:
        1- test4 arg11:int arg12:string
        2- test4 arg21:string
    server:/$
    
\subsection console_attributes Attributes of automatically parsed commands As have seen so far, some documentation is automatically provided. We can add more info, by setting additional attributes. \code senf::console::root() .add("test5", &fun3) .doc("Echo text to the console") .overloadDoc("Repeat {arg12} for {arg11} lines"); senf::console::root() .add("test4", &fun2) .overloadDoc("Echo the {arg21} argument") \endcode This additional info is used to provide more documentation: \htmlonly
    server:/$ help test5
    Usage:
        1- test5 arg11:int arg12:string
        2- test5 arg21:string

    Echo text to the console

    Variant 1:
    Repeat {arg12} for {arg11} lines
    
    Variant 2:
    Echo the {arg21} argument
    senf:/$
    
\endhtmlonly \subsection console_argattributes Argument attributes Additional attributes can be set for each parameter. They are all passed to the senf::console::ParsedArgumentAttributor::arg() attribute. \code namespace kw = senf::console::kw; senf::console::root() .add("test6", &fun3) .doc("Echo text to the console") .overloadDoc("Repeat {text} for {n} lines"); .arg( kw::name = "n", kw::description="Number of repetitions" ) .arg( kw::name = "text", kw::description="Text to output" ); senf::console::root() .add("test6", &fun2) .overloadDoc("Echo the {text} argument") .arg( kw::name = "text" ); \endcode (Sadly, there is no way to automatically find out the \e name of an argument, just it's type.) Every callback argument corresponds with a call of the \c arg() attribute. Argument attributes are set using keywords from the \ref senf::console::kw namespace. You will probably either use this namespace via a namespace alias (as above) or via a using namespace senf::console::kw declaration (but beware of name collisions). You don't need to specify any information for an argument: To skip an argument, just call \c arg() without attributes for this argument. After adding this information, the online help is much more readable \htmlonly
    server:/$ help test6
    Usage:
        1- test6 n:int text:string
        2- test6 text:string

    With:
        n         Number of repetitions
        text      Text to output

    Echo text to the console

    Variant 1:
    Repeat {text} for {n} lines
    
    Variant 2:
    Echo the {text} argument
    senf:/$
    
\endhtmlonly \subsection console_argattribpos Passing argument attributes as positional arguments Since most of the time, we only need to set the name and possibly a description for arguments, there is a shortcut: name and description can be specified as positional arguments in this order. So the following will give the exactly same result as the example in the previous section \code namespace kw = senf::console::kw; senf::console::root() .add("test6", &fun3) .doc("Echo text to the console") .overloadDoc("Repeat for lines"); .arg("n", "Number of repetitions") .arg("text", "Text to output"); senf::console::root() .add("test6", &fun2) .overloadDoc("Echo the argument") .arg("text"); \endcode Keyword arguments should always be used if additional attributes are set. You can however mix positional and keyword arguments. \subsection console_defaults Default values Another information which can not be automatically gathered from the type system is default values. These have to be declared explicitly: \code namespace kw = senf::console::kw; senf::console::root() .add("test7", &fun3) .doc("Echo {text} to the console, repeating {text} for {n} lines") .arg("n", "Number of repetitions", kw::default_value=1) .arg("text", "Text to output"); \endcode Default values can be used together with overloading. Default (optional) value support is quite flexible, it is not mandatory, for default values to be specified only for the trailing arguments. For the exact definition, how parsed argument values are assigned to overload arguments in the presence of default values, see \ref senf::console::kw::default_value. \htmlonly
    server:/$ test7 echo
    echo
    server:/$ test7 4 ok
    ok
    ok
    ok
    ok
    server:/$ help test7
    Usage:
        test4 [n:unsigned] text:string
    
    With:
        n         Number of repetitions
            default: 1
        text      Text to output

    Echo {text} to the console, repeating {text} for {n} lines
    server:/$
    
\endhtmlonly \subsection console_attr_summary Attribute summary Here a summary of the most common attributes
\link senf::console::ParsedArgumentAttributorBase::doc() .doc\endlink ( \e doc )Set documentation for all overloads
\link senf::console::ParsedArgumentAttributorBase::overloadDoc() .overloadDoc\endlink ( \e doc )Set documentation for a specific overload
\link senf::console::ParsedArgumentAttributor::arg() .arg\endlink ( \e argument \e attributes )Set argument attributes (see below)
The most important argument attributes (all defined in the senf::console::kw namespace) are:
\link senf::console::kw::name kw::name\endlinkParameter name
\link senf::console::kw::description kw::description\endlinkOne-line description of the argument
\link senf::console::kw::default_value kw::default_value\endlinkArguments default value
\see senf::console::ParsedArgumentAttributor / List of all members for the complete attribute interface \n \ref senf::console::kw for a list of all argument attribute keywords \section console_memberfn Registering member functions Member functions are supported like non-member functions. They must however be added through a senf::console::ScopedDirectory instance to bind them to their instance. \code class Test { public: senf::console::ScopedDirectory dir; Test(std::string label) : dir(this), label_ (label) { dir.add("test4", &Test::test2); dir.add("test4", &Test::test3); } std::string test2(std::string const & text) { return label_ + ": " + text; } void test3(std::ostream & os, unsigned n, std::string const & text) { while (n-- > 0) os << label << ": " << text << std::endl; } private: std::string label_; }; // ... Test testOb ("test"); senf::console::root().add("testobj", testOb.dir); \endcode Binding via senf::console::ScopedDirectory ensures, that the commands are automatically removed from the tree when the object is destroyed. */ // Local Variables: // mode: c++ // fill-column: 100 // comment-column: 40 // c-file-style: "senf" // indent-tabs-mode: nil // ispell-local-dictionary: "american" // compile-command: "scons -u test" // mode: auto-fill // End: