From aa2c51cbff7950392f0574723772c6dc0afb9c0a Mon Sep 17 00:00:00 2001
From: g0dil <g0dil@wiback.org>
Date: Wed, 21 May 2008 21:41:58 +0000
Subject: [PATCH] Utils: Document new utilities Console: Implement LinkNode

---
 Console/Config.cc                  |  7 +--
 Console/Config.test.cc             | 34 ++++++++++++++
 Console/Executor.cc                | 22 +++++++--
 Console/Executor.hh                |  3 +-
 Console/Executor.test.cc           | 11 +++--
 Console/Node.cc                    | 12 ++++-
 Console/Node.cci                   | 54 ++++++++++++++++++++++
 Console/Node.hh                    | 74 ++++++++++++++++++++++--------
 Console/Node.test.cc               | 26 +++++------
 Console/ScopedDirectory.cci        |  6 +++
 Console/ScopedDirectory.hh         |  1 +
 Console/Server.cci                 |  2 +-
 Utils/Backtrace.hh                 | 19 ++++++++
 Utils/Mainpage.dox                 | 16 ++++++-
 Console/Node.ct => Utils/Range.cti | 33 ++++++-------
 Utils/Range.hh                     | 66 ++++++++++++++++++++++++++
 Utils/String.hh                    |  5 ++
 Utils/auto_unit_test.hh            | 36 +++++++++++++++
 Utils/preprocessor.hh              |  2 +-
 19 files changed, 357 insertions(+), 72 deletions(-)
 rename Console/Node.ct => Utils/Range.cti (57%)
 create mode 100644 Utils/Range.hh

diff --git a/Console/Config.cc b/Console/Config.cc
index afb365feb..3d2200b37 100644
--- a/Console/Config.cc
+++ b/Console/Config.cc
@@ -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();
     }
diff --git a/Console/Config.test.cc b/Console/Config.test.cc
index 2215ccecb..df3383b15 100644
--- a/Console/Config.test.cc
+++ b/Console/Config.test.cc
@@ -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_
 
diff --git a/Console/Executor.cc b/Console/Executor.cc
index d40f69ddc..d5f56fbdc 100644
--- a/Console/Executor.cc
+++ b/Console/Executor.cc
@@ -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 &) {}
 }
diff --git a/Console/Executor.hh b/Console/Executor.hh
index f771e3b6c..882792a80 100644
--- a/Console/Executor.hh
+++ b/Console/Executor.hh
@@ -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
diff --git a/Console/Executor.test.cc b/Console/Executor.test.cc
index 0e751f173..36f9dcfb2 100644
--- a/Console/Executor.test.cc
+++ b/Console/Executor.test.cc
@@ -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(), "" );
     }
     
     {
diff --git a/Console/Node.cc b/Console/Node.cc
index 3e1666705..42086bb05 100644
--- a/Console/Node.cc
+++ b/Console/Node.cc
@@ -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));
diff --git a/Console/Node.cci b/Console/Node.cci
index 108902a2c..242881fd8 100644
--- a/Console/Node.cci
+++ b/Console/Node.cci
@@ -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()
 {}
 
diff --git a/Console/Node.hh b/Console/Node.hh
index ca57da107..939113b42 100644
--- a/Console/Node.hh
+++ b/Console/Node.hh
@@ -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
 
diff --git a/Console/Node.test.cc b/Console/Node.test.cc
index 0e0668d88..cab82e35e 100644
--- a/Console/Node.test.cc
+++ b/Console/Node.test.cc
@@ -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 &) {
diff --git a/Console/ScopedDirectory.cci b/Console/ScopedDirectory.cci
index c94e1c0e5..6f868b70c 100644
--- a/Console/ScopedDirectory.cci
+++ b/Console/ScopedDirectory.cci
@@ -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
diff --git a/Console/ScopedDirectory.hh b/Console/ScopedDirectory.hh
index 89da74ba2..04a33ec33 100644
--- a/Console/ScopedDirectory.hh
+++ b/Console/ScopedDirectory.hh
@@ -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);
 
diff --git a/Console/Server.cci b/Console/Server.cci
index b68443977..78387d614 100644
--- a/Console/Server.cci
+++ b/Console/Server.cci
@@ -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)
diff --git a/Utils/Backtrace.hh b/Utils/Backtrace.hh
index 4861a3b6c..ccc1d9768 100644
--- a/Utils/Backtrace.hh
+++ b/Utils/Backtrace.hh
@@ -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);
 
 }
diff --git a/Utils/Mainpage.dox b/Utils/Mainpage.dox
index cc20df80a..3c0c53f78 100644
--- a/Utils/Mainpage.dox
+++ b/Utils/Mainpage.dox
@@ -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>
  */
 
diff --git a/Console/Node.ct b/Utils/Range.cti
similarity index 57%
rename from Console/Node.ct
rename to Utils/Range.cti
index c9516c034..0444e4251 100644
--- a/Console/Node.ct
+++ b/Utils/Range.cti
@@ -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_
 
 
diff --git a/Utils/Range.hh b/Utils/Range.hh
new file mode 100644
index 000000000..db6d63eb8
--- /dev/null
+++ b/Utils/Range.hh
@@ -0,0 +1,66 @@
+// $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:
diff --git a/Utils/String.hh b/Utils/String.hh
index 0b8028f42..1bdf93ebc 100644
--- a/Utils/String.hh
+++ b/Utils/String.hh
@@ -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);
 
diff --git a/Utils/auto_unit_test.hh b/Utils/auto_unit_test.hh
index a12669225..b2e60ae5a 100644
--- a/Utils/auto_unit_test.hh
+++ b/Utils/auto_unit_test.hh
@@ -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 ; }                                                                     \
diff --git a/Utils/preprocessor.hh b/Utils/preprocessor.hh
index 55a3fa824..0567d781e 100644
--- a/Utils/preprocessor.hh
+++ b/Utils/preprocessor.hh
@@ -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.
  */
-- 
GitLab