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);
}
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();
senf::console::Executor::traverseCommand(ParseCommandInfo::CommandPathRange const & path)
{
try {
- return cwd().traverse(path);
+ return cwd().traverse(path, autocomplete_);
}
catch (std::bad_cast &) {
throw InvalidPathException();
// senf::console::Executor
prefix_ senf::console::Executor::Executor()
+ : autocd_ (false), autocomplete_ (false)
{
oldCwd_ = cwd_ = boost::static_pointer_cast<DirectoryNode>(
root().shared_from_this());
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_
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:
DirectoryNode::weak_ptr oldCwd_;
typedef std::vector<DirectoryNode::weak_ptr> DirStack;
DirStack dirstack_;
+
+ bool autocd_;
+ bool autocomplete_;
};
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()
{}
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());
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();
}
}
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
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
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);
: 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 >();
}
///////////////////////////////////////////////////////////////////////////
// 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);
}
///////////////////////////////////////////////////////////////////////////
///< 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: