From: g0dil Date: Sat, 22 Mar 2008 13:47:00 +0000 (+0000) Subject: Console: Implement current directory management and builtins (cd, dirstack, ls) X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=5e9e6057a4e5c1241ff3f1b75b0f797eb570725d;p=senf.git Console: Implement current directory management and builtins (cd, dirstack, ls) git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@757 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Console/Executor.cc b/Console/Executor.cc index 8203348..71c3f78 100644 --- a/Console/Executor.cc +++ b/Console/Executor.cc @@ -38,13 +38,81 @@ prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & command, std::ostream & output) { -# warning Implement Executor::operator() SENF_LOG(( "Executing: " << command )); - if (command.builtin() == ParseCommandInfo::BuiltinEXIT) + + if (cwd_.expired()) + cwd_ = boost::static_pointer_cast( + root().shared_from_this()); + + switch(command.builtin()) { + case ParseCommandInfo::NoBuiltin : + break; + + case ParseCommandInfo::BuiltinCD : + if ( command.arguments() && + ! chdir(command.arguments().begin()[0]) ) + output << "invalid directory\n"; + break; + + case ParseCommandInfo::BuiltinLS : + for (DirectoryNode::child_iterator i (cwd().children().begin()); + i != cwd().children().end(); ++i) + output << i->first << "\n"; + break; + + case ParseCommandInfo::BuiltinPUSHD : + dirstack_.push_back(cwd_); + if ( command.arguments() + && ! chdir(command.arguments().begin()[0]) ) + output << "invalid directory\n"; + break; + + case ParseCommandInfo::BuiltinPOPD : + if (! dirstack_.empty()) { + cwd_ = dirstack_.back(); + dirstack_.pop_back(); + } + break; + + case ParseCommandInfo::BuiltinEXIT : throw ExitException(); + } return true; } +prefix_ bool senf::console::Executor::chdir(ParseCommandInfo::argument_value_type const & path) +{ + try { + DirectoryNode::ptr dir (cwd_.lock()); + ParseCommandInfo::token_iterator i (path.begin()); + ParseCommandInfo::token_iterator const i_end (path.end()); + if (i != i_end && i->value().empty()) { + dir = boost::static_pointer_cast( + root().shared_from_this()); + ++ i; + } + for (; i != i_end; ++i) { + if (i->value() == "..") { + dir = dir->parent(); + if (! dir) + dir = boost::static_pointer_cast( + root().shared_from_this()); + } + else if (! i->value().empty() && i->value() != ".") + dir = boost::static_pointer_cast( + (*dir)[i->value()].shared_from_this()); + } + cwd_ = dir; + } + catch (std::bad_cast &) { + return false; + } + catch (UnknownNodeNameException &) { + return false; + } + return true; +} + ///////////////////////////////cc.e//////////////////////////////////////// #undef prefix_ //#include "Executor.mpp" diff --git a/Console/Executor.cci b/Console/Executor.cci new file mode 100644 index 0000000..6c20c2f --- /dev/null +++ b/Console/Executor.cci @@ -0,0 +1,60 @@ +// $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 Executor inline non-template implementation */ + +//#include "Executor.ih" + +// Custom includes + +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// senf::console::Executor + +prefix_ senf::console::Executor::Executor() +{ + cwd_ = boost::static_pointer_cast( + root().shared_from_this()); +} + +prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd() + const +{ + return cwd_.expired() ? root() : *cwd_.lock(); +} + +///////////////////////////////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/Executor.hh b/Console/Executor.hh index 08da10f..be6f15f 100644 --- a/Console/Executor.hh +++ b/Console/Executor.hh @@ -27,8 +27,10 @@ #define HH_Executor_ 1 // Custom includes +#include #include "Parse.hh" #include "../Utils/Logger/SenfLog.hh" +#include "Node.hh" //#include "Executor.mpp" ///////////////////////////////hh.p//////////////////////////////////////// @@ -39,6 +41,7 @@ namespace console { /** \brief */ class Executor + : boost::noncopyable { SENF_LOG_CLASS_AREA(); SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); @@ -51,20 +54,32 @@ namespace console { struct ExitException {}; // NOT derived from std::exception ! /////////////////////////////////////////////////////////////////////////// + //\/name Structors and default members + ///\{ + Executor(); + + ///\} + /////////////////////////////////////////////////////////////////////////// + bool operator()(ParseCommandInfo const & command, std::ostream & output); - + DirectoryNode & cwd() const; + protected: private: + bool chdir(ParseCommandInfo::argument_value_type const & path); + DirectoryNode::weak_ptr cwd_; + typedef std::vector DirStack; + DirStack dirstack_; }; }} ///////////////////////////////hh.e//////////////////////////////////////// -//#include "Executor.cci" +#include "Executor.cci" //#include "Executor.ct" //#include "Executor.cti" #endif diff --git a/Console/Node.cc b/Console/Node.cc index 964eb22..90518b0 100644 --- a/Console/Node.cc +++ b/Console/Node.cc @@ -32,11 +32,33 @@ #define prefix_ ///////////////////////////////cc.p//////////////////////////////////////// +prefix_ senf::console::DirectoryNode & senf::console::root() +{ + static DirectoryNode::ptr rootNode(new DirectoryNode("")); + return *rootNode; +} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::GenericNode + +prefix_ std::string senf::console::GenericNode::path() + const +{ + std::string path (name()); + ptr node (parent()); + while (node) { + path = node->name() + "/" + path; + node = node->parent(); + } + return path.empty() ? "/" : path; +} + /////////////////////////////////////////////////////////////////////////// //senf::console::DirectoryNode prefix_ void senf::console::DirectoryNode::add(GenericNode::ptr node, bool uniquify) { + BOOST_ASSERT( ! node->parent() ); if (children_.find(node->name()) != children_.end()) { if (! uniquify) throw DuplicateNodeNameException() << ": '" << node->name() << "'"; @@ -49,6 +71,7 @@ prefix_ void senf::console::DirectoryNode::add(GenericNode::ptr node, bool uniqu name(*node, newName); } children_.insert(std::make_pair(node->name(),node)); + node->parent_ = this; } prefix_ senf::console::GenericNode & diff --git a/Console/Node.cci b/Console/Node.cci index 88ed2c6..2881e56 100644 --- a/Console/Node.cci +++ b/Console/Node.cci @@ -34,14 +34,17 @@ /////////////////////////////////////////////////////////////////////////// // senf::console::GenericNode +prefix_ senf::console::GenericNode::~GenericNode() +{} + prefix_ std::string const & senf::console::GenericNode::name() const { return name_; } -prefix_ senf::console::GenericNode::GenericNode(std::string const & name, bool managed) - : name_ (name), managed_ (managed) +prefix_ senf::console::GenericNode::GenericNode(std::string const & name) + : name_ (name), parent_ (0) {} prefix_ void senf::console::GenericNode::name(std::string const & name) @@ -54,38 +57,22 @@ prefix_ void senf::console::GenericNode::name(GenericNode & node, std::string co node.name_ = name; } -prefix_ senf::console::DirectoryNode & senf::console::GenericNode::parent() - const -{ - SENF_ASSERT( parent_ ); - return *parent_; -} - -prefix_ bool senf::console::GenericNode::managed() +prefix_ boost::shared_ptr senf::console::GenericNode::parent() const { - return managed_; -} - -prefix_ bool senf::console::GenericNode::release() -{ - // Beware ! call release() first so the call is not short-circuited way ! - return intrusive_refcount_base::release() && managed_; + return boost::static_pointer_cast( + parent_ ? parent_->shared_from_this() : ptr() ); } /////////////////////////////////////////////////////////////////////////// // senf::console::DirectoryNode -prefix_ void senf::console::DirectoryNode::add(std::auto_ptr node, bool uniquify) -{ - SENF_ASSERT( node->managed() ); - add(GenericNode::ptr(node.release()), uniquify); -} - -prefix_ void senf::console::DirectoryNode::add(GenericNode & node, bool uniquify) +prefix_ senf::console::GenericNode & +senf::console::DirectoryNode::add(std::auto_ptr node, bool uniquify) { - SENF_ASSERT( ! node.managed() ); - add(GenericNode::ptr(&node),uniquify); + GenericNode::ptr p (node.release()); + add(p, uniquify); + return *p; } prefix_ senf::console::DirectoryNode & @@ -102,15 +89,28 @@ senf::console::DirectoryNode::operator()(std::string const & name) return dynamic_cast(lookup(name)); } -prefix_ senf::console::DirectoryNode::DirectoryNode(std::string const & name, bool managed) - : GenericNode(name, managed) +prefix_ senf::console::DirectoryNode & +senf::console::DirectoryNode::mkdir(std::string const & name) +{ + return static_cast( + add(std::auto_ptr(new DirectoryNode(name)))); +} + +prefix_ senf::console::DirectoryNode::ChildrenRange senf::console::DirectoryNode::children() + const +{ + return boost::make_iterator_range(children_.begin(), children_.end()); +} + +prefix_ senf::console::DirectoryNode::DirectoryNode(std::string const & name) + : GenericNode(name) {} /////////////////////////////////////////////////////////////////////////// // senf::console::CommandNode -prefix_ senf::console::CommandNode::CommandNode(std::string const & name, bool managed) - : GenericNode(name, managed) +prefix_ senf::console::CommandNode::CommandNode(std::string const & name) + : GenericNode(name) {} ///////////////////////////////cci.e/////////////////////////////////////// diff --git a/Console/Node.hh b/Console/Node.hh index 154bc97..c2c8be5 100644 --- a/Console/Node.hh +++ b/Console/Node.hh @@ -28,9 +28,11 @@ // Custom includes #include -#include +#include +#include +#include #include -#include "../Utils/intrusive_refcount.hh" +#include #include "../Utils/Exception.hh" //#include "Node.mpp" @@ -45,59 +47,77 @@ namespace console { /** \brief */ class GenericNode - : public intrusive_refcount_t + : public boost::enable_shared_from_this { public: /////////////////////////////////////////////////////////////////////////// // Types - typedef boost::intrusive_ptr ptr; + typedef boost::shared_ptr ptr; + typedef boost::weak_ptr weak_ptr; /////////////////////////////////////////////////////////////////////////// + virtual ~GenericNode(); + std::string const & name() const; - DirectoryNode & parent() const; + boost::shared_ptr parent() const; bool managed() const; + std::string path() const; + protected: - explicit GenericNode(std::string const & name, bool managed = false); + explicit GenericNode(std::string const & name); void name(std::string const & name); static void name(GenericNode & node, std::string const & name); void parent(DirectoryNode * parent); private: - bool release(); - std::string name_; - bool managed_; DirectoryNode * parent_; friend class intrusive_refcount_base; + friend class DirectoryNode; }; /** \brief */ class DirectoryNode : public GenericNode { + typedef std::map ChildMap; + public: - typedef boost::intrusive_ptr ptr; + /////////////////////////////////////////////////////////////////////////// + // Types + + typedef boost::shared_ptr ptr; + typedef boost::weak_ptr weak_ptr; + + typedef boost::iterator_range ChildrenRange; + typedef ChildMap::const_iterator child_iterator; + + /////////////////////////////////////////////////////////////////////////// - void add(std::auto_ptr node, bool uniquify = true); - void add(GenericNode & node, bool uniquify = true); + GenericNode & add(std::auto_ptr node, bool uniquify = true); DirectoryNode & operator[](std::string const & name) const; CommandNode & operator()(std::string const & name) const; + DirectoryNode & mkdir(std::string const & name); + + ChildrenRange children() const; + protected: - explicit DirectoryNode(std::string const & name, bool managed = false); + explicit DirectoryNode(std::string const & name); private: void add(GenericNode::ptr node, bool uniquify); GenericNode & lookup(std::string const & name) const; - typedef std::map ChildMap; ChildMap children_; + + friend DirectoryNode & root(); }; struct DuplicateNodeNameException : public senf::Exception @@ -111,15 +131,23 @@ namespace console { class CommandNode : public GenericNode { public: - typedef boost::intrusive_ptr ptr; + /////////////////////////////////////////////////////////////////////////// + // Types + + typedef boost::shared_ptr ptr; + typedef boost::weak_ptr weak_ptr; + + /////////////////////////////////////////////////////////////////////////// protected: - explicit CommandNode(std::string const & name, bool managed = false); + explicit CommandNode(std::string const & name); private: }; + DirectoryNode & root(); + }} ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Console/Parse.ih b/Console/Parse.ih index 3c5a1fc..2f7c80d 100644 --- a/Console/Parse.ih +++ b/Console/Parse.ih @@ -126,7 +126,7 @@ namespace detail { { boost::spirit::rule command, path, argument, word, string, hexstring, token, punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, - skip, commands, block, statement; + skip, commands, block, statement, relpath, abspath; boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p; boost::spirit::distinct_parser<> keyword_p; @@ -157,13 +157,50 @@ namespace detail { using namespace boost::spirit; typedef ParseDispatcher PD; + /////////////////////////////////////////////////////////////////// + // Spirit grammar + // + // Syntax summary: + // This is EBNF with some minor tweaks to accommodate C++ syntax + // + // * and + 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 + // + // 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 + // quite readable. + // + // ch_p match character + // eps_p always matches nothing (to attach unconditional actions) + // confix_p(a,b,c) match b, preceded by a and terminated by c. Used to parse + // string literals and comments + // lex_escape_ch_p match a lex style escape char. This is like a C++ style + // literal string escape char, however \x will be replaced by 'x' + // for any char 'x' if it has no special meaning. + // keyword_p match a delimited keyword + // comment_p(a,b) match comment starting with a and terminated with b. b + // defaults to end-of-line + // + // lexeme_d don't skip whitespace (as defined by the skip parser) + // + // Aligned to the right at column 50 are semantic actions. + // + // For clarity, I have used 'ch_p' explicitly throughout even though it is auxiliary + // in most cases. + // + // More info is in the Boost.Spirit documentation + commands = * command ; command - = builtin + = builtin >> (ch_p(';') | end_p) | path >> ( block | statement ) + | ch_p(';') // Ignore empty commands ; builtin @@ -232,12 +269,22 @@ namespace detail { 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) ] + >> relpath | abspath + ; + + relpath + = ( word [ push_back_a(self.context.path) ] % ch_p('/') ) >> ( ! ch_p('/') [ push_back_a(self.context.path,"") ] ) ; + abspath + = 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 = ch_p('(') [ self.dispatch(&PD::pushPunctuation, "(") ] >> * token @@ -269,6 +316,13 @@ namespace detail { = space_p | comment_p('#') ; + /////////////////////////////////////////////////////////////////// + + start_parsers( + commands, // CommandParser + skip // SkipParser + ); + BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1); @@ -282,12 +336,11 @@ namespace detail { 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( - commands, // CommandParser - skip // SkipParser - ); - + BOOST_SPIRIT_DEBUG_TRACE_RULE(commands,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(block,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(statement,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(relpath,1); + BOOST_SPIRIT_DEBUG_TRACE_RULE(abspath,1); } }; }; diff --git a/Console/Server.cc b/Console/Server.cc index 6cd21d9..569a7cf 100644 --- a/Console/Server.cc +++ b/Console/Server.cc @@ -104,7 +104,7 @@ prefix_ void senf::console::Server::removeClient(Client & client) prefix_ senf::console::Client::Client(ClientHandle handle, std::string const & name) : handle_ (handle), name_ (name), out_(::dup(handle.fd())) { - out_ << name_ << "# " << std::flush; + showPrompt(); ReadHelper::dispatch( handle_, 16384u, ReadUntil("\n"), senf::membind(&Client::clientData, this) ); } @@ -145,11 +145,16 @@ prefix_ void senf::console::Client::clientData(ReadHelper::ptr hel return; } - out_ << name_ << "# " << std::flush; + showPrompt(); ReadHelper::dispatch( handle_, 16384u, ReadUntil("\n"), senf::membind(&Client::clientData, this) ); } +prefix_ void senf::console::Client::showPrompt() +{ + out_ << name_ << ":" << executor_.cwd().path() << "# " << std::flush; +} + ///////////////////////////////cc.e//////////////////////////////////////// #undef prefix_ //#include "Server.mpp" diff --git a/Console/Server.hh b/Console/Server.hh index 5394302..bfce303 100644 --- a/Console/Server.hh +++ b/Console/Server.hh @@ -113,6 +113,7 @@ namespace console { Client(ClientHandle handle, std::string const & name); void clientData(ReadHelper::ptr helper); + void showPrompt(); ClientHandle handle_; std::string tail_; diff --git a/Console/testServer.cc b/Console/testServer.cc index 3eab4dc..9e5a3c9 100644 --- a/Console/testServer.cc +++ b/Console/testServer.cc @@ -40,6 +40,9 @@ int main(int, char const **) { senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >(); + senf::console::root().mkdir("network").mkdir("eth0"); + senf::console::root().mkdir("server"); + senf::console::Server::start( senf::INet4SocketAddress("127.0.0.1:23232") ) .name("testServer");