Scheduler: Implement Timer helper
g0dil [Thu, 29 May 2008 23:13:04 +0000 (23:13 +0000)]
Console: Add incremental parsing support
Console: Implement non-interactive network console

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

13 files changed:
Console/Parse.cc
Console/Parse.cci
Console/Parse.hh
Console/Parse.ih
Console/Parse.test.cc
Console/Readline.cci
Console/Server.cc
Console/Server.cci
Console/Server.hh
Console/Server.ih
Scheduler/Timer.cci [new file with mode: 0644]
Scheduler/Timer.hh [new file with mode: 0644]
Socket/ClientSocketHandle.hh

index bc4fd46..57a5532 100644 (file)
@@ -45,12 +45,11 @@ namespace detail {
     struct ParseDispatcher
     {
         ParseCommandInfo * info_;
-        CommandParser::Callback cb_;
 
         struct BindInfo {
-            BindInfo( ParseDispatcher & d, ParseCommandInfo & info, CommandParser::Callback cb)
-                : dispatcher (d) { dispatcher.info_ = &info; dispatcher.cb_ = cb; }
-            ~BindInfo() { dispatcher.info_ = 0; dispatcher.cb_  = 0; }
+            BindInfo( ParseDispatcher & d, ParseCommandInfo & info)
+                : dispatcher (d) { dispatcher.info_ = &info; }
+            ~BindInfo() { dispatcher.info_ = 0; }
 
             ParseDispatcher & dispatcher;
         };
@@ -60,7 +59,7 @@ namespace detail {
               info_->command(command); }
 
         void endCommand()
-            { cb_(*info_); }
+            { }
 
         void pushToken(Token const & token)
             { info_->addToken(token); }
@@ -68,36 +67,30 @@ namespace detail {
         void builtin_cd(std::vector<Token> & path)
             { info_->clear();
               info_->builtin(ParseCommandInfo::BuiltinCD);
-              setBuiltinPathArg(path);
-              cb_(*info_); }
+              setBuiltinPathArg(path); }
 
         void builtin_ls(std::vector<Token> & path)
             { info_->clear();
               info_->builtin(ParseCommandInfo::BuiltinLS);
-              setBuiltinPathArg(path);
-              cb_(*info_); }
+              setBuiltinPathArg(path); }
 
         void pushDirectory(std::vector<Token> & path)
             { info_->clear();
               info_->builtin(ParseCommandInfo::BuiltinPUSHD);
-              setBuiltinPathArg(path);
-              cb_(*info_); }
+              setBuiltinPathArg(path); }
 
         void popDirectory()
             { info_->clear();
-              info_->builtin(ParseCommandInfo::BuiltinPOPD);
-              cb_(*info_); }
+              info_->builtin(ParseCommandInfo::BuiltinPOPD); }
         
         void builtin_exit()
             { info_->clear();
-              info_->builtin(ParseCommandInfo::BuiltinEXIT);
-              cb_(*info_); }
+              info_->builtin(ParseCommandInfo::BuiltinEXIT); }
 
         void builtin_help(std::vector<Token> & path)
             { info_->clear();
               info_->builtin(ParseCommandInfo::BuiltinHELP);
-              setBuiltinPathArg(path);
-              cb_(*info_); }
+              setBuiltinPathArg(path); }
 
         void setBuiltinPathArg(std::vector<Token> & path)
             {
@@ -132,14 +125,16 @@ prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const
         "Word" };
     // The real table is:
     //     static const int bitPosition[32] = {
-    //         0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
-    //         31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
-    // However, we have replaced all values > sizeof(tokenTypeName) with 0
+    //          0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8, 
+    //         31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9 };
+    // However, we have replaced all values >= sizeof(tokenTypeName) with 0
+    // and have added 1 to all the remaining values
     static const int bitPosition[32] = {
-        0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 4, 8, 
-        0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 6, 0, 5, 10, 9 };
+        1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9, 
+        0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
+    // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
     os << tokenTypeName[ token.type() 
-                         ? bitPosition[((token.type() & -token.type()) * 0x077CB531UL) >> 27]+1
+                         ? bitPosition[((token.type() & -token.type()) * 0x077CB531UL) >> 27]
                          : 0 ]
        << "('"
        << token.value()
@@ -165,7 +160,7 @@ prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
         }
     }
     else {
-        char const * builtins[] = { "", "cd", "ls", "pushd", "popd", "exit", "help" };
+        char const * builtins[] = { 0, "cd", "ls", "pushd", "popd", "exit", "help" };
         stream << "builtin-" << builtins[info.builtin()];
     }
         
@@ -253,40 +248,79 @@ prefix_ senf::console::CommandParser::CommandParser()
 prefix_ senf::console::CommandParser::~CommandParser()
 {}
 
-prefix_ bool senf::console::CommandParser::parse(std::string command, Callback cb)
+// This template member is placed here, since it is ONLY called from the implementation.  Otherwise,
+// we would need to expose the Impl member to the public, which we don't want to do.
+
+template <class Iterator>
+prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator b, Iterator e, Callback cb)
 {
     ParseCommandInfo info;
-    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, cb);
-    return boost::spirit::parse( command.begin(), command.end(), 
-                                 impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
-                                 impl().grammar.use_parser<Impl::Grammar::SkipParser>()
-        ).full;
+    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
+    boost::spirit::parse_info<Iterator> result;
+
+    for(;;) {
+        result = boost::spirit::parse(
+            b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
+        b = result.stop;
+        if (b == e) 
+            return e;
+        info.clear();
+        result = boost::spirit::parse(b, e,
+                                      impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
+                                      impl().grammar.use_parser<Impl::Grammar::SkipParser>());
+        if (! result.hit) 
+            return b;
+        if (! info.empty()) 
+            cb(info);
+        b = result.stop;
+    }
 }
 
-prefix_ bool senf::console::CommandParser::parseFile(std::string filename, Callback cb)
+prefix_ bool senf::console::CommandParser::parse(std::string const & command, Callback cb)
+{
+    return parseLoop(command.begin(), command.end(), cb) == command.end();
+}
+
+prefix_ bool senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
 {
-    ParseCommandInfo info;
-    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, cb);
     boost::spirit::file_iterator<> i (filename);
     if (!i) throw SystemException(ENOENT SENF_EXC_DEBUGINFO);
     boost::spirit::file_iterator<> const i_end (i.make_end());
     
