Skip to content
Snippets Groups Projects
Commit aa2c51cb authored by g0dil's avatar g0dil
Browse files

Utils: Document new utilities

Console: Implement LinkNode
parent 8ee4c7f7
No related branches found
No related tags found
No related merge requests found
......@@ -82,11 +82,8 @@ prefix_ void senf::console::ConfigFile::policyCallback(DirectoryNode::ptr restri
{
if (dir.hasChild(name)) {
GenericNode & item (dir.get(name));
if (restrict && ! item.isChildOf(*restrict)) {
DirectoryNode * itemdir (dynamic_cast<DirectoryNode*>(&item));
if (! itemdir || ! restrict->isChildOf(*itemdir))
throw Executor::IgnoreCommandException();
}
if (restrict && ! item.isChildOf(*restrict) && ! item.isDirectory())
throw Executor::IgnoreCommandException();
if (parsed(item))
throw Executor::IgnoreCommandException();
}
......
......@@ -168,6 +168,40 @@ BOOST_AUTO_UNIT_TEST(configFileSkipGroup)
}
}
BOOST_AUTO_UNIT_TEST(configRestrictAndLink)
{
TempFile cfgf ("test.cfg");
cfgf << "dir1/fun1 10;\n"
<< "link1 { dir3 { fun2; } fun1 5; }"
<< TempFile::close;
senf::console::ScopedDirectory<> dir1;
senf::console::root().add("dir1", dir1);
dir1.add("fun1",&fun1);
senf::console::ScopedDirectory<> dir2;
dir1.add("dir2", dir2);
dir2.mkdir("dir3").add("fun2", &fun2);
dir2.add("fun1", &fun1);
senf::console::ScopedDirectory<> dir4;
senf::console::root().add("dir4", dir4);
dir4.link("link1", dir2);
{
senf::console::ConfigFile cfg (cfgf.name(), dir4);
var1 = 0;
var2 = false;
SENF_CHECK_NO_THROW( cfg.parse(dir2["dir3"]) );
BOOST_CHECK_EQUAL( var1, 0 );
BOOST_CHECK_EQUAL( var2, true );
BOOST_CHECK_THROW( cfg.parse(), senf::console::SyntaxErrorException );
}
}
///////////////////////////////cc.e////////////////////////////////////////
#undef prefix_
......
......@@ -28,7 +28,11 @@
// Custom includes
#include <boost/utility.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/bind.hpp>
#include "../Utils/senfassert.hh"
#include "../Utils/Range.hh"
#include "../Utils/String.hh"
//#include "Executor.mpp"
#define prefix_
......@@ -57,6 +61,18 @@ prefix_ senf::console::DirectoryNode & senf::console::Executor::cwd()
return * cwd_.back().lock();
}
prefix_ std::string senf::console::Executor::cwdPath()
const
{
if (skipping())
return "";
return "/" + senf::stringJoin(
senf::make_transform_range(
boost::make_iterator_range(boost::next(cwd_.begin()), cwd_.end()),
boost::bind(&DirectoryNode::name, boost::bind(&DirectoryNode::weak_ptr::lock, _1))),
"/" );
}
prefix_ void senf::console::Executor::execute(std::ostream & output,
ParseCommandInfo const & command)
{
......@@ -122,13 +138,13 @@ prefix_ void senf::console::Executor::execute(std::ostream & output,
}
}
catch (InvalidPathException &) {
output << "invalid path" << std::endl;
throw SyntaxErrorException("invalid path");
}
catch (InvalidDirectoryException &) {
output << "invalid directory" << std::endl;
throw SyntaxErrorException("invalid directory");
}
catch (InvalidCommandException &) {
output << "invalid command" << std::endl;
throw SyntaxErrorException("invalid command");
}
catch (IgnoreCommandException &) {}
}
......
......@@ -91,7 +91,8 @@ namespace console {
/**< Output will be written to \a output.
Same as execute(). */
DirectoryNode & cwd() const; ///< Current working directory
bool skipping() const;
std::string cwdPath() const; ///< Return pathname of current directory
bool skipping() const; ///< \c true, if currently skipping a directory group
bool autocd() const; ///< Get current autocd status
/**< if autocd is enabled, specifying a directory name as
......
......@@ -62,7 +62,8 @@ BOOST_AUTO_UNIT_TEST(executor)
parser.parse("cd dir1", &setCommand);
executor(os, commands.back());
BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinCD );
BOOST_CHECK( &executor.cwd() == &senf::console::root()["dir1"] );
BOOST_CHECK( executor.cwd() == senf::console::root()["dir1"] );
BOOST_CHECK_EQUAL( executor.cwdPath(), "/dir1" );
BOOST_CHECK_EQUAL( os.str(), "" );
}
......@@ -78,10 +79,10 @@ BOOST_AUTO_UNIT_TEST(executor)
{
std::stringstream os;
parser.parse("cd dir1", &setCommand);
executor(os, commands.back());
BOOST_CHECK_THROW( executor(os, commands.back()), senf::console::SyntaxErrorException );
BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinCD );
BOOST_CHECK( &executor.cwd() == &senf::console::root()["dir2"] );
BOOST_CHECK_EQUAL( os.str(), "invalid directory\n" );
BOOST_CHECK_EQUAL( os.str(), "" );
}
{
......@@ -112,9 +113,9 @@ BOOST_AUTO_UNIT_TEST(executor)
{
std::stringstream os;
parser.parse("ls dir3", &setCommand);
executor(os, commands.back());
BOOST_CHECK_THROW( executor(os, commands.back()), senf::console::SyntaxErrorException );
BOOST_CHECK_EQUAL( commands.back().builtin(), senf::console::ParseCommandInfo::BuiltinLS );
BOOST_CHECK_EQUAL( os.str(), "invalid directory\n" );
BOOST_CHECK_EQUAL( os.str(), "" );
}
{
......
......@@ -88,6 +88,16 @@ prefix_ bool senf::console::GenericNode::isChildOf(DirectoryNode & parent)
return node;
}
///////////////////////////////////////////////////////////////////////////
// senf::console::LinkNode
prefix_ void senf::console::LinkNode::v_help(std::ostream & os)
const
{
os << "link to ";
follow().help(os);
}
///////////////////////////////////////////////////////////////////////////
//senf::console::DirectoryNode
......@@ -127,7 +137,7 @@ prefix_ void senf::console::DirectoryNode::add(GenericNode::ptr node)
}
prefix_ senf::console::GenericNode &
senf::console::DirectoryNode::get(std::string const & name)
senf::console::DirectoryNode::getLink(std::string const & name)
const
{
ChildMap::const_iterator i (children_.find(name));
......
......@@ -96,6 +96,45 @@ prefix_ bool senf::console::GenericNode::operator!=(GenericNode & other)
return this != & other;
}
prefix_ bool senf::console::GenericNode::isDirectory()
const
{
return dynamic_cast<DirectoryNode const *>(this);
}
prefix_ bool senf::console::GenericNode::isLink()
const
{
return dynamic_cast<LinkNode const *>(this);
}
prefix_ bool senf::console::GenericNode::isCommand()
const
{
return dynamic_cast<CommandNode const *>(this);
}
///////////////////////////////////////////////////////////////////////////
// senf::console::LinkNode
prefix_ senf::console::LinkNode::ptr senf::console::LinkNode::create(GenericNode & node)
{
GenericNode::ptr p (node.thisptr());
while ( p->isLink() )
p = dynamic_cast<LinkNode&>(*p).follow().thisptr();
return ptr(new LinkNode(*p));
}
prefix_ senf::console::GenericNode & senf::console::LinkNode::follow()
const
{
return *node_;
}
prefix_ senf::console::LinkNode::LinkNode(GenericNode & node)
: node_ (node.thisptr())
{}
///////////////////////////////////////////////////////////////////////////
// senf::console::DirectoryNode
......@@ -111,6 +150,16 @@ prefix_ bool senf::console::DirectoryNode::hasChild(std::string const & name)
return i != children_.end();
}
prefix_ senf::console::GenericNode &
senf::console::DirectoryNode::get(std::string const & name)
const
{
GenericNode & node (getLink(name));
return node.isLink()
? dynamic_cast<LinkNode&>(node).follow()
: node;
}
prefix_ senf::console::DirectoryNode &
senf::console::DirectoryNode::getDirectory(std::string const & name)
const
......@@ -165,6 +214,11 @@ senf::console::DirectoryNode::completions(std::string const & s)
children_.lower_bound(s + "\xff"));
}
prefix_ void senf::console::DirectoryNode::link(std::string const & name, GenericNode & target)
{
add(name, LinkNode::create(target));
}
prefix_ senf::console::DirectoryNode::DirectoryNode()
{}
......
......@@ -215,6 +215,7 @@
namespace senf {
namespace console {
class LinkNode;
class DirectoryNode;
class CommandNode;
......@@ -288,6 +289,10 @@ namespace console {
bool operator!= (GenericNode & other) const;
/// \c true, if this and \a other are different nodes
bool isDirectory() const; ///< \c true, if this is a drectory node
bool isLink() const; ///< \c true, if this is a link node
bool isCommand() const; ///< \c true, if this is a command node
protected:
GenericNode();
......@@ -310,6 +315,47 @@ namespace console {
friend class DirectoryNode;
};
/** \brief Config/console tree link node
A LinkNode references another node and provides an additional alias name for that node. A
LinkNode works like a mixture of UNIX symlinks and hardlinks: It is an explicit link like a
UNIX symlink but references another node directly (not via it's path) like a UNIX
hardlink. Therefore, a LinkNode works across chroot().
*/
class LinkNode
: public GenericNode
{
public:
///////////////////////////////////////////////////////////////////////////
// Types
typedef boost::shared_ptr<LinkNode> ptr;
typedef boost::shared_ptr<LinkNode const> cptr;
typedef boost::weak_ptr<LinkNode> weak_ptr;
///////////////////////////////////////////////////////////////////////////
///\name Structors and default members
///@{
static ptr create(GenericNode & node); ///< Create new link node.
/**< You should normally use DirectoryNode::link() to
create a link node. */
///@}
///////////////////////////////////////////////////////////////////////////
GenericNode & follow() const; ///< Access the referenced node
protected:
private:
explicit LinkNode(GenericNode & node);
virtual void v_help(std::ostream &) const;
GenericNode::ptr node_;
};
class SimpleCommandNode;
/** \brief Internal: Node creation helper traits
......@@ -445,6 +491,10 @@ namespace console {
///< Get child node
/**< \throws UnknownNodeNameException if a child \a name
does not exist */
GenericNode & getLink(std::string const & name) const;
///< Get child node without dereferencing links
/**< \throws UnknownNodeNameException if a child \a name
does not exist */
DirectoryNode & getDirectory(std::string const & name) const;
///< Get directory child node
......@@ -488,28 +538,12 @@ namespace console {
///< Return iterator range of completions for \a s
/**< The returned range is sorted by child name. */
void link(std::string const & name, GenericNode & target);
///\}
///////////////////////////////////////////////////////////////////////////
template <class ForwardRange>
GenericNode & traverse(ForwardRange const & range, bool autocomplete=false,
DirectoryNode & root = root());
///< Traverse node path starting at this node
/**< The <tt>ForwardRange::value_type</tt> must be
(convertible to) std::string. Each range element
constitutes a step along the node traversal.
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.
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
DirectoryNode & doc(std::string const & doc); ///< Set node documentation
ptr thisptr();
cptr thisptr() const;
......@@ -668,7 +702,7 @@ BOOST_TYPEOF_REGISTER_TYPE(senf::console::SimpleCommandNode)
///////////////////////////////hh.e////////////////////////////////////////
#include "Node.cci"
#include "Node.ct"
//#include "Node.ct"
#include "Node.cti"
#endif
......
......@@ -37,7 +37,7 @@
#define prefix_
///////////////////////////////cc.p////////////////////////////////////////
BOOST_AUTO_UNIT_TEST(gnericNode)
BOOST_AUTO_UNIT_TEST(genericNode)
{
senf::console::GenericNode & node (
senf::console::root().mkdir("dir1").mkdir("dir2").doc("help info"));
......@@ -107,19 +107,6 @@ BOOST_AUTO_UNIT_TEST(directoryNode)
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);
......@@ -132,6 +119,17 @@ BOOST_AUTO_UNIT_TEST(directoryNode)
BOOST_CHECK_EQUAL( senf::console::root().children().size(), 0u );
}
BOOST_AUTO_UNIT_TEST(linkNode)
{
senf::console::root().mkdir("dir1");
senf::console::root().link("link1", senf::console::root()["dir1"]);
BOOST_CHECK( senf::console::root()["dir1"] == senf::console::root()["link1"] );
senf::console::root().remove("dir1");
senf::console::root().remove("link1");
}
namespace {
struct Functor {
void operator()(std::ostream & os, senf::console::ParseCommandInfo const &) {
......
......@@ -98,6 +98,12 @@ senf::console::ScopedDirectoryBase::mkdir(std::string const & name)
return node().mkdir(name);
}
prefix_ void senf::console::ScopedDirectoryBase::link(std::string const & name,
GenericNode & target)
{
return node().link(name, target);
}
prefix_ senf::console::DirectoryNode::ChildrenRange
senf::console::ScopedDirectoryBase::children()
const
......
......@@ -83,6 +83,7 @@ namespace console {
CommandNode & operator()(std::string const & name) const;
GenericNode & get(std::string const & name) const;
DirectoryNode & mkdir(std::string const & name);
void link(std::string const & name, GenericNode & node);
DirectoryNode::ChildrenRange children() const;
DirectoryNode & doc(std::string const & doc);
......
......@@ -139,7 +139,7 @@ prefix_ std::string const & senf::console::Client::name()
prefix_ std::string senf::console::Client::promptString()
const
{
return name_ + ":" + executor_.cwd().path() + "$ ";
return name_ + ":" + executor_.cwdPath() + "$ ";
}
prefix_ senf::console::Client & senf::console::Client::get(std::ostream & os)
......
......@@ -33,8 +33,27 @@
///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
/** \defgroup backtraces Backtrace formatting and parsing
These functions help format and barse backtrace information as returned by the glibc
::backtrace function.
*/
/** \brief Formnat a given backtrace
This functions will write \a backtrace formatted to \a os. This includes demangling symbol
names and interpreting some additional kernel symbols.
\ingroup backtraces
*/
void formatBacktrace(std::ostream & os, void ** backtrace, unsigned numEntries);
/** \brief Write a backtrace to \a os
backtrace() will write a backtrace of the current call stack to \a os.
\ingroup backtraces
*/
void backtrace(std::ostream & os, unsigned numEntries);
}
......
......@@ -99,13 +99,27 @@ namespace senf {
specification</td></tr>
<tr><td>\ref utils_tags</td><td>Miscellaneous type tags</td></tr>
<tr><td>stringJoin()</td><td>Utility to join a string range into
a single string (with separator)</td><?tr>
<tr><td>make_transform_range()</td><td>\c boost::make_transform_iterator() with support for
ranges</td></tr>
<tr><td>\ref backtraces</td><td>Utilities to parse and format backtrace information as provided
by the GNU libc</td></tr>
</table>
\section compatibility Compatibility
\section testing Correctness and testing
<table class="listing">
<tr><td>\ref auto_unit_test.hh</td><td>Boost auto unit test compatibility across Boost versions
1.33 and 1.34</td></tr>
<tr><td>\ref SENF_ASSERT()</td><td>SENF specific assertion macro</td></tr>
<tr><td>\ref unittest</td><td>Additional unit test extension for Boost.Test</td></tr>
</table>
*/
......
......@@ -21,34 +21,27 @@
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/** \file
\brief Node non-inline template implementation */
\brief Range inline template implementation */
#include "Node.ih"
//#include "Range.ih"
// Custom includes
#include <boost/range.hpp>
#define prefix_
///////////////////////////////ct.p////////////////////////////////////////
#define prefix_ inline
///////////////////////////////cti.p///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// senf::console::DirectoryNode
template <class ForwardRange>
prefix_ senf::console::GenericNode &
senf::console::DirectoryNode::traverse(ForwardRange const & range, bool autocomplete,
DirectoryNode & root)
template <class Range, class Fn>
prefix_ boost::iterator_range<
boost::transform_iterator< Fn,
typename boost::range_const_iterator<Range>::type > >
senf::make_transform_range(Range const & range, Fn const & fn)
{
typedef typename boost::range_const_iterator<ForwardRange>::type const_iterator;
detail::NodeTraverser traverser (root, *this, autocomplete);
const_iterator i (boost::begin(range));
const_iterator const i_end (boost::end(range));
for (; i != i_end; ++i)
traverser( *i );
return traverser.node();
return boost::make_iterator_range(
boost::make_transform_iterator(boost::begin(range), fn),
boost::make_transform_iterator(boost::end(range), fn) );
}
///////////////////////////////ct.e////////////////////////////////////////
///////////////////////////////cti.e///////////////////////////////////////
#undef prefix_
......
// $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 Range public header */
#ifndef HH_Range_
#define HH_Range_ 1
// Custom includes
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/transform_iterator.hpp>
//#include "Range.mpp"
///////////////////////////////hh.p////////////////////////////////////////
namespace senf {
/** \brief Make a \c boost::iterator_range from \c boost::trasform_iterator
This helper is like \c boost::make_transform_iterator, however for ranges instead of
iterators.
*/
template <class Range, class Fn>
boost::iterator_range<
boost::transform_iterator< Fn,
typename boost::range_const_iterator<Range>::type > >
make_transform_range(Range const & range, Fn const & fn);
}
///////////////////////////////hh.e////////////////////////////////////////
//#include "Range.cci"
//#include "Range.ct"
#include "Range.cti"
#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:
......@@ -34,6 +34,11 @@
namespace senf {
/** \brief Join string range with separator into single string
This utility will build string by joining all elements of \a range into a single string
using \a sep as separator. The \a range may contain values of any streamable type.
*/
template <class ForwardReadableRange>
std::string stringJoin(ForwardReadableRange const & range, std::string sep);
......
......@@ -50,8 +50,44 @@
#include <boost/test/auto_unit_test.hpp>
/** \defgroup unittest Boost.Test extensions
This module defines some additional extensions to Boost.Test
*/
/** \brief Check for compile failure
COMPILE_RAIL() is used to check, that a certain piece of code will produce a compile time
failure.
\code
#ifdef COMPILE_CHECK
COMPILE_FAIL(foo)
{
// fails to compile ....
int x = "foo";
}
COMPILE_FAIL(bar)
{ ... }
#endif
\endcode
This check is performed by the extended unit-test builder in \c senfscons.
\ingroup unittest
*/
#define COMPILE_FAIL(n) void n()
/** \brief Show exception information
\ref SENF_CHECK_NO_THROW() is an extension to \c BOOST_CHECK_NO_THROW() which will write out the
unabridged exception information when an exception is thrown in addition to signaling an error.
\ingroup unittest
*/
#define SENF_CHECK_NO_THROW(expr) \
BOOST_CHECK_NO_THROW( \
try { (void) expr ; } \
......
......@@ -34,7 +34,7 @@
//#include "preprocessor.mpp"
///////////////////////////////hh.p////////////////////////////////////////
/** \defgroup senfpp Preprocessor meta programming macros
/** \defgroup senfpp Preprocessor meta programming
preprocessor.hh provides some additional helper macros based on the Boost.Preprocessor library.
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment