Add 'rt' library to build (needed at least by gentoo)
g0dil [Mon, 19 May 2008 14:37:08 +0000 (14:37 +0000)]
Console: Restructure ConfigFile::parse and add chroot support
Console: Some ConfigFile documentation

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@847 270642c3-0616-0410-b53a-bc976706d245

Console/Config.cc
Console/Config.cci
Console/Config.hh
Console/Config.test.cc
Console/Executor.hh
Console/ScopedDirectory.cci
Console/ScopedDirectory.hh
SConstruct
senfscons/senfutil.py

index 15d3cc5..afb365f 100644 (file)
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::ConfigFile
 
-prefix_ void senf::console::ConfigFile::parse()
+#ifndef DOXYGEN
+
+namespace {
+    struct BindPolicy
+    {
+        BindPolicy(senf::console::Executor & e, senf::console::Executor::SecurityPolicy p) 
+            : e_ (e) 
+            { e_.policy(p); }
+        
+        ~BindPolicy() 
+            { e_.policy(senf::console::Executor::SecurityPolicy()); }
+        
+        senf::console::Executor & e_;
+    };
+}
+
+#endif
+
+prefix_ void senf::console::ConfigFile::parse(DirectoryNode & restrict)
 {
+    DirectoryNode::ptr r (restrict.thisptr());
+    BindPolicy bp ( executor_, 
+                    boost::bind(&ConfigFile::policyCallback, this, r, _1, _2) );
     if (! parser_.parseFile(filename_, boost::bind<void>( boost::ref(executor_),
                                                           boost::ref(std::cerr),
                                                           _1 )) )
         throw SyntaxErrorException();
+    insertParsedNode(r);
 }
 
-prefix_ void senf::console::ConfigFile::parse(DirectoryNode & restrict)
+prefix_ bool senf::console::ConfigFile::parsed(GenericNode & node)
+    const
+{
+    ParsedNodes::const_iterator i (parsedNodes_.begin());
+    ParsedNodes::const_iterator const i_end (parsedNodes_.end());
+    for (; i != i_end; ++i)
+        if ( ! i->expired() && node.isChildOf(*(i->lock())) )
+            return true;
+    return false;
+}
+
+prefix_ void senf::console::ConfigFile::policyCallback(DirectoryNode::ptr restrict,
+                                                       DirectoryNode & dir,
+                                                       std::string const & name)
+{
+    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 (parsed(item))
+            throw Executor::IgnoreCommandException();
+    }
+    else if (restrict && ! dir.isChildOf(*restrict))
+        throw Executor::IgnoreCommandException();
+}
+
+namespace {
+    struct RemoveNodesFn
+    {
+        RemoveNodesFn(senf::console::DirectoryNode::ptr newNode) : newNode_ (newNode) {}
+
+        bool operator()(senf::console::DirectoryNode::weak_ptr node) const
+            { return node.expired() || node.lock()->isChildOf(*newNode_); }
+
+        senf::console::DirectoryNode::ptr newNode_;
+    };
+}
+
+prefix_ void senf::console::ConfigFile::insertParsedNode(DirectoryNode::ptr node)
 {
-    restrict_ = restrict.thisptr();
-    parse();
-    parsedNodes_.push_back(restrict_);
-    restrict_.reset();
+    parsedNodes_.erase(
+        std::remove_if(parsedNodes_.begin(), parsedNodes_.end(), RemoveNodesFn(node)),
+        parsedNodes_.end());
+    parsedNodes_.push_back(node);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
-prefix_ void senf::console::readConfig(std::string const & filename)
+prefix_ void senf::console::readConfig(std::string const & filename, DirectoryNode & root)
 {
-    ConfigFile cfg (filename);
+    ConfigFile cfg (filename, root);
     cfg.parse();
 }
 
index f4e42bc..3c96a59 100644 (file)
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::ConfigFile
 
-prefix_ senf::console::ConfigFile::ConfigFile(std::string const & filename)
+prefix_ senf::console::ConfigFile::ConfigFile(std::string const & filename,
+                                              DirectoryNode & root)
     : filename_ (filename)
 {
-    executor_.policy(senf::membind(&ConfigFile::policyCallback, this));
+    executor_.chroot(root);
 }
 
-prefix_ void senf::console::ConfigFile::policyCallback(DirectoryNode & dir,
-                                                       std::string const & name)
+prefix_ void senf::console::ConfigFile::parse()
 {
-    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();
-        }
-        ParsedNodes::const_iterator i (parsedNodes_.begin());
-        ParsedNodes::const_iterator const i_end (parsedNodes_.end());
-        for (; i != i_end; ++i) {
-            if ( ! i->expired() && item.isChildOf(*(i->lock())) )
-                throw Executor::IgnoreCommandException();
-        }
-    }
-    else {
-        if (restrict_ && ! dir.isChildOf(*restrict_))
-            throw Executor::IgnoreCommandException();
-    }
+    parse(executor_.chroot());
+}
+
+prefix_ bool senf::console::ConfigFile::complete()
+    const
+{
+    return parsedNodes_.size() == 1 
+        && ! parsedNodes_[0].expired() 
+        && *parsedNodes_[0].lock() == executor_.chroot();
+}
+
+prefix_ void senf::console::ConfigFile::reset()
+{
+    parsedNodes_.clear();
 }
 
 ///////////////////////////////cci.e///////////////////////////////////////