-    return boost::spirit::parse( i, i_end, 
-                                 impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
-                                 impl().grammar.use_parser<Impl::Grammar::SkipParser>()
-        ).full;
+    return parseLoop(i, i_end, cb) == i_end;
 }
 
-prefix_ bool senf::console::CommandParser::parseArguments(std::string arguments,
+prefix_ bool senf::console::CommandParser::parseArguments(std::string const & arguments,
                                                           ParseCommandInfo & info)
 {
-    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, 0);
+    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
     return boost::spirit::parse( arguments.begin(), arguments.end(), 
                                  impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
                                  impl().grammar.use_parser<Impl::Grammar::SkipParser>()
         ).full;
 }
 
+struct senf::console::CommandParser::SetIncremental
+{
+    SetIncremental(CommandParser & parser) : parser_ (parser) {
+        parser_.impl().grammar.incremental = true;
+    }
+
+    ~SetIncremental() {
+        parser_.impl().grammar.incremental = false;
+    }
+
+    CommandParser & parser_;
+};
+
+prefix_ std::string::size_type
+senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
+{
+    SetIncremental si (*this);
+    return std::distance( commands.begin(), 
+                          parseLoop(commands.begin(), commands.end(), cb) );
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::SyntaxErrorException
 
index fcce862..322fc44 100644 (file)
@@ -170,6 +170,11 @@ prefix_ void senf::console::ParseCommandInfo::clear()
     tokens_.clear();
 }
 
+prefix_ bool senf::console::ParseCommandInfo::empty()
+{
+    return builtin_ == NoBuiltin && commandPath_.empty();
+}
+
 prefix_ void senf::console::ParseCommandInfo::builtin(BuiltinCommand builtin)
 {
     builtin_ = builtin;
index c246ce1..5653566 100644 (file)
@@ -377,6 +377,7 @@ namespace console {
                                              single range not divided into separate arguments. */
 
         void clear();                   ///< Clear all data members
+        bool empty();                   ///< \c true, if the data is empty
 
         void builtin(BuiltinCommand builtin); ///< Assign builtin command 
         void command(std::vector<Token> & commandPath); ///< Assign non-builtin command
@@ -595,24 +596,41 @@ namespace console {
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
-        bool parse(std::string command, Callback cb); ///< Parse string
-        bool parseFile(std::string filename, Callback cb); ///< Parse file
+        bool parse(std::string const & command, Callback cb); ///< Parse string
+        bool parseFile(std::string const & filename, Callback cb); ///< Parse file
                                         /**< \throws SystemException if the file cannot be
                                              read. */
 
-        bool parseArguments(std::string arguments, ParseCommandInfo & info);
+        bool parseArguments(std::string const & arguments, ParseCommandInfo & info);
                                         ///< Parse \a argumtns
                                         /**< parseArguments() parses the string \a arguments which
                                              contains arbitrary command arguments (without the name
                                              of the command). The argument tokens are written into
                                              \a info. */
 
+        std::string::size_type parseIncremental(std::string const & commands, Callback cb);
+                                        ///< Incremental parse
+                                        /**< An incremental parse will parse all complete statements
+                                             in \a commands. parseIncremental() will return the
+                                             number of characters successfully parsed from \a
+                                             commands. 
+                                             
+                                             \note The incremental parser \e requires all statements
+                                                 to be terminated explicitly. This means, that the
+                                                 last ';' is \e not optional in this case. */
+
     private:
         struct Impl;
+        struct SetIncremental;
+
+        template <class Iterator>
+        Iterator parseLoop(Iterator b, Iterator e, Callback cb);
 
         Impl & impl();
 
         boost::scoped_ptr<Impl> impl_;
+
+        friend class SetIncremental;
     };
 
 }}
index ae9a193..56628c9 100644 (file)
@@ -32,6 +32,8 @@
 #include <boost/spirit.hpp>
 #include <boost/spirit/utility/grammar_def.hpp>
 #include <boost/spirit/actor.hpp>
+#include <boost/spirit/dynamic.hpp>
+#include <boost/spirit/phoenix.hpp>
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
 #include <boost/ref.hpp>
