--- /dev/null
+// $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:
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 {
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:
//#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_
//#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_
#ifndef DOXYGEN
+ ///////////////////////////////////////////////////////////////////////////
+ // append_a
+
struct append_action
{
template <class T, class Value>
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> >
// 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
//
// 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
};
};
+
+
#endif
}}}
namespace
{
-
-
struct TestParseDispatcher
{
TestParseDispatcher(std::ostream & os) : os_ (os) {}
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)
" (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,
"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 {
\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)
{
return *instance_;
}
-///////////////////////////////////////////////////////////////////////////
-// senf::console::Server
-
boost::scoped_ptr<senf::console::Server> senf::console::Server::instance_;
prefix_ void senf::console::Server::start(ServerHandle handle)
// 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();
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))))
/** \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)
#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"
#include "../Utils/Logger.hh"
//#include "Server.mpp"
+#include "Server.ih"
///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
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 );
Executor executor_;
std::string name_;
unsigned promptLen_;
+ std::string lastCommand_;
friend class Server;
};
--- /dev/null
+// $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:
.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();