Console: Implement current directory management and builtins (cd, dirstack, ls)
g0dil [Sat, 22 Mar 2008 13:47:00 +0000 (13:47 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@757 270642c3-0616-0410-b53a-bc976706d245

Console/Executor.cc
Console/Executor.cci [new file with mode: 0644]
Console/Executor.hh
Console/Node.cc
Console/Node.cci
Console/Node.hh
Console/Parse.ih
Console/Server.cc
Console/Server.hh
Console/testServer.cc

index 8203348..71c3f78 100644 (file)
 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<DirectoryNode>(
+            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<DirectoryNode>(
+                root().shared_from_this());
+            ++ i;
+        }
+        for (; i != i_end; ++i) {
+            if (i->value() == "..") {
+                dir = dir->parent(); 
+                if (! dir)
+                    dir = boost::static_pointer_cast<DirectoryNode>(
+                        root().shared_from_this());
+            }
+            else if (! i->value().empty() && i->value() != ".")
+                dir = boost::static_pointer_cast<DirectoryNode>(
+                    (*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 (file)
index 0000000..6c20c2f
--- /dev/null
@@ -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 <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 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<DirectoryNode>(
+        root().shared_from_this());
+}
+
+prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
+    const
+{
+    return cwd_.expired() ? root() : *cwd_.lock();
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index 08da10f..be6f15f 100644 (file)
 #define HH_Executor_ 1
 
 // Custom includes
+#include <boost/utility.hpp>
 #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<DirectoryNode::weak_ptr> DirStack;
+        DirStack dirstack_;
     };
 
 
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
-//#include "Executor.cci"
+#include "Executor.cci"
 //#include "Executor.ct"
 //#include "Executor.cti"
 #endif
index 964eb22..90518b0 100644 (file)
 #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 &
index 88ed2c6..2881e56 100644 (file)
 ///////////////////////////////////////////////////////////////////////////
 // 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::DirectoryNode> 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<DirectoryNode>(
+        parent_ ? parent_->shared_from_this() : ptr() );
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::DirectoryNode
 
-prefix_ void senf::console::DirectoryNode::add(std::auto_ptr<GenericNode> 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<GenericNode> 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<CommandNode&>(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<DirectoryNode &>(
+        add(std::auto_ptr<GenericNode>(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///////////////////////////////////////
index 154bc97..c2c8be5 100644 (file)
 
 // Custom includes
 #include <map>
-#include <boost/intrusive_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
 #include <boost/utility.hpp>
-#include "../Utils/intrusive_refcount.hh"
+#include <boost/range/iterator_range.hpp>
 #include "../Utils/Exception.hh"
 
 //#include "Node.mpp"
@@ -45,59 +47,77 @@ namespace console {
     /** \brief
       */
     class GenericNode 
-        : public intrusive_refcount_t<GenericNode>
+        : public boost::enable_shared_from_this<GenericNode>
     {
     public:
         ///////////////////////////////////////////////////////////////////////////
         // Types
 
-        typedef boost::intrusive_ptr<GenericNode> ptr;
+        typedef boost::shared_ptr<GenericNode> ptr;
+        typedef boost::weak_ptr<GenericNode> weak_ptr;
 
         ///////////////////////////////////////////////////////////////////////////
 
+        virtual ~GenericNode();
+
         std::string const & name() const;
-        DirectoryNode & parent() const;
+        boost::shared_ptr<DirectoryNode> 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<std::string, GenericNode::ptr> ChildMap;
+
     public:
-        typedef boost::intrusive_ptr<DirectoryNode> ptr;
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+        typedef boost::shared_ptr<DirectoryNode> ptr;
+        typedef boost::weak_ptr<DirectoryNode> weak_ptr;
+
+        typedef boost::iterator_range<ChildMap::const_iterator> ChildrenRange;
+        typedef ChildMap::const_iterator child_iterator;
+
+        ///////////////////////////////////////////////////////////////////////////
 
-        void add(std::auto_ptr<GenericNode> node, bool uniquify = true);
-        void add(GenericNode & node, bool uniquify = true);
+        GenericNode & add(std::auto_ptr<GenericNode> 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<std::string, GenericNode::ptr> 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<CommandNode> ptr;
+        ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+        typedef boost::shared_ptr<CommandNode> ptr;
+        typedef boost::weak_ptr<CommandNode> weak_ptr;
+
+        ///////////////////////////////////////////////////////////////////////////
 
     protected:
-        explicit CommandNode(std::string const & name, bool managed = false);
+        explicit CommandNode(std::string const & name);
 
     private:
 
     };
 
+    DirectoryNode & root();
+
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
index 3c5a1fc..2f7c80d 100644 (file)
@@ -126,7 +126,7 @@ namespace detail {
         {
             boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token,
                 punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, 
-                skip, commands, block, statement;
+                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);
             }
         };
     };
index 6cd21d9..569a7cf 100644 (file)
@@ -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<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
                                         senf::membind(&Client::clientData, this) );
 }
@@ -145,11 +145,16 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr hel
         return;
     }        
 
-    out_ << name_ << "# " << std::flush;
+    showPrompt();
     ReadHelper<ClientHandle>::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"
index 5394302..bfce303 100644 (file)
@@ -113,6 +113,7 @@ namespace console {
         Client(ClientHandle handle, std::string const & name);
 
         void clientData(ReadHelper<ClientHandle>::ptr helper);
+        void showPrompt();
         
         ClientHandle handle_;
         std::string tail_;
index 3eab4dc..9e5a3c9 100644 (file)
@@ -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");