@@ -99,6 +101,11 @@ namespace detail {
         Context & context;
 
         ///////////////////////////////////////////////////////////////////////////
+        // Configuration
+
+        bool incremental;
+
+        ///////////////////////////////////////////////////////////////////////////
         // Dispatching semantic actions
 
         ParseDispatcher & dispatcher;
@@ -169,7 +176,7 @@ namespace detail {
         ///////////////////////////////////////////////////////////////////////////
 
         CommandGrammar(ParseDispatcher & d, Context & c) 
-            : context(c), dispatcher(d) {}
+            : context(c), incremental(false), dispatcher(d) {}
 
         template <class Scanner>
         struct definition 
@@ -179,7 +186,8 @@ namespace detail {
         {
             boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token,
                 punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, 
-                skip, commands, block, statement, relpath, abspath, arguments;
+                skip, statement, relpath, abspath, arguments, group_start, group_close, 
+                statement_end;
             boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p;
             boost::spirit::distinct_parser<> keyword_p;
 
@@ -209,6 +217,7 @@ namespace detail {
 
             {
                 using namespace boost::spirit;
+                using namespace phoenix;
                 typedef ParseDispatcher PD;
                 typedef Token AT;
 
@@ -218,11 +227,13 @@ namespace detail {
                 // Syntax summary:
                 // This is EBNF with some minor tweaks to accommodate C++ syntax
                 //
-                //   * and +    like EBNF but they precede their argument
-                //   >>         is followed by
-                //   !          optional
-                //   a % b      match any number of a's separated by b
-                //   a - b      match a but not b
+                //   * a        any number of a's
+                //   + a        at least one a
+                //   ! a        an optional a
+                //   a >> b     a followed by b
+                //   a | b      a or b
+                //   a % b      any number of a's separated by b's
+                //   a - b      a but not b
                 //
                 // Beside this, we use some special parsers (ch_p, eps_p, confix_p, lex_escape_ch_p,
                 // keyword_p, comment_p) and directives (lexeme_d), however, the parser should be
@@ -248,13 +259,10 @@ namespace detail {
                 //
                 // More info is in the Boost.Spirit documentation
 
-                commands
-                    =  * command
-                    ;
-
                 command 
-                    =    builtin >> (ch_p(';') | end_p)
-                    |    path  >> ( block | statement )
+                    =    builtin >> statement_end
+                    |    path >> ( group_start | statement )
+                    |    group_close
                     |    ch_p(';') // Ignore empty commands
                     ;
 
@@ -275,19 +283,20 @@ namespace detail {
                                                                   boost::ref(self.context.path)) ]
                     ;
 
-                block
+                group_start
                     =    ch_p('{')                [ self.dispatch(&PD::pushDirectory,
                                                                   boost::ref(self.context.path)) ]
-                      >> * command 
-                      >> ch_p('}')                [ self.dispatch(&PD::popDirectory) ]
+                    ;
+
+                group_close
+                    =    ch_p('}')                [ self.dispatch(&PD::popDirectory) ]
                     ;
 
                 statement
-                    = eps_p                       [ self.dispatch(&PD::beginCommand, 
+                    =    eps_p                    [ self.dispatch(&PD::beginCommand, 
                                                                   boost::ref(self.context.path)) ]
                       >> arguments
-                      >> (ch_p(';') | end_p)
-                      >> eps_p                    [ self.dispatch(&PD::endCommand) ]
+                      >> statement_end            [ self.dispatch(&PD::endCommand) ]
                     ;
 
                 arguments
@@ -378,8 +387,7 @@ namespace detail {
                 word                    // Returns value in context.token
                     =    lexeme_d
                          [
-                             eps_p
-                          >> (+ word_p)           [ assign_a(self.context.str) ]
+                             (+ word_p)           [ assign_a(self.context.str) ]
                          ]
                       >> eps_p                    [ self.set_token_a(AT::Word, self.context.str) ]
                     ;
@@ -389,6 +397,16 @@ namespace detail {
                                                   [ append_a(self.context.str) ]
                     ;
 
+                statement_end
+                    =    if_p(var(self.incremental)) [
+                               ch_p(';')
+                         ]
+                         .else_p [
+                               ch_p(';') 
+                             | end_p
+                         ]
+                    ;
+
                 skip
                     =    space_p | comment_p('#')
                     ;
@@ -396,7 +414,7 @@ namespace detail {
                 ///////////////////////////////////////////////////////////////////
 
                 start_parsers(
-                    commands,           // CommandParser
+                    command,            // CommandParser
                     skip,               // SkipParser
                     arguments           // ArgumentsParser
                 );
index c58a6cc..c650745 100644 (file)
@@ -82,60 +82,104 @@ BOOST_AUTO_UNIT_TEST(commandGrammar)
     typedef senf::console::detail::CommandGrammar<TestParseDispatcher> Grammar;
     Grammar grammar (dispatcher, context);
 
-    char text[] = 
-        "# Comment\n"
-        "doo / bii / doo arg"
-        "                flab::blub"
-        "                123.434>a"
-        "                (a,b;c (huhu/{haha}))"
-        "                \"foo\\\"bar\" #\n"
-        "                x\"01 02 # Inner comment\n"
-        "                   0304\";"
-        "ls /foo/bar;"
-        "cd /foo/bar;"
-        "exit;"
-        "foo/bar/ { ls; }"
-        "help /foo/bar";
-
-    BOOST_CHECK( boost::spirit::parse( 
-                     text, 
-                     grammar.use_parser<Grammar::CommandParser>(), 
-                     grammar.use_parser<Grammar::SkipParser>() ) . full );
-    BOOST_CHECK_EQUAL( ss.str(), 
-                       "beginCommand( Word('doo')/Word('bii')/Word('doo') )\n"
-                       "pushToken( Word('arg') )\n"
-                       "pushToken( Word('flab::blub') )\n"
-                       "pushToken( Word('123.434>a') )\n"
-                       "pushToken( ArgumentGroupOpen('(') )\n"
-                       "pushToken( Word('a') )\n"
-                       "pushToken( OtherPunctuation(',') )\n"
-                       "pushToken( Word('b') )\n"
-                       "pushToken( CommandTerminator(';') )\n"
-                       "pushToken( Word('c') )\n"
-                       "pushToken( ArgumentGroupOpen('(') )\n"
-                       "pushToken( Word('huhu') )\n"
-                       "pushToken( PathSeparator('/') )\n"
-                       "pushToken( DirectoryGroupOpen('{') )\n"
-                       "pushToken( Word('haha') )\n"
-                       "pushToken( DirectoryGroupClose('}') )\n"
-                       "pushToken( ArgumentGroupClose(')') )\n"
-                       "pushToken( ArgumentGroupClose(')') )\n"
-                       "pushToken( BasicString('foo\"bar') )\n"
-                       "pushToken( HexString('\x01\x02\x03\x04') )\n"
-                       "endCommand()\n"
-                       "builtin_ls( None('')/Word('foo')/Word('bar') )\n"
-                       "builtin_cd( None('')/Word('foo')/Word('bar') )\n"
-                       "builtin_exit()\n"
-                       "pushDirectory( Word('foo')/Word('bar')/None('') )\n"
-                       "builtin_ls(  )\n"
-                       "popDirectory()\n"
-                       "builtin_help( None('')/Word('foo')/Word('bar') )\n" );
+    {
+        static char text[] = 
+            "# Comment\n"
+            "doo / bii / doo arg"
+            "                flab::blub"
+            "                123.434>a"
+            "                (a,b;c (huhu/{haha}))"
+            "                \"foo\\\"bar\" #\n"
+            "                x\"01 02 # Inner comment\n"
+            "                   0304\";";
+
+        BOOST_CHECK( boost::spirit::parse( 
+                         text, 
+                         grammar.use_parser<Grammar::CommandParser>(), 
+                         grammar.use_parser<Grammar::SkipParser>() ) . full );
+        BOOST_CHECK_EQUAL( ss.str(), 
+                           "beginCommand( Word('doo')/Word('bii')/Word('doo') )\n"
+                           "pushToken( Word('arg') )\n"
+                           "pushToken( Word('flab::blub') )\n"
+                           "pushToken( Word('123.434>a') )\n"
+                           "pushToken( ArgumentGroupOpen('(') )\n"
+                           "pushToken( Word('a') )\n"
+                           "pushToken( OtherPunctuation(',') )\n"
+                           "pushToken( Word('b') )\n"
+                           "pushToken( CommandTerminator(';') )\n"
+                           "pushToken( Word('c') )\n"
+                           "pushToken( ArgumentGroupOpen('(') )\n"
+                           "pushToken( Word('huhu') )\n"
+                           "pushToken( PathSeparator('/') )\n"
+                           "pushToken( DirectoryGroupOpen('{') )\n"
+                           "pushToken( Word('haha') )\n"
+                           "pushToken( DirectoryGroupClose('}') )\n"
+                           "pushToken( ArgumentGroupClose(')') )\n"
+                           "pushToken( ArgumentGroupClose(')') )\n"
+                           "pushToken( BasicString('foo\"bar') )\n"
+                           "pushToken( HexString('\x01\x02\x03\x04') )\n"
+                           "endCommand()\n" );
+    }
+
+    {
+        ss.str("");
+        BOOST_CHECK( boost::spirit::parse( 
+                         "ls /foo/bar;", 
+                         grammar.use_parser<Grammar::CommandParser>(), 
+                         grammar.use_parser<Grammar::SkipParser>() ) . full );
+        BOOST_CHECK_EQUAL( ss.str(), "builtin_ls( None('')/Word('foo')/Word('bar') )\n" );
+    }
+
+    {
+        ss.str("");
+        BOOST_CHECK( boost::spirit::parse( 
+                         "cd /foo/bar;", 
+                         grammar.use_parser<Grammar::CommandParser>(), 
+                         grammar.use_parser<Grammar::SkipParser>() ) . full );
+        BOOST_CHECK_EQUAL( ss.str(), "builtin_cd( None('')/Word('foo')/Word('bar') )\n" );
+    }
+
+    {
+        ss.str("");
+        BOOST_CHECK( boost::spirit::parse( 
+                         "exit;", 
+                         grammar.use_parser<Grammar::CommandParser>(), 
+                         grammar.use_parser<Grammar::SkipParser>() ) . full );
+        BOOST_CHECK_EQUAL( ss.str(), "builtin_exit()\n" );
+    }
+
+    {
+        ss.str("");
+        BOOST_CHECK( boost::spirit::parse( 
+                         "foo/bar/ {", 
+                         grammar.use_parser<Grammar::CommandParser>(), 
+                         grammar.use_parser<Grammar::SkipParser>() ) . full );
+        BOOST_CHECK_EQUAL( ss.str(), "pushDirectory( Word('foo')/Word('bar')/None('') )\n" );
+    }
+
+    {
+        ss.str("");
+        BOOST_CHECK( boost::spirit::parse( 
+                         "}", 
+                         grammar.use_parser<Grammar::CommandParser>(), 
+                         grammar.use_parser<Grammar::SkipParser>() ) . full );
+        BOOST_CHECK_EQUAL( ss.str(), "popDirectory()\n" );
+    }
+
+    {
+        ss.str("");
+        BOOST_CHECK( boost::spirit::parse( 
+                         "help /foo/bar", 
+                         grammar.use_parser<Grammar::CommandParser>(), 
+                         grammar.use_parser<Grammar::SkipParser>() ) . full );
+        BOOST_CHECK_EQUAL( ss.str(), "builtin_help( None('')/Word('foo')/Word('bar') )\n" );
+    }
 }
 
 namespace {
-    senf::console::ParseCommandInfo info;
+    std::vector<senf::console::ParseCommandInfo> commands;
     void setInfo(senf::console::ParseCommandInfo const & i)
-    { info = i; }
+    { commands.push_back(i); }
 }
 
 BOOST_AUTO_UNIT_TEST(commandParser)
@@ -150,60 +194,68 @@ BOOST_AUTO_UNIT_TEST(commandParser)
         "                (a,b,c (huhu))"
         "                \"foo\\\"bar\" #\n"
         "                x\"01 02 # Inner comment\n"
-        "                   0304\"";
+        "                   0304\";"
+        "ls /foo/bar; ";
 
     BOOST_CHECK( parser.parse(text, &setInfo) );
+    BOOST_CHECK_EQUAL( commands.size(), 2u );
 
-    senf::console::Token path[] = { 
-        senf::console::Token(senf::console::Token::Word, "doo"), 
-        senf::console::Token(senf::console::Token::Word, "bii"),
-        senf::console::Token(senf::console::Token::Word, "doo")
-    };
+    {
+        senf::console::ParseCommandInfo const & info (commands.front());
 
-    BOOST_CHECK_EQUAL_COLLECTIONS( info.commandPath().begin(), info.commandPath().end(),
-                                   path, path + sizeof(path)/sizeof(path[0]) );
-    BOOST_CHECK_EQUAL( info.tokens().size(), 15u );
-
-    char const * tokens[] = { "arg", 
-                              "flab::blub", 
-                              "123.434>a", 
-                              "(", "a", ",", "b", ",", "c", "(", "huhu", ")", ")",
-                              "foo\"bar",
-                              "\x01\x02\x03\x04" };
-
-    senf::console::ParseCommandInfo::argument_iterator args (info.arguments().begin());
-    BOOST_REQUIRE( args != info.arguments().end() );
-    BOOST_REQUIRE_EQUAL( args->size(), 1u );
-    BOOST_CHECK_EQUAL( args->begin()->value(), tokens[0] );
-
-    ++ args;
-    BOOST_REQUIRE( args != info.arguments().end() );
-    BOOST_REQUIRE_EQUAL( args->size(), 1u );
-    BOOST_CHECK_EQUAL( args->begin()->value(), tokens[1] );
-                         
-    ++ args;
-    BOOST_REQUIRE( args != info.arguments().end() );
-    BOOST_REQUIRE_EQUAL( args->size(), 1u );
-    BOOST_CHECK_EQUAL( args->begin()->value(), tokens[2] );
-
-    ++ args;
-    BOOST_REQUIRE( args != info.arguments().end() );
-    BOOST_REQUIRE_EQUAL( args->size(), 8u );
-    for (unsigned i (0); i<8; ++i)
-        BOOST_CHECK_EQUAL( args->begin()[i].value(), tokens[4+i] );
-
-    ++ args;
-    BOOST_REQUIRE( args != info.arguments().end() );
-    BOOST_REQUIRE_EQUAL( args->size(), 1u );
-    BOOST_CHECK_EQUAL( args->begin()->value(), tokens[13] );
-
-    ++ args;
-    BOOST_REQUIRE( args != info.arguments().end() );
-    BOOST_REQUIRE_EQUAL( args->size(), 1u );
-    BOOST_CHECK_EQUAL( args->begin()->value(), tokens[14] );
-
-    ++ args;
-    BOOST_CHECK( args == info.arguments().end() );
+        senf::console::Token path[] = { 
+            senf::console::Token(senf::console::Token::Word, "doo"), 
+            senf::console::Token(senf::console::Token::Word, "bii"),
+            senf::console::Token(senf::console::Token::Word, "doo")
+        };
+
+        BOOST_CHECK_EQUAL_COLLECTIONS( info.commandPath().begin(), info.commandPath().end(),
+                                       path, path + sizeof(path)/sizeof(path[0]) );
+        BOOST_CHECK_EQUAL( info.tokens().size(), 15u );
+        
+        char const * tokens[] = { "arg", 
+                                  "flab::blub", 
+                                  "123.434>a", 
+                                  "(", "a", ",", "b", ",", "c", "(", "huhu", ")", ")",
+                                  "foo\"bar",
+                                  "\x01\x02\x03\x04" };
+
+        senf::console::ParseCommandInfo::argument_iterator args (info.arguments().begin());
+        BOOST_REQUIRE( args != info.arguments().end() );
+        BOOST_REQUIRE_EQUAL( args->size(), 1u );
+        BOOST_CHECK_EQUAL( args->begin()->value(), tokens[0] );
+        
+        ++ args;
+        BOOST_REQUIRE( args != info.arguments().end() );
+        BOOST_REQUIRE_EQUAL( args->size(), 1u );
+        BOOST_CHECK_EQUAL( args->begin()->value(), tokens[1] );
+        
+        ++ args;
+        BOOST_REQUIRE( args != info.arguments().end() );
+        BOOST_REQUIRE_EQUAL( args->size(), 1u );
+        BOOST_CHECK_EQUAL( args->begin()->value(), tokens[2] );
+        
+        ++ args;
+        BOOST_REQUIRE( args != info.arguments().end() );
+        BOOST_REQUIRE_EQUAL( args->size(), 8u );
+        for (unsigned i (0); i<8; ++i)
+            BOOST_CHECK_EQUAL( args->begin()[i].value(), tokens[4+i] );
+        
+        ++ args;
+        BOOST_REQUIRE( args != info.arguments().end() );
+        BOOST_REQUIRE_EQUAL( args->size(), 1u );
+        BOOST_CHECK_EQUAL( args->begin()->value(), tokens[13] );
+        
+        ++ args;
+        BOOST_REQUIRE( args != info.arguments().end() );
+        BOOST_REQUIRE_EQUAL( args->size(), 1u );
+        BOOST_CHECK_EQUAL( args->begin()->value(), tokens[14] );
+        
+        ++ args;
+        BOOST_CHECK( args == info.arguments().end() );
+    }
+
+    commands.clear();
 }
 
 namespace {
@@ -220,17 +272,19 @@ BOOST_AUTO_UNIT_TEST(checkedArgumentIterator)
     senf::console::CommandParser parser;
 
     BOOST_CHECK( parser.parse("foo a", &setInfo) );
-    BOOST_CHECK_THROW( parseArgs(info.arguments()), senf::console::SyntaxErrorException );
+    BOOST_CHECK_THROW( parseArgs(commands.back().arguments()), 
+                       senf::console::SyntaxErrorException );
 
     BOOST_CHECK( parser.parse("foo a b", &setInfo) );
-    BOOST_CHECK_NO_THROW( parseArgs(info.arguments()) );
+    BOOST_CHECK_NO_THROW( parseArgs(commands.back().arguments()) );
 
     BOOST_CHECK( parser.parse("foo a b c", &setInfo) );
-    BOOST_CHECK_THROW( parseArgs(info.arguments()), senf::console::SyntaxErrorException );
+    BOOST_CHECK_THROW( parseArgs(commands.back().arguments()), 
+                       senf::console::SyntaxErrorException );
     
-    senf::console::CheckedArgumentIteratorWrapper arg (info.arguments());
-    BOOST_CHECK( arg == info.arguments().begin() );
-    BOOST_CHECK( arg != info.arguments().end() );
+    senf::console::CheckedArgumentIteratorWrapper arg (commands.back().arguments());
+    BOOST_CHECK( arg == commands.back().arguments().begin() );
+    BOOST_CHECK( arg != commands.back().arguments().end() );
     BOOST_CHECK( arg );
     ++ arg;
     BOOST_CHECK( arg );
@@ -238,7 +292,23 @@ BOOST_AUTO_UNIT_TEST(checkedArgumentIterator)
     BOOST_CHECK( arg.done() );
 
     senf::console::ParseCommandInfo::ArgumentIterator i (arg);
-    BOOST_CHECK( i == info.arguments().end() );
+    BOOST_CHECK( i == commands.back().arguments().end() );
+
+    commands.clear();
+}
+
+BOOST_AUTO_UNIT_TEST(parseIncremental)
+{
+    senf::console::CommandParser parser;
+
+    BOOST_CHECK_EQUAL( parser.parseIncremental("foo a", &setInfo), 0u );
+    BOOST_CHECK_EQUAL( parser.parseIncremental("foo a; cd", &setInfo), 7u );
+    BOOST_CHECK_EQUAL( parser.parseIncremental("foo a; cd /bar", &setInfo), 7u );
+    BOOST_CHECK_EQUAL( parser.parseIncremental("foo a; cd /bar; ", &setInfo), 16u );
+    BOOST_CHECK_EQUAL( parser.parseIncremental(" ", &setInfo), 1u );
+    BOOST_CHECK_EQUAL( commands.size(), 4u );
+
+    commands.clear();
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index da2842c..200da7c 100644 (file)
@@ -53,8 +53,12 @@ prefix_ int senf::console::detail::ReadlineClientReader::getc()
 
 prefix_ void senf::console::detail::ReadlineClientReader::write(std::string text)
 {
-    translate(text);
-    handle().write(text);
+    try {
+        translate(text);
+        handle().write(text);
+    } catch (SystemException &) {
+        ;
+    }
 }
 
 prefix_ void senf::console::detail::ReadlineClientReader::terminate()
index eecc1d6..310e341 100644 (file)
@@ -103,7 +103,7 @@ prefix_ senf::console::Server & senf::console::Server::start(ServerHandle handle
 }
 
 prefix_ senf::console::Server::Server(ServerHandle handle)
-    : handle_ (handle)
+    : handle_ (handle), mode_ (Automatic)
 {
     Scheduler::instance().add( handle_, senf::membind(&Server::newClient, this) );
 }
@@ -116,7 +116,7 @@ prefix_ senf::console::Server::~Server()
 prefix_ void senf::console::Server::newClient(Scheduler::EventId event)
 {
     ServerHandle::ClientSocketHandle client (handle_.accept());
-    boost::intrusive_ptr<Client> p (new Client(*this, client, name_));
+    boost::intrusive_ptr<Client> p (new Client(*this, client));
     clients_.insert( p );
     SENF_LOG(( "Registered new client " << p.get() ));
 }
@@ -190,16 +190,85 @@ prefix_ void senf::console::detail::DumbClientReader::v_translate(std::string &
 {}
 
 ///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::NoninteractiveClientReader
+
+prefix_
+senf::console::detail::NoninteractiveClientReader::NoninteractiveClientReader(Client & client)
+    : ClientReader (client), binding_ (handle(),
+                                       senf::membind(&NoninteractiveClientReader::newData, this),
+                                       senf::Scheduler::EV_READ)
+{}
+
+prefix_ void senf::console::detail::NoninteractiveClientReader::v_disablePrompt()
+{}
+
+prefix_ void senf::console::detail::NoninteractiveClientReader::v_enablePrompt()
+{}
+
+prefix_ void senf::console::detail::NoninteractiveClientReader::v_translate(std::string & data)
+{}
+
+prefix_ void
+senf::console::detail::NoninteractiveClientReader::newData(senf::Scheduler::EventId event)
+{
+    if (event != senf::Scheduler::EV_READ || handle().eof()) {
+        if (! buffer_.empty())
+            handleInput(buffer_);
+        stopClient();
+        return;
+    }
+
+    std::string::size_type n (buffer_.size());
+    buffer_.resize(n + handle().available());
+    buffer_.erase(handle().read(boost::make_iterator_range(buffer_.begin()+n, buffer_.end())),
+                  buffer_.end());
+    buffer_.erase(0, handleInput(buffer_, true));
+    stream() << std::flush;
+}
+
+///////////////////////////////////////////////////////////////////////////
 // senf::console::Client
 
-prefix_ senf::console::Client::Client(Server & server, ClientHandle handle,
-                                      std::string const & name)
+prefix_ senf::console::Client::Client(Server & server, ClientHandle handle)
     : out_t(boost::ref(*this)), senf::log::IOStreamTarget(out_t::member), server_ (server),
-      handle_ (handle), name_ (name), reader_ (new detail::SafeReadlineClientReader (*this))
+      handle_ (handle), 
+      binding_ (handle, boost::bind(&Client::setNoninteractive,this), Scheduler::EV_READ, false),
+      timer_ (Scheduler::instance().eventTime() + ClockService::milliseconds(INTERACTIVE_TIMEOUT),
+              boost::bind(&Client::setInteractive, this), false),
+      name_ (server.name()), reader_ (), mode_ (server.mode())
 {
-    executor_.autocd(true).autocomplete(true);
     handle_.facet<senf::TCPSocketProtocol>().nodelay();
-    // route< senf::SenfLog, senf::log::NOTICE >();
+    switch (mode_) {
+    case Server::Interactive :
+        setInteractive();
+        break;
+    case Server::Noninteractive :
+        setNoninteractive();
+        break;
+    case Server::Automatic :
+        binding_.enable();
+        timer_.enable();
+        break;
+    }
+}
+
+prefix_ void senf::console::Client::setInteractive()
+{
+    SENF_LOG(("Set client interactive"));
+    binding_.disable();
+    timer_.disable();
+    mode_ = Server::Interactive;
+    reader_.reset(new detail::SafeReadlineClientReader (*this));
+    executor_.autocd(true).autocomplete(true);
+}
+
+prefix_ void senf::console::Client::setNoninteractive()
+{
+    SENF_LOG(("Set client non-interactive"));
+    binding_.disable();
+    timer_.disable();
+    mode_ = Server::Noninteractive;
+    reader_.reset(new detail::NoninteractiveClientReader(*this));
 }
 
 prefix_ void senf::console::Client::translate(std::string & data)
@@ -207,17 +276,29 @@ prefix_ void senf::console::Client::translate(std::string & data)
     reader_->translate(data);
 }
 
-prefix_ void senf::console::Client::handleInput(std::string data)
+prefix_ std::string::size_type senf::console::Client::handleInput(std::string data,
+                                                                  bool incremental)
 {
-    if (data.empty())
+    SENF_LOG(("Data: " << data));
+    
+    if (data.empty() && ! incremental)
         data = lastCommand_;
     else
         lastCommand_ = data;
 
+    bool state (true);
+    std::string::size_type n (data.size());
+
     try {
-        if (! parser_.parse(data, boost::bind<void>( boost::ref(executor_),
-                                                     boost::ref(stream()),
-                                                     _1 )) )
+        if (incremental)
+            n = parser_.parseIncremental(data, boost::bind<void>( boost::ref(executor_),
+                                                                  boost::ref(stream()),
+                                                                  _1 ));
+        else
+            state = parser_.parse(data, boost::bind<void>( boost::ref(executor_),
+                                                           boost::ref(stream()),
+                                                           _1 ));
+        if (! state )
             stream() << "syntax error" << std::endl;
     }
     catch (Executor::ExitException &) {
@@ -226,7 +307,6 @@ prefix_ void senf::console::Client::handleInput(std::string data)
         // are called from the client reader callback and that will continue executing even if we
         // call stop here ...
         handle_.facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD);
-        return;
     }
     catch (std::exception & ex) {
         stream() << ex.what() << std::endl;
@@ -234,6 +314,7 @@ prefix_ void senf::console::Client::handleInput(std::string data)
     catch (...) {
         stream() << "unidentified error (unknown exception thrown)" << std::endl;
     }
+    return n;
 }
 
 prefix_ void senf::console::Client::v_write(senf::log::time_type timestamp,
@@ -254,13 +335,18 @@ prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Client const
     typedef ClientSocketHandle< MakeSocketPolicy<
         INet6AddressingPolicy,ConnectedCommunicationPolicy>::policy > V6Socket;
 
-    if (check_socket_cast<V4Socket>(client.handle()))
-        os << dynamic_socket_cast<V4Socket>(client.handle()).peer();
-    else if (check_socket_cast<V6Socket>(client.handle()))
-        os << dynamic_socket_cast<V6Socket>(client.handle()).peer();
-    else
-        os << static_cast<void const *>(&client);
-
+    try {
+        if (check_socket_cast<V4Socket>(client.handle()))
+            os << dynamic_socket_cast<V4Socket>(client.handle()).peer();
+        else if (check_socket_cast<V6Socket>(client.handle()))
+            os << dynamic_socket_cast<V6Socket>(client.handle()).peer();
+        else
+            os << static_cast<void const *>(&client);
+    }
+    catch (SystemException &) {
+        os << "0.0.0.0:0";
+    }
+        
     return os;
 }
 
index 427e8ea..405347c 100644 (file)
@@ -52,6 +52,30 @@ prefix_ senf::console::Server & senf::console::Server::name(std::string const &
     return *this;
 }
 
+prefix_ std::string const & senf::console::Server::name()
+    const
+{
+    return name_;
+}
+
+prefix_ senf::console::Server & senf::console::Server::root(DirectoryNode & root)
+{
+    root_ = root.thisptr();
+    return *this;
+}
+
+prefix_ senf::console::Server & senf::console::Server::mode(Mode m)
+{
+    mode_ = m;
+    return *this;
+}
+
+prefix_ senf::console::Server::Mode senf::console::Server::mode()
+    const
+{
+    return mode_;
+}
+
 prefix_ void senf::console::Server::stop()
 {
     // commit suicide
@@ -62,7 +86,9 @@ prefix_ void senf::console::Server::stop()
 // senf::console::Client
 
 prefix_ senf::console::Client::~Client()
-{}
+{
+    stream() << std::flush;
+}
 
 prefix_ void senf::console::Client::stop()
 {
@@ -82,6 +108,18 @@ prefix_ std::string senf::console::Client::promptString()
     return name_ + ":" + executor_.cwdPath() + "$ ";
 }
 
+prefix_ senf::console::DirectoryNode & senf::console::Client::root()
+    const
+{
+    return server_.root();
+}
+
+prefix_ senf::console::Server::Mode senf::console::Client::mode()
+    const
+{
+    return mode_;
+}
+
 prefix_ senf::console::Client & senf::console::Client::get(std::ostream & os)
 {
     return dynamic_cast<detail::NonblockingSocketOStream&>(os)->client();
@@ -133,10 +171,11 @@ prefix_ void senf::console::detail::ClientReader::stopClient()
     client().stop();
 }
 
-prefix_ void senf::console::detail::ClientReader::handleInput(std::string const & input)
+prefix_ std::string::size_type
+senf::console::detail::ClientReader::handleInput(std::string const & input, bool incremental)
     const
 {
-    client().handleInput(input);
+    return client().handleInput(input, incremental);
 }
 
 prefix_ void senf::console::detail::ClientReader::disablePrompt()
index 0216840..52925d0 100644 (file)
@@ -35,6 +35,8 @@
 #include "../Socket/Protocols/INet/TCPSocketHandle.hh"
 #include "../Socket/ServerSocketHandle.hh"
 #include "../Scheduler/Scheduler.hh"
+#include "../Scheduler/Binding.hh"
+#include "../Scheduler/Timer.hh"
 #include "../Scheduler/ReadHelper.hh"
 #include "Parse.hh"
 #include "Executor.hh"
@@ -80,17 +82,50 @@ namespace console {
         
         typedef detail::ServerHandle ServerHandle;
 
+        enum Mode { Automatic, Interactive, Noninteractive };
+
+        ///////////////////////////////////////////////////////////////////////////
+
         ~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
+
+        std::string const & name() const; ///< Get server name
+                                        /**< This information is used in the prompt string. */
+
         Server & name(std::string const & name); ///< Set server name
                                         /**< This information is used in the prompt string. */
+
+        DirectoryNode & root() const;   ///< Get root node
+
+        Server & root(DirectoryNode & root); ///< Set root node
+                                        /**< \a node will be the root node for all clients launched
+                                             from this server. */
+
+        Mode mode() const;              ///< Get mode
+                                        /**< \see \ref mode(Mode) */
         
+        Server & mode(Mode mode);       ///< Set mode
+                                        /**< There are two Server types: 
+                                             \li An interactive server displays a command prompt and
+                                                 optionally supports command-line editing.
+                                             \li A non-interactive server does not display any
+                                                 prompt and does not allow any interactive
+                                                 editing. This type of server is used for (remote)
+                                                 scripting.
+
+                                             The \a mode parameter selects between these modes. In
+                                             \c Automatic (the default), a client connection is
+                                             considered to be interactive if there is no data
+                                             traffic in the first 500ms after the connection is
+                                             opened. */
+
         void stop();                    ///< Stop the server
                                         /**< All clients will be closed */
+
         
     protected:
 
@@ -104,6 +139,8 @@ namespace console {
         void removeClient(Client & client);
         
         ServerHandle handle_;
+        DirectoryNode::ptr root_;
+        Mode mode_;
         
         typedef std::set< boost::intrusive_ptr<Client> > Clients;
         Clients clients_;
@@ -130,6 +167,8 @@ namespace console {
         SENF_LOG_CLASS_AREA();
         SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
 
+        static const unsigned INTERACTIVE_TIMEOUT = 500; // milliseconds;
+
     public:
         typedef Server::ServerHandle::ClientSocketHandle ClientHandle;
 
@@ -142,27 +181,35 @@ namespace console {
         ClientHandle handle() const;
         std::ostream & stream();
         std::string promptString() const;
+        DirectoryNode & root() const;
+        Server::Mode mode() const;
 
         static Client & get(std::ostream & os);
 
     protected:
         
     private:
-        Client(Server & server, ClientHandle handle, std::string const & name);
+        Client(Server & server, ClientHandle handle);
 
+        void setInteractive();
+        void setNoninteractive();
+        
         void translate(std::string & data);
-        void handleInput(std::string input);
+        unsigned handleInput(std::string input, bool incremental = false);
         virtual void v_write(senf::log::time_type timestamp, std::string const & stream, 
                              std::string const & area, unsigned level, 
                              std::string const & message);
         
         Server & server_;
         ClientHandle handle_;
+        SchedulerBinding binding_;
+        SchedulerTimer timer_;
         CommandParser parser_;
         Executor executor_;
         std::string name_;
         std::string lastCommand_;
         boost::scoped_ptr<detail::ClientReader> reader_;
+        Server::Mode mode_;
 
         friend class Server;
         friend class detail::ClientReader;
index 6bbaf5a..d31f713 100644 (file)
@@ -90,7 +90,7 @@ namespace detail {
         // Called by subclasses to perform actions in the Client
 
         void stopClient();
-        void handleInput(std::string const & input) const;
+        std::string::size_type handleInput(std::string const & input, bool incremental=false) const;
 
         // Called by the Client
 
@@ -132,6 +132,28 @@ namespace detail {
         unsigned promptLen_;
         bool promptActive_;
     };
+
+    /** \brief Internal: Primitive ClientReader implementation
+        
+        This implementation uses the cooked telnet mode to read lines from the console. It does not
+        support explicit line editing or any other advanced features.
+     */
+    class NoninteractiveClientReader
+        : public ClientReader
+    {
+    public:
+        NoninteractiveClientReader(Client & client);
+
+    private:
+        virtual void v_disablePrompt();
+        virtual void v_enablePrompt();
+        virtual void v_translate(std::string & data);
+
+        void newData(senf::Scheduler::EventId event);
+
+        SchedulerBinding binding_;
+        std::string buffer_;
+    };
     
 }}}
 
diff --git a/Scheduler/Timer.cci b/Scheduler/Timer.cci
new file mode 100644 (file)
index 0000000..dad761c
--- /dev/null
@@ -0,0 +1,88 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// 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.
+
+/** \file
+    \brief Timer inline non-template implementation */
+
+//#include "Timer.ih"
+
+// Custom includes
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+prefix_ senf::SchedulerTimer::SchedulerTimer(ClockService::clock_type timeout,
+                                             Scheduler::SimpleCallback const & cb,
+                                             bool enabled)
+    : timeout_ (timeout), cb_ (cb), 
+      id_ (enabled ? Scheduler::instance().timeout(timeout_, cb_) : 0), 
+      enabled_ (enabled)
+{}
+
+prefix_ void senf::SchedulerTimer::enable()
+{
+    if (!enabled_) {
+        id_ = Scheduler::instance().timeout(timeout_, cb_);
+        enabled_ = true;
+    }
+}
+
+prefix_ void senf::SchedulerTimer::disable()
+{
+    if (enabled_) {
+        Scheduler::instance().cancelTimeout(id_);
+        enabled_ = false;
+    }
+}
+
+prefix_ bool senf::SchedulerTimer::enabled()
+{
+    return enabled_;
+}
+
+prefix_ void senf::SchedulerTimer::update(ClockService::clock_type timeout)
+{
+    if (enabled_)
+        Scheduler::instance().cancelTimeout(id_);
+    timeout_ = timeout;
+    if (enabled_)
+        Scheduler::instance().timeout(timeout_, cb_);
+}
+
+prefix_ senf::SchedulerTimer::~SchedulerTimer()
+{
+    disable();
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// 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"
+// End:
diff --git a/Scheduler/Timer.hh b/Scheduler/Timer.hh
new file mode 100644 (file)
index 0000000..93dbf43
--- /dev/null
@@ -0,0 +1,108 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// 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.
+
+/** \file
+    \brief Timer public header */
+
+#ifndef HH_Timer_
+#define HH_Timer_ 1
+
+// Custom includes
+#include "Scheduler.hh"
+
+//#include "Timer.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+
+    /** \brief Manage scheduler timer
+
+        This class will manage a single timer: The timer can be enabled, disabled and updated and
+        will automatically be removed, when this instance is destroyed.
+
+        \code
+        class Foo
+        {
+        public:
+            Foo() : timer_ ( ClockServer::now() + ClockService::milliseconds(500),
+                             senf::membind(&Foo::timer, this) ) {}
+
+            void blarf() { timer_.disable(); }
+        
+        private:
+            void timer(); 
+        
+            senf::SchedulerTimer timer_;
+        };
+        \endcode
+      */
+    class SchedulerTimer
+        : boost::noncopyable
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        SchedulerTimer(ClockService::clock_type timeout, Scheduler::SimpleCallback const & cb,
+                       bool enabled=true);
+        ~SchedulerTimer();
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+
+        void enable();                  ///< Enable timer
+        void disable();                 ///< Disable timer
+        bool enabled();                 ///< \c true, if timer is currently enabled
+                                        /**< An expired timer can still be in enabled state. */
+
+        void update(ClockService::clock_type timeout); ///< Change timeout time and enable timer
+                                        /**< If the timer is not enabled, you need to call enable()
+                                             for the timer to become effective. */
+
+    protected:
+
+    private:
+        ClockService::clock_type timeout_;
+        Scheduler::SimpleCallback cb_;
+        unsigned id_;
+        bool enabled_;
+    };
+
+}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "Timer.cci"
+//#include "Timer.ct"
+//#include "Timer.cti"
+#endif
+
+\f
+// 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"
+// End:
index 2d15dc9..ee23d52 100644 (file)
@@ -427,13 +427,13 @@ namespace senf {
         void state(SocketStateMap & map, unsigned lod=0);
         std::string dumpState(unsigned lod=0);
 
+        unsigned available();
+
     protected:
         ClientSocketHandle(FileHandle other, bool isChecked);
         explicit ClientSocketHandle(std::auto_ptr<SocketBody> body);
 
     private:
-        unsigned available();
-
         friend class senf::ServerSocketHandle<SPolicy>;
     };