diff --git a/Console/Executor.cc b/Console/Executor.cc index da8aa49e32370985836df31567f047a526cf9dd8..a7db590ddb29f3a7e6ad9ed676629905522acc52 100644 --- a/Console/Executor.cc +++ b/Console/Executor.cc @@ -60,8 +60,11 @@ prefix_ void senf::console::Executor::execute(std::ostream & output, GenericNode & node ( traverseCommand(command.commandPath()) ); DirectoryNode * dir ( dynamic_cast<DirectoryNode*>(&node) ); if ( dir ) { - oldCwd_ = cwd_; - cwd_ = dir->thisptr(); + if (autocd_) { + oldCwd_ = cwd_; + cwd_ = dir->thisptr(); + } else + throw InvalidCommandException(); } else { dynamic_cast<CommandNode &>(node)(output, command); } @@ -144,7 +147,8 @@ senf::console::Executor::traverseNode(ParseCommandInfo::argument_value_type cons return cwd().traverse( boost::make_iterator_range( boost::make_transform_iterator(path.begin(), TraverseTokens()), - boost::make_transform_iterator(path.end(), TraverseTokens()))); + boost::make_transform_iterator(path.end(), TraverseTokens())), + autocomplete_); } catch (std::bad_cast &) { throw InvalidPathException(); @@ -158,7 +162,7 @@ prefix_ senf::console::GenericNode & senf::console::Executor::traverseCommand(ParseCommandInfo::CommandPathRange const & path) { try { - return cwd().traverse(path); + return cwd().traverse(path, autocomplete_); } catch (std::bad_cast &) { throw InvalidPathException(); diff --git a/Console/Executor.cci b/Console/Executor.cci index 81ef6e0e5d5afd12cefd730d42fa908ec0cef2ee..89e2501300917ff841b863e195f88f357abd1566 100644 --- a/Console/Executor.cci +++ b/Console/Executor.cci @@ -34,6 +34,7 @@ // senf::console::Executor prefix_ senf::console::Executor::Executor() + : autocd_ (false), autocomplete_ (false) { oldCwd_ = cwd_ = boost::static_pointer_cast<DirectoryNode>( root().shared_from_this()); @@ -51,6 +52,30 @@ prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd() return cwd_.expired() ? root() : *cwd_.lock(); } +prefix_ bool senf::console::Executor::autocd() + const +{ + return autocd_; +} + +prefix_ senf::console::Executor & senf::console::Executor::autocd(bool v) +{ + autocd_ = v; + return *this; +} + +prefix_ bool senf::console::Executor::autocomplete() + const +{ + return autocomplete_; +} + +prefix_ senf::console::Executor & senf::console::Executor::autocomplete(bool v) +{ + autocomplete_ = true; + return *this; +} + ///////////////////////////////cci.e/////////////////////////////////////// #undef prefix_ diff --git a/Console/Executor.hh b/Console/Executor.hh index 441c3d78dca6a73a26ca6ef837678126b40ab133..92a2dfe218bfd8c7dfe367294ade2b8094276aee 100644 --- a/Console/Executor.hh +++ b/Console/Executor.hh @@ -86,6 +86,24 @@ namespace console { Same as execute(). */ DirectoryNode & cwd() const; ///< Current working directory + bool autocd() const; ///< Get current autocd status + /**< if autocd is enabled, specifying a directory name as + command will cd to that directory. Disabled by + default (but enabled automatically by the interactive + console). */ + Executor & autocd(bool v); ///< Set autocd status + /**< \see autocd() */ + + bool autocomplete() const; ///< Get current autocomplete status + /**< if autocomplete is enabled, path components which can + be uniquely completed will be completed + automatically. Disabled by default (but enabled + automatically by the interactive console) void + autocomplete(bool v). */ + + Executor & autocomplete(bool v); ///< Set autocomplete status + /**< \see autocomplete() */ + protected: private: @@ -101,6 +119,9 @@ namespace console { DirectoryNode::weak_ptr oldCwd_; typedef std::vector<DirectoryNode::weak_ptr> DirStack; DirStack dirstack_; + + bool autocd_; + bool autocomplete_; }; diff --git a/Console/Node.cci b/Console/Node.cci index c0d76bc0e6c1cd45e37a29adb06025ddfb10a372..8fe2c2bca879c4fd5495c7ed04d8a41d0ed4042a 100644 --- a/Console/Node.cci +++ b/Console/Node.cci @@ -145,6 +145,14 @@ prefix_ senf::console::DirectoryNode::ChildrenRange senf::console::DirectoryNode return boost::make_iterator_range(children_.begin(), children_.end()); } +prefix_ senf::console::DirectoryNode::ChildrenRange +senf::console::DirectoryNode::completions(std::string const & s) + const +{ + return boost::make_iterator_range(children_.lower_bound(s), + children_.lower_bound(s + "\xff")); +} + prefix_ senf::console::DirectoryNode::DirectoryNode() {} diff --git a/Console/Node.ct b/Console/Node.ct index 4244e693d3ea7fb3d4e47e3d0cda226f87ea4781..ddf17ac97a620aaa86450c7426b93d2f2c6db997 100644 --- a/Console/Node.ct +++ b/Console/Node.ct @@ -36,7 +36,7 @@ template <class ForwardRange> prefix_ senf::console::GenericNode & -senf::console::DirectoryNode::traverse(ForwardRange const & range) +senf::console::DirectoryNode::traverse(ForwardRange const & range, bool autocomplete) { typedef typename boost::range_const_iterator<ForwardRange>::type const_iterator; DirectoryNode::ptr dir (thisptr()); @@ -55,12 +55,18 @@ senf::console::DirectoryNode::traverse(ForwardRange const & range) dir = root().thisptr(); } else if (*i != std::string("") && *i != std::string(".")) { + std::string name (*i); + if (! dir->hasChild(name) && autocomplete) { + ChildrenRange completions (dir->completions(name)); + if (completions.size() == 1) + name = completions.begin()->first; + } if (next_i == i_end) - return dir->get(*i); + return dir->get(name); else { // Why does g++ give an error on this line ???? : - // dir = dynamic_cast<DirectoryNode&>( dir->get(*i) ).thisptr(); - DirectoryNode & d (dynamic_cast<DirectoryNode&>( dir->get(*i) )); + // dir = dynamic_cast<DirectoryNode&>( dir->get(name) ).thisptr(); + DirectoryNode & d (dynamic_cast<DirectoryNode&>( dir->get(name) )); dir = d.thisptr(); } } diff --git a/Console/Node.hh b/Console/Node.hh index a93c23cccd5a5820589e669f26f8d20fd445e33d..e443205e4b41e9ee95811ca83ef536dd9759c72c 100644 --- a/Console/Node.hh +++ b/Console/Node.hh @@ -465,15 +465,18 @@ namespace console { DirectoryNode & mkdir(std::string const & name); ///< Create sub-directory node - ChildrenRange children() const; - ///< Return iterator range over all children. + ChildrenRange children() const; ///< Return iterator range over all children. + /**< The returned range is sorted by child name. */ + + ChildrenRange completions(std::string const & s) const; + ///< Return iterator range of completions for \a s /**< The returned range is sorted by child name. */ ///\} /////////////////////////////////////////////////////////////////////////// template <class ForwardRange> - GenericNode & traverse(ForwardRange const & range); + GenericNode & traverse(ForwardRange const & range, bool autocomplete=false); ///< Traverse node path starting at this node /**< The <tt>ForwardRange::value_type</tt> must be (convertible to) std::string. Each range element @@ -482,7 +485,11 @@ namespace console { If the range starts with an empty element, the traversal is started at the root() node, otherwise it is started at \a this node. The traversal supports '.', - '..' and ignores further empty elements. */ + '..' and ignores further empty elements. + + If \a autocomplete is set to \c true, invalid path + components which can be uniquely completed will be + completed automatically while traversing the tree. */ DirectoryNode & doc(std::string const & doc); ///< Set node documentation diff --git a/Console/Node.test.cc b/Console/Node.test.cc index d2f9142c8373159cebe695a781c305ab98fbf506..0e0668d88a3cdee3fe6988a811df2877b4c09f2c 100644 --- a/Console/Node.test.cc +++ b/Console/Node.test.cc @@ -98,12 +98,28 @@ BOOST_AUTO_UNIT_TEST(directoryNode) children, children+sizeof(children)/sizeof(children[0]) ); + char const * const completions[] = { "dir1", "dir2" }; + BOOST_CHECK_EQUAL_COLLECTIONS( + boost::make_transform_iterator(senf::console::root().completions("dir").begin(), + select1st<std::string const &>()), + boost::make_transform_iterator(senf::console::root().completions("dir").end(), + select1st<std::string const &>()), + completions, + completions+sizeof(completions)/sizeof(completions[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"] ); + char const * const incompletePath[] = { "d" }; + BOOST_CHECK( &senf::console::root()["dir2"].traverse( boost::make_iterator_range( + incompletePath, + incompletePath+sizeof(incompletePath)/sizeof(incompletePath[0])), + true ) + == &senf::console::root()["dir2"]["dir3"] ); + p->doc("test doc"); std::stringstream ss; p->help(ss); diff --git a/Console/Server.cc b/Console/Server.cc index ffe724694fc2ee11624ed9c04fd8b35781fb6e50..9ab2c8d8cba6eaee80372734327f476da68ddb6f 100644 --- a/Console/Server.cc +++ b/Console/Server.cc @@ -198,6 +198,7 @@ prefix_ senf::console::Client::Client(Server & server, ClientHandle handle, : out_t(boost::ref(*this)), senf::log::IOStreamTarget(out_t::member), server_ (server), handle_ (handle), name_ (name), reader_ (new detail::SafeReadlineClientReader (*this)) { + executor_.autocd(true).autocomplete(true); handle_.facet<senf::TCPSocketProtocol>().nodelay(); // route< senf::SenfLog, senf::log::NOTICE >(); } diff --git a/Console/Server.cci b/Console/Server.cci index 573773d32d4153ccce89a74bcc60c944719195a3..b68443977c40485b98db726a989dc9f841880ae7 100644 --- a/Console/Server.cci +++ b/Console/Server.cci @@ -46,9 +46,16 @@ prefix_ senf::console::Client & senf::console::detail::NonblockingSocketSink::cl /////////////////////////////////////////////////////////////////////////// // senf::console::Server -prefix_ void senf::console::Server::name(std::string const & name) +prefix_ senf::console::Server & senf::console::Server::name(std::string const & name) { name_ = name; + return *this; +} + +prefix_ void senf::console::Server::stop() +{ + // commit suicide + instancePtr().reset(0); } /////////////////////////////////////////////////////////////////////////// diff --git a/Console/Server.hh b/Console/Server.hh index 8d9b30a8ebd27cbefba9ca1ab854ef4e6b235d76..e33be2cb496cd7eb6516d983b13b5fa8751c85b6 100644 --- a/Console/Server.hh +++ b/Console/Server.hh @@ -85,9 +85,12 @@ namespace console { ///< Start server on given IPv4 address/port static Server & start(senf::INet6SocketAddress const & address); ///< Start server on given IPv6 address/port - void name(std::string const & name); ///< Set server name + Server & name(std::string const & name); ///< Set server name /**< This information is used in the prompt string. */ - + + void stop(); ///< Stop the server + /**< All clients will be closed */ + protected: private: