{
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();
}
}
}
+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_
// 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_
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)
{
}
}
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 &) {}
}
/**< 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
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(), "" );
}
{
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(), "" );
}
{
{
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(), "" );
}
{
}
///////////////////////////////////////////////////////////////////////////
+// senf::console::LinkNode
+
+prefix_ void senf::console::LinkNode::v_help(std::ostream & os)
+ const
+{
+ os << "link to ";
+ follow().help(os);
+}
+
+///////////////////////////////////////////////////////////////////////////
//senf::console::DirectoryNode
prefix_ senf::console::GenericNode::ptr
}
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));
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
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
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()
{}
namespace senf {
namespace console {
+ class LinkNode;
class DirectoryNode;
class CommandNode;
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();
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
///< 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
///< 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;
///////////////////////////////hh.e////////////////////////////////////////
#include "Node.cci"
-#include "Node.ct"
+//#include "Node.ct"
#include "Node.cti"
#endif
#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"));
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);
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 &) {
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
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);
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)
///////////////////////////////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);
}
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>
*/
// 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_
\f
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/** \file
- \brief Node non-inline template implementation */
+ \brief Range public header */
-#include "Node.ih"
+#ifndef HH_Range_
+#define HH_Range_ 1
// Custom includes
-#include <boost/range.hpp>
-
-#define prefix_
-///////////////////////////////ct.p////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////
-// senf::console::DirectoryNode
-
-template <class ForwardRange>
-prefix_ senf::console::GenericNode &
-senf::console::DirectoryNode::traverse(ForwardRange const & range, bool autocomplete,
- DirectoryNode & root)
-{
- 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();
+#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);
+
}
-///////////////////////////////ct.e////////////////////////////////////////
-#undef prefix_
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "Range.cci"
+//#include "Range.ct"
+#include "Range.cti"
+#endif
\f
// Local Variables:
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);
#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 ; } \
//#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.
*/