Console: Added lots of unit-tests
g0dil [Fri, 28 Mar 2008 11:03:50 +0000 (11:03 +0000)]
Console: Added non-blocking client output stream
Console: Repeat last command on RET

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

12 files changed:
Console/Executor.test.cc [new file with mode: 0644]
Console/Node.ct
Console/Node.hh
Console/Node.test.cc
Console/ObjectDirectory.test.cc
Console/Parse.ih
Console/Parse.test.cc
Console/Server.cc
Console/Server.cci
Console/Server.hh
Console/Server.ih [new file with mode: 0644]
Console/testServer.cc

diff --git a/Console/Executor.test.cc b/Console/Executor.test.cc
new file mode 100644 (file)
index 0000000..902bc84
--- /dev/null
@@ -0,0 +1,178 @@
+// $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 Executor.test unit tests */
+
+//#include "Executor.test.hh"
+//#include "Executor.test.ih"
+
+// Custom includes
+#include <sstream>
+#include <vector>
+#include "Executor.hh"
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/test_tools.hpp>
+
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+namespace {
+    std::vector<senf::console::ParseCommandInfo> commands;
+    void setCommand(senf::console::ParseCommandInfo const & cmd) {
+        commands.push_back(cmd);
+    }
+    void testCommand(std::ostream & os, senf::console::Executor::Arguments) {
+        os << "testCommand\n";
+    }
+}
+
+BOOST_AUTO_UNIT_TEST(executor)
+{
+    senf::console::root().mkdir("dir1").mkdir("dir3");
+    senf::console::root().mkdir("dir2").doc("Helptext").add("test",&testCommand);
+
+    senf::console::Executor executor;
+    senf::console::CommandParser parser;
+
+    BOOST_CHECK( &executor.cwd() == &senf::console::root() );
+    
+    {
+        std::stringstream os;
+        parser.parse("cd dir1", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinCD );
+        BOOST_CHECK( &executor.cwd() == &senf::console::root()["dir1"] );
+        BOOST_CHECK_EQUAL( os.str(), "" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("cd /dir2", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinCD );
+        BOOST_CHECK( &executor.cwd() == &senf::console::root()["dir2"] );
+        BOOST_CHECK_EQUAL( os.str(), "" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("cd dir1", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinCD );
+        BOOST_CHECK( &executor.cwd() == &senf::console::root()["dir2"] );
+        BOOST_CHECK_EQUAL( os.str(), "invalid directory\n" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("cd /", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinCD );
+        BOOST_CHECK( &executor.cwd() == &senf::console::root() );
+        BOOST_CHECK_EQUAL( os.str(), "" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("ls", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinLS );
+        BOOST_CHECK_EQUAL( os.str(), "dir1/\ndir2/\n" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("ls dir1", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinLS );
+        BOOST_CHECK_EQUAL( os.str(), "dir3/\n" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("ls dir3", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinLS );
+        BOOST_CHECK_EQUAL( os.str(), "invalid directory\n" );
+    }
+    
+    {
+        std::stringstream os;
+        parser.parse("dir1/dir3 { }", &setCommand);
+        executor(commands.rbegin()[1], os);
+        BOOST_CHECK_EQUAL( commands.rbegin()[1].builtin(), senf::console::ParseCommandInfo::BuiltinPUSHD );
+        BOOST_CHECK( &executor.cwd() == &senf::console::root()["dir1"]["dir3"] );
+        BOOST_CHECK_EQUAL( os.str(), "" );
+    }
+
+    {
+        std::stringstream os;
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinPOPD );
+        BOOST_CHECK( &executor.cwd() == &senf::console::root() );
+        BOOST_CHECK_EQUAL( os.str(), "" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("exit", &setCommand);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinEXIT );
+        BOOST_CHECK_THROW( executor(commands.back(), os), senf::console::Executor::ExitException );
+        BOOST_CHECK_EQUAL( os.str(), "" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("help /dir2", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinHELP );
+        BOOST_CHECK_EQUAL( os.str(), "senf::console::DirectoryNode at /dir2\n\nHelptext\n" );
+    }
+
+    {
+        std::stringstream os;
+        parser.parse("dir2/test", &setCommand);
+        executor(commands.back(), os);
+        BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::NoBuiltin );
+        BOOST_CHECK_EQUAL( os.str(), "testCommand\n" );
+    }
+
+    commands.clear();
+    senf::console::root().remove("dir1");
+    senf::console::root().remove("dir2");
+}
+
+///////////////////////////////cc.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:
index 0a5a9c0..4244e69 100644 (file)
@@ -42,19 +42,19 @@ senf::console::DirectoryNode::traverse(ForwardRange const & range)
     DirectoryNode::ptr dir (thisptr());
     const_iterator i (boost::begin(range));
     const_iterator const i_end (boost::end(range));
-    if (i != i_end && i->empty()) {
+    if (i != i_end && *i == std::string("")) {
         dir = root().thisptr();
         ++ i;
     }
     while (i != i_end) {
         const_iterator next_i (i);
         ++ next_i;
-        if (*i == "..") {
+        if (*i == std::string("..")) {
             dir = dir->parent();
             if (! dir)
                 dir = root().thisptr();
         }
-        else if (! i->empty()  && *i != ".") {
+        else if (*i != std::string("") && *i != std::string(".")) {
             if (next_i == i_end)
                 return dir->get(*i);
             else {
index c3911ff..a1cd759 100644 (file)
@@ -370,14 +370,19 @@ namespace console {
         typedef boost::function<void (std::ostream &, Arguments const &)> Function;
 
         ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///\{
+
+        static ptr create(Function const & fn);
+
+        ///\}
+        ///////////////////////////////////////////////////////////////////////////
 
         virtual void operator()(std::ostream & output, Arguments const & arguments);
 
         ptr thisptr();
         cptr thisptr() const;
 
-        static ptr create(Function const & fn);
-
         SimpleCommandNode & doc(std::string const & doc);
 
     protected:
index 0c94afd..f5a2c02 100644 (file)
@@ -27,7 +27,9 @@
 //#include "Node.test.ih"
 
 // Custom includes
+#include <sstream>
 #include "Node.hh"
+#include <boost/iterator/transform_iterator.hpp>
 
 #include "../Utils/auto_unit_test.hh"
 #include <boost/test/test_tools.hpp>
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
-BOOST_AUTO_UNIT_TEST(node)
-{}
+BOOST_AUTO_UNIT_TEST(gnericNode)
+{
+    senf::console::GenericNode & node (
+        senf::console::root().mkdir("dir1").mkdir("dir2").doc("help info"));
+    senf::console::GenericNode::weak_ptr wp (node.thisptr());
+
+    BOOST_CHECK_EQUAL( node.name(), "dir2" );
+    BOOST_CHECK( node.parent() );
+    BOOST_CHECK_EQUAL( node.path(), "/dir1/dir2" );
+    BOOST_CHECK( node.active() );
+    std::stringstream ss;
+    node.help(ss);
+    BOOST_CHECK_EQUAL( ss.str(), "help info\n" );
+    
+    {
+        senf::console::GenericNode::ptr p (senf::console::root()["dir1"].unlink());
+        BOOST_CHECK( ! node.active() );
+        BOOST_CHECK( ! wp.expired() );
+    }
+    BOOST_CHECK( wp.expired() );
+}
+
+namespace {
+    void callback(std::ostream & os, senf::console::SimpleCommandNode::Arguments arguments)
+    {
+        os << "callback";
+    }
+
+    template <class T>
+    struct select1st {
+        typedef T result_type;
+        template <class U> result_type operator()(U const & u) const { return u.first; }
+    };
+}
+
+BOOST_AUTO_UNIT_TEST(directoryNode)
+{
+    senf::console::DirectoryNode::ptr p (senf::console::DirectoryNode::create());
+
+    BOOST_CHECK( & senf::console::root().add("dir1", p) == p.get() );
+
+    senf::console::SimpleCommandNode & fnnode (
+        senf::console::root().add( "fn", senf::console::SimpleCommandNode::create(&callback) ));
+    BOOST_CHECK( &senf::console::root()["dir1"] == p.get() );
+    BOOST_CHECK_THROW( senf::console::root()["dir2"], senf::console::UnknownNodeNameException );
+    BOOST_CHECK_THROW( senf::console::root()("dir1"), std::bad_cast );
+    BOOST_CHECK( &senf::console::root()("fn") == &fnnode );
+    BOOST_CHECK_THROW( senf::console::root()("fn2"), senf::console::UnknownNodeNameException );
+    BOOST_CHECK_THROW( senf::console::root()["fn"], std::bad_cast );
+    BOOST_CHECK( &senf::console::root().get("dir1") == p.get() );
+    
+    senf::console::root().mkdir("dir2").mkdir("dir3");
+    char const * const children[] = { "dir1", "dir2", "fn" };
+    BOOST_CHECK_EQUAL_COLLECTIONS( 
+        boost::make_transform_iterator(senf::console::root().children().begin(), 
+                                       select1st<std::string const &>()),
+        boost::make_transform_iterator(senf::console::root().children().end(),
+                                       select1st<std::string const &>()),
+        children, 
+        children+sizeof(children)/sizeof(children[0]) );
+
+    char const * const path[] = { "..", "dir2", "dir3" };
+    BOOST_CHECK( &senf::console::root()["dir1"].traverse( boost::make_iterator_range(
+                                                              path, 
+                                                              path+sizeof(path)/sizeof(path[0])) )
+                 == &senf::console::root()["dir2"]["dir3"] );
+
+    p->doc("test doc");
+    std::stringstream ss;
+    p->help(ss);
+    BOOST_CHECK_EQUAL( ss.str(), "test doc\n" );
+
+    BOOST_CHECK( senf::console::root().remove("dir1") == p );
+    senf::console::root().remove("dir2");
+    senf::console::root().remove("fn");
+
+    BOOST_CHECK_EQUAL( senf::console::root().children().size(), 0u );
+}
+
+namespace {
+    struct Functor {
+        void operator()(std::ostream & os, 
+                        senf::console::SimpleCommandNode::Arguments const &) {
+            os << "functor";
+        }
+    };
+}
+
+BOOST_AUTO_UNIT_TEST(senfConsoleAddNode)
+{
+    senf::console::root().add("fn1", &callback);
+    senf::console::root().add("fn2", Functor());
+    
+    senf::console::ParseCommandInfo info;
+
+    {
+        std::stringstream ss;
+        senf::console::root()("fn1")(ss, info.arguments());
+        BOOST_CHECK_EQUAL( ss.str(), "callback" );
+    }
+
+    {
+        std::stringstream ss;
+        senf::console::root()("fn2")(ss, info.arguments());
+        BOOST_CHECK_EQUAL( ss.str(), "functor" );
+    }
+    
+    senf::console::root().remove("fn1");
+    senf::console::root().remove("fn2");
+}
+
+BOOST_AUTO_UNIT_TEST(simpleCommandNode)
+{
+    senf::console::root().add("fn", senf::console::SimpleCommandNode::create(&callback))
+        .doc("help text");
+    {
+        std::stringstream ss;
+        senf::console::ParseCommandInfo info;
+        senf::console::root()("fn")(ss, info.arguments());
+        BOOST_CHECK_EQUAL( ss.str(), "callback" );
+    }
+    
+    {
+        std::stringstream ss;
+        senf::console::root().get("fn").help(ss);
+        BOOST_CHECK_EQUAL( ss.str(), "help text\n" );
+    }
+
+    senf::console::root().remove("fn");
+}
 
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
index 1c5ac82..a8e99af 100644 (file)
@@ -27,6 +27,7 @@
 //#include "ObjectDirectory.test.ih"
 
 // Custom includes
+#include <sstream>
 #include "ObjectDirectory.hh"
 
 #include <boost/test/auto_unit_test.hpp>
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
-BOOST_AUTO_UNIT_TEST(ownerDirectory)
-{}
+namespace {
+    struct TestObject {
+        typedef TestObject Self;
+
+        senf::console::ObjectDirectory<Self> dir;
+        TestObject() : dir(this) {
+            dir.add("member", &Self::member);
+        }
+
+        void member(std::ostream & os, senf::console::CommandNode::Arguments const &) {
+            os << "member";
+        }
+    };
+}
+
+BOOST_AUTO_UNIT_TEST(objectDirectory)
+{
+    {
+        TestObject ob;
+        senf::console::root().add("ob",ob.dir);
+        std::stringstream ss;
+        senf::console::ParseCommandInfo info;
+        senf::console::root()["ob"]("member")(ss, info.arguments());
+        BOOST_CHECK_EQUAL( ss.str(), "member" );
+    }
+    BOOST_CHECK_THROW( senf::console::root()["ob"], senf::console::UnknownNodeNameException );
+}
 
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
index f676f10..0de5ade 100644 (file)
@@ -44,6 +44,9 @@ namespace detail {
 
 #ifndef DOXYGEN
 
+    ///////////////////////////////////////////////////////////////////////////
+    // append_a
+
     struct append_action
     {
         template <class T, class Value>
@@ -58,16 +61,15 @@ namespace detail {
     template <class T>
     inline boost::spirit::ref_value_actor<T, append_action> 
     append_a(T & ref)
-    {
-        return boost::spirit::ref_value_actor<T, append_action>(ref);
-    }
+    { return boost::spirit::ref_value_actor<T, append_action>(ref); }
     
     template <class T, class Value>
     inline boost::spirit::ref_const_ref_actor<T, Value, append_action> 
     append_a(T & ref, Value const & value)
-    {
-        return boost::spirit::ref_const_ref_actor<T, Value, append_action>(ref, value);
-    }
+    { return boost::spirit::ref_const_ref_actor<T, Value, append_action>(ref, value); }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Grammar
 
     template <class ParseDispatcher>
     struct CommandGrammar : boost::spirit::grammar<CommandGrammar<ParseDispatcher> >
@@ -165,7 +167,7 @@ namespace detail {
                 // Syntax summary:
                 // This is EBNF with some minor tweaks to accommodate C++ syntax
                 //
-                //   * and +    precede their argument
+                //   * and +    like EBNF but they precede their argument
                 //   >>         is followed by
                 //   !          optional
                 //   a % b      match any number of a's separated by b
@@ -190,7 +192,7 @@ namespace detail {
                 //
                 // Aligned to the right at column 50 are semantic actions.
                 //
-                // For clarity, I have used 'ch_p' explicitly throughout even though it is auxiliary
+                // For clarity, I have used 'ch_p' explicitly throughout even though it is optional
                 // in most cases.
                 //
                 // More info is in the Boost.Spirit documentation
@@ -351,6 +353,8 @@ namespace detail {
         };
     };
 
+
+
 #endif
 
 }}}
index 8c03817..491866a 100644 (file)
@@ -43,8 +43,6 @@
 
 namespace 
 {
-    
-
     struct TestParseDispatcher 
     {
         TestParseDispatcher(std::ostream & os) : os_ (os) {}
@@ -75,7 +73,7 @@ namespace
         void builtin_cd(std::vector<std::string> const & path)
             { os_ << "builtin_cd( " << senf::stringJoin(path, "/") << " )\n"; }
         void builtin_ls(std::vector<std::string> const & path)
-            { os_ << "builtin_cd( " << senf::stringJoin(path, "/") << " )\n"; }
+            { os_ << "builtin_ls( " << senf::stringJoin(path, "/") << " )\n"; }
         void builtin_exit()
             { os_ << "builtin_exit()\n"; }
         void builtin_help(std::vector<std::string> const & path)
@@ -100,7 +98,12 @@ BOOST_AUTO_UNIT_TEST(commandGrammar)
         "                (a,b,c (huhu))"
         "                \"foo\\\"bar\" #\n"
         "                x\"01 02 # Inner comment\n"
-        "                   0304\"";
+        "                   0304\";"
+        "ls /foo/bar;"
+        "cd /foo/bar;"
+        "exit;"
+        "foo/bar/ { ls; }"
+        "help /foo/bar";
 
     BOOST_CHECK( boost::spirit::parse( 
                      text, 
@@ -123,7 +126,14 @@ BOOST_AUTO_UNIT_TEST(commandGrammar)
                        "closeGroup()\n"
                        "pushArgument( foo\"bar )\n"
                        "pushArgument( \x01\x02\x03\x04 )\n"
-                       "endCommand()\n" );
+                       "endCommand()\n"
+                       "builtin_ls( /foo/bar )\n"
+                       "builtin_cd( /foo/bar )\n"
+                       "builtin_exit()\n"
+                       "pushDirectory( foo/bar/ )\n"
+                       "builtin_ls(  )\n"
+                       "popDirectory()\n"
+                       "builtin_help( /foo/bar )\n" );
 }
 
 namespace {
index eb1fac1..29f18cf 100644 (file)
@@ -24,7 +24,7 @@
     \brief Server non-inline non-template implementation */
 
 #include "Server.hh"
-//#include "Server.ih"
+#include "Server.ih"
 
 // Custom includes
 #include <unistd.h>
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::NonBlockingSocketSink
+
+prefix_ std::streamsize senf::console::detail::NonblockingSocketSink::write(const char * s,
+                                                                            std::streamsize n)
+{
+    try {
+        if (handle_.writeable()) 
+            handle_.write(s, s+n);
+    }
+    catch (SystemException & ex) {
+        ;
+    }
+    return n;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::Server
+
 prefix_ senf::console::Server &
 senf::console::Server::start(senf::INet4SocketAddress const & address)
 {
@@ -61,9 +80,6 @@ senf::console::Server::start(senf::INet6SocketAddress const & address)
     return *instance_;
 }
 
-///////////////////////////////////////////////////////////////////////////
-// senf::console::Server
-
 boost::scoped_ptr<senf::console::Server> senf::console::Server::instance_;
 
 prefix_ void senf::console::Server::start(ServerHandle handle)
@@ -102,7 +118,7 @@ prefix_ void senf::console::Server::removeClient(Client & client)
 // senf::console::Client
 
 prefix_ senf::console::Client::Client(ClientHandle handle, std::string const & name)
-    : out_t(::dup(handle.fd())), senf::log::IOStreamTarget(out_t::member),
+    : out_t(handle), senf::log::IOStreamTarget(out_t::member),
       handle_ (handle), name_ (name), promptLen_(0)
 {
     showPrompt();
@@ -133,6 +149,11 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr hel
     tail_ = helper->tail();
     boost::trim(data); // Gets rid of superfluous  \r or \n characters
 
+    if (data.empty())
+        data = lastCommand_;
+    else
+        lastCommand_ = data;
+
     try {
         if (! parser_.parse(data, boost::bind<void>(boost::ref(executor_), _1, 
                                                     boost::ref(out_t::member))))
index 12e0c00..d88e82f 100644 (file)
@@ -23,7 +23,7 @@
 /** \file
     \brief Server inline non-template implementation */
 
-//#include "Server.ih"
+#include "Server.ih"
 
 // Custom includes
 
 ///////////////////////////////cci.p///////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::NonblockingSocketSink
+
+prefix_ senf::console::detail::NonblockingSocketSink::NonblockingSocketSink(Handle handle)
+    : handle_ (handle)
+{}
+
+///////////////////////////////////////////////////////////////////////////
 // senf::console::Server
 
 prefix_ void senf::console::Server::name(std::string const & name)
index 26092fa..7bca747 100644 (file)
@@ -31,8 +31,6 @@
 #include <boost/utility.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/shared_ptr.hpp>
-#include <boost/iostreams/device/file_descriptor.hpp>
-#include <boost/iostreams/stream.hpp>
 #include "../Utils/intrusive_refcount.hh"
 #include "../Socket/Protocols/INet/TCPSocketHandle.hh"
 #include "../Socket/ServerSocketHandle.hh"
@@ -44,6 +42,7 @@
 #include "../Utils/Logger.hh"
 
 //#include "Server.mpp"
+#include "Server.ih"
 ///////////////////////////////hh.p////////////////////////////////////////
 
 namespace senf {
@@ -109,21 +108,16 @@ namespace console {
         and passes the commands to an Executor instance.
 
         \fixme Fix Client::clientData implementation
-        \fixme Remove the 'dup' needed here so we don't close the same fd twice (see Client
-            constructor)
-        \fixme Make output non-blocking (use a non-blocking/discarding streambuf) and possibly set
-            socket send buffer size
         \fixme Don't register a new ReadHelper every round
         \fixme Ensure, that output errors (or any errors) in the console don't terminate the
             application
      */
     class Client
         : public senf::intrusive_refcount, 
-          private boost::base_from_member< boost::iostreams::stream<boost::iostreams::file_descriptor_sink> >,
+          private boost::base_from_member< detail::NonblockingSocketOStream >,
           public senf::log::IOStreamTarget
     {
-        typedef boost::base_from_member< 
-            boost::iostreams::stream<boost::iostreams::file_descriptor_sink> > out_t;
+        typedef boost::base_from_member< detail::NonblockingSocketOStream > out_t;
 
         SENF_LOG_CLASS_AREA();
         SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
@@ -153,6 +147,7 @@ namespace console {
         Executor executor_;
         std::string name_;
         unsigned promptLen_;
+        std::string lastCommand_;
 
         friend class Server;
     };
diff --git a/Console/Server.ih b/Console/Server.ih
new file mode 100644 (file)
index 0000000..57e0f5c
--- /dev/null
@@ -0,0 +1,71 @@
+// $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 Server internal header */
+
+#ifndef IH_Server_
+#define IH_Server_ 1
+
+// Custom includes
+#include <boost/iostreams/concepts.hpp>
+#include <boost/iostreams/stream.hpp>
+
+///////////////////////////////ih.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+namespace detail {
+
+    class NonblockingSocketSink 
+        : public boost::iostreams::sink
+    {
+    public:
+        typedef ClientSocketHandle< 
+            MakeSocketPolicy<StreamFramingPolicy,
+                             WriteablePolicy,
+                             ConnectedCommunicationPolicy>::policy > Handle;
+
+        NonblockingSocketSink(Handle handle);
+        std::streamsize write(const char * s, std::streamsize n);
+        
+    private:
+        Handle handle_;
+    };
+
+    typedef boost::iostreams::stream<NonblockingSocketSink> NonblockingSocketOStream;
+
+}}}
+
+///////////////////////////////ih.e////////////////////////////////////////
+#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 8b3f3da..dc40117 100644 (file)
@@ -102,7 +102,7 @@ int main(int, char const **)
         .add("testob", test.dir)
         .doc("Example of an instance directory");
 
-    senf::console::Server::start( senf::INet4SocketAddress("127.0.0.1:23232") )
+    senf::console::Server::start( senf::INet4SocketAddress("0.0.0.0:23232") )
         .name("testServer");
 
     senf::Scheduler::instance().process();