From: g0dil Date: Thu, 20 Mar 2008 17:28:27 +0000 (+0000) Subject: Console: Add builtin parsing X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=412cf8e222086fb5d89b15cb11556799e131f390;p=senf.git Console: Add builtin parsing Console: Expose Server to libarary user git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@755 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Console/Executor.cc b/Console/Executor.cc index 0a9285a..3865656 100644 --- a/Console/Executor.cc +++ b/Console/Executor.cc @@ -39,7 +39,7 @@ prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & comman std::ostream & output) { # warning Implement Executor::operator() - SENF_LOG(( "Executing '" << command.commandPath() << "'" )); + SENF_LOG(( "Executing: " << command )); return true; } diff --git a/Console/Parse.cc b/Console/Parse.cc index 502dd1c..1f8a6b2 100644 --- a/Console/Parse.cc +++ b/Console/Parse.cc @@ -27,6 +27,7 @@ #include "Parse.ih" // Custom includes +#include "../Utils/String.hh" #include //#include "Parse.mpp" @@ -42,7 +43,10 @@ namespace detail { static void init(ParseCommandInfo & info) { info.init(); } - static void setCommand(ParseCommandInfo & info, std::string const & commandPath) + static void setBuiltin(ParseCommandInfo & info, ParseCommandInfo::BuiltinCommand builtin) + { info.setBuiltin(builtin); } + + static void setCommand(ParseCommandInfo & info, std::vector & commandPath) { info.setCommand(commandPath); } static void startArgument(ParseCommandInfo & info) @@ -82,7 +86,7 @@ namespace detail { ParseDispatcher & dispatcher; }; - void beginCommand(std::string const & command) + void beginCommand(std::vector & command) { ParserAccess::init(info()); ParserAccess::setCommand(info(), command); } @@ -105,6 +109,27 @@ namespace detail { void pushWord(std::string const & token) { ParserAccess::addToken(info(), ParserAccess::makeToken(token)); } + + void builtin_cd(std::vector & path) + { ParserAccess::init(info()); + ParserAccess::setBuiltin(info(), ParseCommandInfo::BuiltinCD); + setBuiltinPathArg(path); + ParserAccess::finalize(info()); } + + void builtin_ls(std::vector & path) + { ParserAccess::init(info()); + ParserAccess::setBuiltin(info(), ParseCommandInfo::BuiltinLS); + setBuiltinPathArg(path); + ParserAccess::finalize(info()); } + + void setBuiltinPathArg(std::vector & path) + { + ParserAccess::startArgument(info()); + for (std::vector::const_iterator i (path.begin()); + i != path.end(); ++i) + ParserAccess::addToken(info(), ParserAccess::makeToken(*i)); + ParserAccess::endArgument(info()); + } }; }}} @@ -139,15 +164,43 @@ prefix_ void senf::console::ParseCommandInfo::finalize() tempArguments_.clear(); } +prefix_ std::ostream & senf::console::operator<<(std::ostream & stream, + ParseCommandInfo const & info) +{ + if (info.builtin() == ParseCommandInfo::NoBuiltin) + stream << senf::stringJoin(info.commandPath(), "/"); + else { + char const * builtins[] = { "", "cd", "ls" }; + stream << "builtin-" << builtins[info.builtin()]; + } + + ParseCommandInfo::ArgumentsRange args (info.arguments()); + for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) { + ParseCommandInfo::token_iterator j (i->begin()); + stream << " ["; + if ( j != i->end() ) { + for (;;) { + stream << "'" << j->value() << "'"; + if ( ++j != i->end() ) stream << ' '; + else break; + } + } + stream << "]"; + } + + return stream; +} + /////////////////////////////////////////////////////////////////////////// // senf::console::SingleCommandParser struct senf::console::SingleCommandParser::Impl { + typedef detail::CommandGrammar Grammar; + detail::ParseDispatcher dispatcher; - detail::CommandGrammar::Context context; - detail::CommandGrammar grammar; - detail::SkipGrammar skipGrammar; + Grammar::Context context; + Grammar grammar; Impl() : dispatcher(), context(), grammar(dispatcher, context) {} }; @@ -163,7 +216,10 @@ prefix_ bool senf::console::SingleCommandParser::parseCommand(std::string comman ParseCommandInfo & info) { detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info); - return boost::spirit::parse( command.c_str(), impl().grammar, impl().skipGrammar ).full; + return boost::spirit::parse( command.c_str(), + impl().grammar.use_parser(), + impl().grammar.use_parser() + ).full; } ///////////////////////////////cc.e//////////////////////////////////////// diff --git a/Console/Parse.cci b/Console/Parse.cci index 1a72abf..0c7c0c6 100644 --- a/Console/Parse.cci +++ b/Console/Parse.cci @@ -48,50 +48,31 @@ prefix_ senf::console::ArgumentToken::ArgumentToken(std::string token) /////////////////////////////////////////////////////////////////////////// // senf::console::ParseCommandInfo -prefix_ std::string const & senf::console::ParseCommandInfo::commandPath() +prefix_ senf::console::ParseCommandInfo::BuiltinCommand +senf::console::ParseCommandInfo::builtin() const { - return commandPath_; + return builtin_; } -prefix_ senf::console::ParseCommandInfo::size_type senf::console::ParseCommandInfo::arguments() +prefix_ senf::console::ParseCommandInfo::CommandPathRange +senf::console::ParseCommandInfo::commandPath() const { - return arguments_.size(); + return boost::make_iterator_range(commandPath_.begin(), commandPath_.end()); } -prefix_ senf::console::ParseCommandInfo::argument_iterator -senf::console::ParseCommandInfo::begin_arguments() +prefix_ senf::console::ParseCommandInfo::ArgumentsRange +senf::console::ParseCommandInfo::arguments() const { - return arguments_.begin(); + return boost::make_iterator_range(arguments_.begin(), arguments_.end()); } -prefix_ senf::console::ParseCommandInfo::argument_iterator -senf::console::ParseCommandInfo::end_arguments() +prefix_ senf::console::ParseCommandInfo::TokensRange senf::console::ParseCommandInfo::tokens() const { - return arguments_.end(); -} - -prefix_ senf::console::ParseCommandInfo::size_type senf::console::ParseCommandInfo::tokens() - const -{ - return tokens_.size(); -} - -prefix_ senf::console::ParseCommandInfo::token_iterator -senf::console::ParseCommandInfo::begin_tokens() - const -{ - return tokens_.begin(); -} - -prefix_ senf::console::ParseCommandInfo::token_iterator -senf::console::ParseCommandInfo::end_tokens() - const -{ - return tokens_.end(); + return boost::make_iterator_range(tokens_.begin(), tokens_.end()); } //////////////////////////////////////// @@ -99,15 +80,23 @@ senf::console::ParseCommandInfo::end_tokens() prefix_ void senf::console::ParseCommandInfo::init() { - commandPath_ = ""; + builtin_ = NoBuiltin; + commandPath_.clear(); tokens_.clear(); arguments_.clear(); tempArguments_.clear(); } -prefix_ void senf::console::ParseCommandInfo::setCommand(std::string const & commandPath) +prefix_ void senf::console::ParseCommandInfo::setBuiltin(BuiltinCommand builtin) +{ + builtin_ = builtin; +} + +prefix_ void +senf::console::ParseCommandInfo::setCommand(std::vector & commandPath) { - commandPath_ = commandPath; + commandPath_.clear(); + commandPath_.swap(commandPath); } prefix_ void senf::console::ParseCommandInfo::startArgument() diff --git a/Console/Parse.hh b/Console/Parse.hh index 4a5d32a..3b059af 100644 --- a/Console/Parse.hh +++ b/Console/Parse.hh @@ -64,11 +64,14 @@ namespace console { class ParseCommandInfo { typedef std::vector Tokens; + typedef std::vector CommandPath; public: + typedef CommandPath::const_iterator path_iterator; typedef Tokens::const_iterator token_iterator; typedef boost::iterator_range argument_value_type; + private: typedef std::vector Arguments; @@ -76,21 +79,23 @@ namespace console { typedef Arguments::const_iterator argument_iterator; typedef Arguments::size_type size_type; - std::string const & commandPath() const; - - size_type arguments() const; - argument_iterator begin_arguments() const; - argument_iterator end_arguments() const; - - size_type tokens() const; - token_iterator begin_tokens() const; - token_iterator end_tokens() const; + typedef boost::iterator_range CommandPathRange; + typedef boost::iterator_range ArgumentsRange; + typedef boost::iterator_range TokensRange; + + enum BuiltinCommand { NoBuiltin, BuiltinCD, BuiltinLS }; + + BuiltinCommand builtin() const; + CommandPathRange commandPath() const; + ArgumentsRange arguments() const; + TokensRange tokens() const; protected: private: void init(); - void setCommand(std::string const & commandPath); + void setBuiltin(BuiltinCommand builtin); + void setCommand(std::vector & commandPath); void startArgument(); void endArgument(); void addToken(ArgumentToken const & token); @@ -98,11 +103,12 @@ namespace console { struct MakeRange; - std::string commandPath_; + std::vector commandPath_; typedef std::pair TempArgumentRange; typedef std::vector TempArguments; + BuiltinCommand builtin_; Tokens tokens_; Arguments arguments_; TempArguments tempArguments_; @@ -110,6 +116,8 @@ namespace console { friend class detail::ParserAccess; }; + std::ostream & operator<<(std::ostream & stream, ParseCommandInfo const & info); + /** \brief */ class SingleCommandParser diff --git a/Console/Parse.ih b/Console/Parse.ih index d5a9e2d..54c2c45 100644 --- a/Console/Parse.ih +++ b/Console/Parse.ih @@ -27,9 +27,10 @@ #define IH_Parse_ 1 // Custom includes +#include #include #include -#include +#include #include #include #include @@ -70,10 +71,16 @@ namespace detail { struct CommandGrammar : boost::spirit::grammar > { /////////////////////////////////////////////////////////////////////////// + // Start rules + + enum { CommandParser, SkipParser }; + + /////////////////////////////////////////////////////////////////////////// // The parse context (variables needed while parsing) struct Context { std::string str; + std::vector path; char ch; }; @@ -113,19 +120,61 @@ namespace detail { : context(c), dispatcher(d) {} template - struct definition + struct definition + : public boost::spirit::grammar_def< boost::spirit::rule, + boost::spirit::rule > { boost::spirit::rule command, path, argument, word, string, hexstring, token; boost::spirit::rule punctuation, hexbyte, balanced_tokens, simple_argument; - boost::spirit::rule complex_argument; + boost::spirit::rule complex_argument, builtin, skip; + + boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p; + + boost::spirit::distinct_parser<> keyword_p; + + definition(CommandGrammar const & self) : + + // Characters with a special meaning within the parser + // and characters reserved for future extensions + special_p ("/(){};"), - definition(CommandGrammar const & self) { + // Characters which are returned as punctuation tokens + punctuation_p (",="), + + // Whitespace characters (we don't want newlines in there) + space_p (" \t"), + + // Invalid characters: All chars below \x20 (space) which are not space_p + // (don't put a \0 in the chset<> argument *string* ...) + invalid_p ( + boost::spirit::chset<>('\0') | boost::spirit::chset<>("\x01-\x20") - space_p ), + + // Valid word characters + word_p ( + boost::spirit::anychar_p - special_p - punctuation_p - space_p - invalid_p), + + // Keywords must not be followed by a word char or '/' + keyword_p ( word_p | boost::spirit::ch_p('/') ) + + { using namespace boost::spirit; typedef ParseDispatcher PD; - + + builtin + = keyword_p("cd") + >> path + >> eps_p [ self.dispatch(&PD::builtin_cd, + boost::ref(self.context.path)) ] + | keyword_p("ls") + >> ! path + >> eps_p [ self.dispatch(&PD::builtin_cd, + boost::ref(self.context.path)) ] + ; + command - = path [ self.dispatch(&PD::beginCommand, - boost::cref(self.context.str)) ] + = builtin + | path [ self.dispatch(&PD::beginCommand, + boost::ref(self.context.path)) ] >> * argument >> ! ch_p(';') >> eps_p [ self.dispatch(&PD::endCommand) ] @@ -133,14 +182,14 @@ namespace detail { argument = simple_argument [ self.dispatch(&PD::pushArgument, - boost::cref(self.context.str)) ] + boost::ref(self.context.str)) ] | complex_argument ; simple_argument // All these return their value in context.str = string | hexstring - | path + | word ; complex_argument // Argument consists of multiple tokens @@ -168,13 +217,12 @@ namespace detail { >> confix_p( "x\"", * hexbyte, '"' ) ; - path // Returns value in context.str - = eps_p [ clear_a(self.context.str) ] - >> ( ! ch_p('/') [ append_a(self.context.str) ] - ) - >> ( word [ append_a(self.context.str) ] - % ch_p('/') [ append_a(self.context.str) ] - ) + path // Returns value in context.path + = eps_p [ clear_a(self.context.path) ] + >> ( ! ch_p('/') [ push_back_a(self.context.path, "") ] ) + >> ( word [ push_back_a(self.context.path) ] + % ch_p('/') ) + >> ( ! ch_p('/') [ push_back_a(self.context.path,"") ] ) ; balanced_tokens @@ -185,18 +233,18 @@ namespace detail { token = simple_argument [ self.dispatch(&PD::pushWord, - boost::cref(self.context.str)) ] + boost::ref(self.context.str)) ] | punctuation [ self.dispatch(&PD::pushPunctuation, - boost::cref(self.context.str)) ] + boost::ref(self.context.str)) ] | balanced_tokens ; punctuation // Returns value in context.str - = regex_p("[,=]") [ assign_a(self.context.str) ] + = punctuation_p [ assign_a(self.context.str) ] ; - word - = regex_p("[^ \t\n\r;,=(){}/\"]+") + word // Returns value in context.str + = lexeme_d[ + word_p ] [ assign_a(self.context.str) ] ; hexbyte @@ -204,40 +252,31 @@ namespace detail { [ append_a(self.context.str) ] ; - BOOST_SPIRIT_DEBUG_RULE(command); - BOOST_SPIRIT_DEBUG_RULE(path); - BOOST_SPIRIT_DEBUG_RULE(argument); - BOOST_SPIRIT_DEBUG_RULE(word); - BOOST_SPIRIT_DEBUG_RULE(string); - BOOST_SPIRIT_DEBUG_RULE(hexstring); - BOOST_SPIRIT_DEBUG_RULE(token); - BOOST_SPIRIT_DEBUG_RULE(punctuation); - BOOST_SPIRIT_DEBUG_RULE(hexbyte); - BOOST_SPIRIT_DEBUG_RULE(balanced_tokens); - BOOST_SPIRIT_DEBUG_RULE(simple_argument); - BOOST_SPIRIT_DEBUG_RULE(complex_argument); - } - - boost::spirit::rule const & start() const { return command; } - }; - }; + skip + = space_p + | comment_p('#') + ; - struct SkipGrammar - : public boost::spirit::grammar - { - template - struct definition - { - boost::spirit::rule rule; + BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(word,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(string,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(hexstring,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(token,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(punctuation,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(hexbyte,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(balanced_tokens,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(simple_argument,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(complex_argument,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1); + + start_parsers( + command, // CommandParser + skip // SkipParser + ); - definition(SkipGrammar const & self) { - rule - = boost::spirit::regex_p("[ \t]+") - | boost::spirit::comment_p('#') - ; } - - boost::spirit::rule const & start() const { return rule; } }; }; diff --git a/Console/Parse.test.cc b/Console/Parse.test.cc index a6d170b..11d8a38 100644 --- a/Console/Parse.test.cc +++ b/Console/Parse.test.cc @@ -26,10 +26,14 @@ //#include "Parse.test.hh" //#include "Parse.test.ih" +// #define BOOST_SPIRIT_DEBUG +// #define BOOST_SPIRIT_DEBUG_TRACENODE 0 + // Custom includes #include #include "Parse.hh" #include "Parse.ih" +#include "../Utils/String.hh" #include #include @@ -39,14 +43,16 @@ namespace { + + struct TestParseDispatcher { TestParseDispatcher(std::ostream & os) : os_ (os) {} std::ostream & os_; - void beginCommand(std::string const & command) - { os_ << "beginCommand( " << command << " )\n"; } + void beginCommand(std::vector const & command) + { os_ << "beginCommand( " << senf::stringJoin(command, "/") << " )\n"; } void endCommand() { os_ << "endCommand()\n"; } @@ -60,6 +66,11 @@ namespace { os_ << "pushPunctuation( " << token << " )\n"; } void pushWord(std::string const & token) { os_ << "pushWord( " << token << " )\n"; } + + void builtin_cd(std::vector const & path) + { os_ << "builtin_cd( " << senf::stringJoin(path, "/") << " )\n"; } + void builtin_ls(std::vector const & path) + { os_ << "builtin_cd( " << senf::stringJoin(path, "/") << " )\n"; } }; } @@ -68,12 +79,13 @@ BOOST_AUTO_UNIT_TEST(commandParser) senf::console::detail::CommandGrammar::Context context; std::stringstream ss; TestParseDispatcher dispatcher (ss); - senf::console::detail::CommandGrammar grammar (dispatcher, context); - senf::console::detail::SkipGrammar skipGrammar; + + typedef senf::console::detail::CommandGrammar Grammar; + Grammar grammar (dispatcher, context); char text[] = "# Comment\n" - "doo / bii / doo arg/../path" + "doo / bii / doo arg" " flab::blub" " 123.434>a" " (a,b,c (huhu))" @@ -83,11 +95,11 @@ BOOST_AUTO_UNIT_TEST(commandParser) BOOST_CHECK( boost::spirit::parse( text, - grammar, - skipGrammar ) . full ); + grammar.use_parser(), + grammar.use_parser() ) . full ); BOOST_CHECK_EQUAL( ss.str(), "beginCommand( doo/bii/doo )\n" - "pushArgument( arg/../path )\n" + "pushArgument( arg )\n" "pushArgument( flab::blub )\n" "pushArgument( 123.434>a )\n" "openGroup()\n" @@ -111,7 +123,7 @@ BOOST_AUTO_UNIT_TEST(singleCommandParser) char const text[] = "# Comment\n" - "doo / bii / doo arg/../path" + "doo / bii / doo arg" " flab::blub" " 123.434>a" " (a,b,c (huhu))" @@ -122,35 +134,38 @@ BOOST_AUTO_UNIT_TEST(singleCommandParser) senf::console::ParseCommandInfo info; BOOST_CHECK( parser.parseCommand(text, info) ); - BOOST_CHECK_EQUAL( info.commandPath(), "doo/bii/doo" ); - BOOST_REQUIRE_EQUAL( info.arguments(), 6u ); - BOOST_REQUIRE_EQUAL( info.tokens(), 13u ); + char const * path[] = { "doo", "bii", "doo" }; + + BOOST_CHECK_EQUAL_COLLECTIONS( info.commandPath().begin(), info.commandPath().end(), + path, path + sizeof(path)/sizeof(path[0]) ); + BOOST_REQUIRE_EQUAL( info.arguments().size(), 6u ); + BOOST_REQUIRE_EQUAL( info.tokens().size(), 13u ); - char const * tokens[] = { "arg/../path", + char const * tokens[] = { "arg", "flab::blub", "123.434>a", "a", ",", "b", ",", "c", "(", "huhu", ")", "foo\"bar", "\x01\x02\x03\x04" }; - BOOST_REQUIRE_EQUAL( info.begin_arguments()[0].size(), 1u ); - BOOST_CHECK_EQUAL( info.begin_arguments()[0].begin()->value(), tokens[0] ); + BOOST_REQUIRE_EQUAL( info.arguments().begin()[0].size(), 1u ); + BOOST_CHECK_EQUAL( info.arguments().begin()[0].begin()->value(), tokens[0] ); - BOOST_REQUIRE_EQUAL( info.begin_arguments()[1].size(), 1u ); - BOOST_CHECK_EQUAL( info.begin_arguments()[1].begin()->value(), tokens[1] ); + BOOST_REQUIRE_EQUAL( info.arguments().begin()[1].size(), 1u ); + BOOST_CHECK_EQUAL( info.arguments().begin()[1].begin()->value(), tokens[1] ); - BOOST_REQUIRE_EQUAL( info.begin_arguments()[2].size(), 1u ); - BOOST_CHECK_EQUAL( info.begin_arguments()[2].begin()->value(), tokens[2] ); + BOOST_REQUIRE_EQUAL( info.arguments().begin()[2].size(), 1u ); + BOOST_CHECK_EQUAL( info.arguments().begin()[2].begin()->value(), tokens[2] ); - BOOST_REQUIRE_EQUAL( info.begin_arguments()[3].size(), 8u ); + BOOST_REQUIRE_EQUAL( info.arguments().begin()[3].size(), 8u ); for (unsigned i (0); i<8; ++i) - BOOST_CHECK_EQUAL( info.begin_arguments()[3].begin()[i].value(), tokens[3+i] ); + BOOST_CHECK_EQUAL( info.arguments().begin()[3].begin()[i].value(), tokens[3+i] ); - BOOST_REQUIRE_EQUAL( info.begin_arguments()[4].size(), 1u ); - BOOST_CHECK_EQUAL( info.begin_arguments()[4].begin()->value(), tokens[11] ); + BOOST_REQUIRE_EQUAL( info.arguments().begin()[4].size(), 1u ); + BOOST_CHECK_EQUAL( info.arguments().begin()[4].begin()->value(), tokens[11] ); - BOOST_REQUIRE_EQUAL( info.begin_arguments()[5].size(), 1u ); - BOOST_CHECK_EQUAL( info.begin_arguments()[5].begin()->value(), tokens[12] ); + BOOST_REQUIRE_EQUAL( info.arguments().begin()[5].size(), 1u ); + BOOST_CHECK_EQUAL( info.arguments().begin()[5].begin()->value(), tokens[12] ); } ///////////////////////////////cc.e//////////////////////////////////////// diff --git a/Console/Server.cc b/Console/Server.cc index 0fd831d..5790d83 100644 --- a/Console/Server.cc +++ b/Console/Server.cc @@ -24,7 +24,7 @@ \brief Server non-inline non-template implementation */ #include "Server.hh" -#include "Server.ih" +//#include "Server.ih" // Custom includes #include @@ -40,55 +40,57 @@ #define prefix_ ///////////////////////////////cc.p//////////////////////////////////////// -prefix_ void senf::console::start(senf::INet4SocketAddress const & address) +prefix_ senf::console::Server & +senf::console::Server::start(senf::INet4SocketAddress const & address) { senf::TCPv4ServerSocketHandle handle (address); - handle.protocol().reuseaddr(); - senf::console::detail::Server::start(handle); - SENF_LOG((detail::Server::SENFLogArea)(log::NOTICE)( + senf::console::Server::start(handle); + SENF_LOG((Server::SENFLogArea)(log::NOTICE)( "Console server started at " << address )); + return *instance_; } -prefix_ void senf::console::start(senf::INet6SocketAddress const & address) +prefix_ senf::console::Server & +senf::console::Server::start(senf::INet6SocketAddress const & address) { senf::TCPv6ServerSocketHandle handle (address); - handle.protocol().reuseaddr(); - senf::console::detail::Server::start(handle); - SENF_LOG((detail::Server::SENFLogArea)(log::NOTICE)( + senf::console::Server::start(handle); + SENF_LOG((Server::SENFLogArea)(log::NOTICE)( "Console server started at " << address )); + return *instance_; } /////////////////////////////////////////////////////////////////////////// -// senf::console::detail::Server +// senf::console::Server -boost::scoped_ptr senf::console::detail::Server::instance_; +boost::scoped_ptr senf::console::Server::instance_; -prefix_ void senf::console::detail::Server::start(ServerHandle handle) +prefix_ void senf::console::Server::start(ServerHandle handle) { SENF_ASSERT( ! instance_ ); instance_.reset(new Server(handle)); } -prefix_ senf::console::detail::Server::Server(ServerHandle handle) +prefix_ senf::console::Server::Server(ServerHandle handle) : handle_ (handle) { Scheduler::instance().add( handle_, senf::membind(&Server::newClient, this) ); } -prefix_ senf::console::detail::Server::~Server() +prefix_ senf::console::Server::~Server() { Scheduler::instance().remove(handle_); } -prefix_ void senf::console::detail::Server::newClient(Scheduler::EventId event) +prefix_ void senf::console::Server::newClient(Scheduler::EventId event) { ServerHandle::ClientSocketHandle client (handle_.accept()); - boost::intrusive_ptr p (new Client(client)); + boost::intrusive_ptr p (new Client(client, name_)); clients_.insert( p ); SENF_LOG(( "Registered new client " << p.get() )); } -prefix_ void senf::console::detail::Server::removeClient(Client & client) +prefix_ void senf::console::Server::removeClient(Client & client) { SENF_LOG(( "Disposing client " << & client )); // THIS DELETES THE CLIENT INSTANCE !! @@ -96,26 +98,26 @@ prefix_ void senf::console::detail::Server::removeClient(Client & client) } /////////////////////////////////////////////////////////////////////////// -// senf::console::detail::Client +// senf::console::Client -prefix_ senf::console::detail::Client::Client(ClientHandle handle) - : handle_ (handle), out_(::dup(handle.fd())) +prefix_ senf::console::Client::Client(ClientHandle handle, std::string const & name) + : handle_ (handle), name_ (name), out_(::dup(handle.fd())) { - out_ << "# " << std::flush; + out_ << name_ << "# " << std::flush; ReadHelper::dispatch( handle_, 16384u, ReadUntil("\n"), senf::membind(&Client::clientData, this) ); } -prefix_ senf::console::detail::Client::~Client() +prefix_ senf::console::Client::~Client() {} -prefix_ void senf::console::detail::Client::stopClient() +prefix_ void senf::console::Client::stopClient() { // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER removeClient RETURNS Server::instance_->removeClient(*this); } -prefix_ void senf::console::detail::Client::clientData(ReadHelper::ptr helper) +prefix_ void senf::console::Client::clientData(ReadHelper::ptr helper) { if (helper->error() || handle_.eof()) { // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS @@ -132,13 +134,19 @@ prefix_ void senf::console::detail::Client::clientData(ReadHelper: tail_ = helper->tail(); boost::trim(data); // Gets rid of superfluous \r or \n characters + if (data == "exit") { + // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS + stopClient(); + return; + } + ParseCommandInfo command; if (parser_.parseCommand(data, command)) executor_(command, out_); else out_ << "syntax error" << std::endl; - out_ << "# " << std::flush; + out_ << name_ << "# " << std::flush; ReadHelper::dispatch( handle_, 16384u, ReadUntil("\n"), senf::membind(&Client::clientData, this) ); } diff --git a/Console/Server.cci b/Console/Server.cci new file mode 100644 index 0000000..12e0c00 --- /dev/null +++ b/Console/Server.cci @@ -0,0 +1,53 @@ +// $Id$ +// +// Copyright (C) 2008 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Server inline non-template implementation */ + +//#include "Server.ih" + +// Custom includes + +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::console::Server + +prefix_ void senf::console::Server::name(std::string const & name) +{ + name_ = name; +} + +///////////////////////////////cci.e/////////////////////////////////////// +#undef prefix_ + + +// 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/Console/Server.hh b/Console/Server.hh index 09bfbaf..e21eea6 100644 --- a/Console/Server.hh +++ b/Console/Server.hh @@ -27,6 +27,19 @@ #define HH_Server_ 1 // Custom includes +#include +#include +#include +#include +#include +#include +#include "../Utils/intrusive_refcount.hh" +#include "../Socket/Protocols/INet/TCPSocketHandle.hh" +#include "../Socket/ServerSocketHandle.hh" +#include "../Scheduler/Scheduler.hh" +#include "../Scheduler/ReadHelper.hh" +#include "Parse.hh" +#include "Executor.hh" #include "../Socket/Protocols/INet/INetAddressing.hh" //#include "Server.mpp" @@ -35,13 +48,88 @@ namespace senf { namespace console { - void start(senf::INet4SocketAddress const & address); - void start(senf::INet6SocketAddress const & address); + class Client; + + /** \brief + */ + class Server + : boost::noncopyable + { + SENF_LOG_CLASS_AREA(); + SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); + public: + /////////////////////////////////////////////////////////////////////////// + // Types + + typedef senf::ServerSocketHandle< + senf::MakeSocketPolicy< senf::TCPv4SocketProtocol::Policy, + senf::UnspecifiedAddressingPolicy>::policy > ServerHandle; + + ~Server(); + + static Server & start(senf::INet4SocketAddress const & address); + static Server & start(senf::INet6SocketAddress const & address); + + void name(std::string const & name); + + protected: + + private: + Server(ServerHandle handle); + + static void start(ServerHandle handle); + + void newClient(Scheduler::EventId event); + void removeClient(Client & client); + + ServerHandle handle_; + + typedef std::set< boost::intrusive_ptr > Clients; + Clients clients_; + std::string name_; + + static boost::scoped_ptr instance_; + + friend class Client; + }; + + /** \brief + */ + class Client + : public senf::intrusive_refcount + { + SENF_LOG_CLASS_AREA(); + SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); + public: + typedef Server::ServerHandle::ClientSocketHandle ClientHandle; + + ~Client(); + + void stopClient(); + + protected: + + private: + Client(ClientHandle handle, std::string const & name); + + void clientData(ReadHelper::ptr helper); + + ClientHandle handle_; + std::string tail_; + SingleCommandParser parser_; + Executor executor_; + std::string name_; + + typedef boost::iostreams::stream fdostream; + fdostream out_; + + friend class Server; + }; }} ///////////////////////////////hh.e//////////////////////////////////////// -//#include "Server.cci" +#include "Server.cci" //#include "Server.ct" //#include "Server.cti" #endif diff --git a/Console/Server.ih b/Console/Server.ih deleted file mode 100644 index d25aed3..0000000 --- a/Console/Server.ih +++ /dev/null @@ -1,135 +0,0 @@ -// $Id$ -// -// Copyright (C) 2008 -// Fraunhofer Institute for Open Communication Systems (FOKUS) -// Competence Center NETwork research (NET), St. Augustin, GERMANY -// Stefan Bund -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the -// Free Software Foundation, Inc., -// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** \file - \brief Server internal header */ - -#ifndef IH_Server_ -#define IH_Server_ 1 - -// Custom includes -#include -#include -#include -#include -#include -#include -#include "../Utils/intrusive_refcount.hh" -#include "../Socket/Protocols/INet/TCPSocketHandle.hh" -#include "../Socket/ServerSocketHandle.hh" -#include "../Scheduler/Scheduler.hh" -#include "../Scheduler/ReadHelper.hh" -#include "Parse.hh" -#include "Executor.hh" - -///////////////////////////////ih.p//////////////////////////////////////// - -namespace senf { -namespace console { -namespace detail { - - class Client; - - /** \brief - */ - class Server - : boost::noncopyable - { - SENF_LOG_CLASS_AREA(); - SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); - public: - /////////////////////////////////////////////////////////////////////////// - // Types - - typedef senf::ServerSocketHandle< - senf::MakeSocketPolicy< senf::TCPv4SocketProtocol::Policy, - senf::UnspecifiedAddressingPolicy>::policy > ServerHandle; - - ~Server(); - - static void start(ServerHandle handle); - - protected: - - private: - Server(ServerHandle handle); - - void newClient(Scheduler::EventId event); - void removeClient(Client & client); - - ServerHandle handle_; - - typedef std::set< boost::intrusive_ptr > Clients; - Clients clients_; - - static boost::scoped_ptr instance_; - - friend class Client; - }; - - /** \brief - */ - class Client - : public senf::intrusive_refcount - { - SENF_LOG_CLASS_AREA(); - SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); - public: - typedef Server::ServerHandle::ClientSocketHandle ClientHandle; - - ~Client(); - - void stopClient(); - - protected: - - private: - Client(ClientHandle handle); - - void clientData(ReadHelper::ptr helper); - - ClientHandle handle_; - std::string tail_; - SingleCommandParser parser_; - Executor executor_; - - typedef boost::iostreams::stream fdostream; - fdostream out_; - - friend class Server; - }; - -}}} - -///////////////////////////////ih.e//////////////////////////////////////// -#endif - - -// 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/Console/testServer.cc b/Console/testServer.cc index caf9106..8e9323a 100644 --- a/Console/testServer.cc +++ b/Console/testServer.cc @@ -40,7 +40,8 @@ int main(int, char const **) { senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >(); - senf::console::start( senf::INet4SocketAddress("127.0.0.1:23232") ); + senf::console::Server::start( senf::INet4SocketAddress("127.0.0.1:23232") ) + .name("testServer "); senf::Scheduler::instance().process(); }