index ce74973..898a02a 100644 (file)
 namespace senf {
 namespace console {
 
-    /** \brief
+    /** \brief Console node tree based config file parser
+
+        A ConfigFile instance allows flexible parsing of a config file against the console node
+        tree. If you just want to parse a file completely, the senf::console::readConfig() function
+        will do that. ConfigFile however allows to incrementally parse only a subdirectory of the
+        complete configuration file.
+        \code
+        senf::console::ConfigFile cf ("/my/config/file")
+
+        // Parse only statements under the directory of some object. The object 'ob'
+        // must have been registered somewhere in the node tree
+        cf.parse(ob.dir);
+        
+        // Parse rest of the config file
+        cf.parse();
+        \endcode
       */
     class ConfigFile
         : boost::noncopyable
@@ -47,18 +62,36 @@ namespace console {
         ///\name Structors and default members
         ///@{
 
-        explicit ConfigFile(std::string const & filename);
+        explicit ConfigFile(std::string const & filename, DirectoryNode & root = root());
+                                        ///< Create ConfigFile object for \a filename
+                                        /**< The \a filename configuration file will be parsed using
+                                             parse() calls. All configuration statements will be
+                                             interpreted relative to \a root as root node. */
 
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
-        void parse();
-        void parse(DirectoryNode & restrict);
+        void parse();                   ///< Parse config file
+                                        /**< All nodes already parsed are skipped */
+        void parse(DirectoryNode & restrict); ///< Parse config file under \a restrict
+                                        /**< Only nodes which are children of \a restrict are
+                                             parsed.  */
+
+        bool complete() const;          ///< \c true, if all nodes have been parsed
+        bool parsed(GenericNode & node) const; ///< \c true. if \a node has been parsed
+
+        void reset();                   ///< Reset node parse info state
+                                        /**< After a call to reset(), all information about already
+                                             parsed nodes is cleared. Calling parse() will parse the
+                                             complete config file again. */
 
     protected:
 
     private:
-        void policyCallback(DirectoryNode & dir, std::string const & item);
+        void policyCallback(DirectoryNode::ptr restrict, DirectoryNode & dir, 
+                            std::string const & item);
+
+        void insertParsedNode(DirectoryNode::ptr node);
 
         typedef std::vector<DirectoryNode::weak_ptr> ParsedNodes;
 
@@ -66,11 +99,19 @@ namespace console {
         CommandParser parser_;
         Executor executor_;
 
-        DirectoryNode::ptr restrict_;
         ParsedNodes parsedNodes_;
     };
 
-    void readConfig(std::string const & filename);
+    /** \brief Read configuration file
+
+        The configuration file \a filename will be loaded, interpreting all node's relative to \a
+        root as root node.
+
+        This function uses a local ConfigFile object to perform the parsing.
+
+        \related ConfigFile
+     */
+    void readConfig(std::string const & filename, DirectoryNode & root = root());
 
 }}
 
index df5c070..2215cce 100644 (file)
@@ -76,8 +76,18 @@ BOOST_AUTO_UNIT_TEST(configFile)
     dir1.add("fun1",&fun1);
 
     {
-        var1 = 0;
         senf::console::ConfigFile cfg (cfgf.name());
+
+        var1 = 0;
+        SENF_CHECK_NO_THROW( cfg.parse() )
+        BOOST_CHECK_EQUAL( var1, 10 );
+
+        var1 = 0;
+        SENF_CHECK_NO_THROW( cfg.parse() )
+        BOOST_CHECK_EQUAL( var1, 0 );
+
+        var1 = 0;
+        cfg.reset();
         SENF_CHECK_NO_THROW( cfg.parse() )
         BOOST_CHECK_EQUAL( var1, 10 );
     }
@@ -98,9 +108,11 @@ BOOST_AUTO_UNIT_TEST(configFileRestrict)
         var1 = 0;
         var2 = false;
         senf::console::ConfigFile cfg (cfgf.name());
-        SENF_CHECK_NO_THROW( cfg.parse(dir1.node()) );
+        SENF_CHECK_NO_THROW( cfg.parse(dir1) );
         BOOST_CHECK_EQUAL( var1, 10 );
         BOOST_CHECK_EQUAL( var2, false );
+        BOOST_CHECK( cfg.parsed(dir1) );
+        BOOST_CHECK( ! cfg.complete() );
 
         senf::console::ScopedDirectory<> dir2;
         senf::console::root().add("dir2", dir2);
@@ -111,6 +123,7 @@ BOOST_AUTO_UNIT_TEST(configFileRestrict)
         SENF_CHECK_NO_THROW( cfg.parse() );
         BOOST_CHECK_EQUAL( var1, 0 );
         BOOST_CHECK_EQUAL( var2, true );
+        BOOST_CHECK( cfg.complete() );
     }
 }
 
@@ -135,15 +148,17 @@ BOOST_AUTO_UNIT_TEST(configFileSkipGroup)
         var1 = 0;
         var2 = false;
         senf::console::ConfigFile cfg (cfgf.name());
-        SENF_CHECK_NO_THROW( cfg.parse(dir1.node()) );
+        SENF_CHECK_NO_THROW( cfg.parse(dir1) );
         BOOST_CHECK_EQUAL( var1, 10 );
         BOOST_CHECK_EQUAL( var2, false );
+        BOOST_CHECK( cfg.parsed(dir1) );
 
         var1 = 0;
         var2 = false;
         SENF_CHECK_NO_THROW( cfg.parse(dir2["dir3"]) );
         BOOST_CHECK_EQUAL( var1, 0 );
         BOOST_CHECK_EQUAL( var2, true );
+        BOOST_CHECK( ! cfg.parsed(dir2) );
 
         var1 = 0;
         var2 = false;
index 5cdd1f7..2c21ee1 100644 (file)
@@ -120,7 +120,7 @@ namespace console {
                                              are accessible via the executor. This value defaults to
                                              senf::console::root(). */
 
-        Executor & policy(SecurityPolicy policy); ///< Set security policy
+        Executor & policy(SecurityPolicy policy = SecurityPolicy()); ///< Set security policy
                                         /**< The security policy is called before traversing a node
                                              to validate that access. */
 
index 6f19b12..c94e1c0 100644 (file)
@@ -39,6 +39,12 @@ prefix_ senf::console::DirectoryNode & senf::console::ScopedDirectoryBase::node(
     return *node_;
 }
 
+prefix_ senf::console::ScopedDirectoryBase::operator DirectoryNode &()
+    const
+{
+    return node();
+}
+
 prefix_ senf::console::GenericNode::ptr
 senf::console::ScopedDirectoryBase::remove(std::string const & name)
 {
index cd5b9b4..89da74b 100644 (file)
@@ -69,6 +69,7 @@ namespace console {
     {
     public:
         DirectoryNode & node() const;   ///< Access the proxied DirectoryNode
+        operator DirectoryNode & () const; ///< Access the proxied DirectoryNode
 
         ///////////////////////////////////////////////////////////////////////////
         ///\name Proxied members (see DirectoryNode)
index fce3c2d..19eadb3 100644 (file)
@@ -142,7 +142,7 @@ def configFilesOpts(target, source, env, for_signature):
 
 env.Append(
    CPPPATH = [ '#/include' ],
-   LIBS = [ 'readline', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB' ],
+   LIBS = [ 'readline', 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB' ],
    TEST_EXTRA_LIBS = [ '$BOOSTFSLIB' ],
    DOXY_XREF_TYPES = [ 'bug', 'fixme', 'todo', 'idea' ],
    DOXY_HTML_XSL = '#/doclib/html-munge.xsl',
index c3ef1b2..249a78d 100644 (file)
@@ -8,7 +8,7 @@ from SCons.Script import *
 # c) check for a local SENF, set options accordingly and update that SENF if needed
 
 def SetupForSENF(env):
-    env.Append( LIBS           = [ 'senf', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB' ],
+    env.Append( LIBS           = [ 'senf', 'readline', 'rt', '$BOOSTREGEXLIB', '$BOOSTIOSTREAMSLIB' ],
                 BOOSTREGEXLIB  = 'boost_regex',
                 BOOSTIOSTREAMSLIB = 'boost_iostreams',
                 CXXFLAGS       = [ '-Wno-long-long',