Console: Refactor config file parser into several classes
g0dil [Fri, 23 May 2008 16:24:03 +0000 (16:24 +0000)]
Console: Implement program option parsing

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

19 files changed:
Console/Config.cc
Console/Config.cci
Console/Config.hh
Console/Config.ih [new file with mode: 0644]
Console/ConfigFile.cc [new file with mode: 0644]
Console/ConfigFile.cci [new file with mode: 0644]
Console/ConfigFile.hh [new file with mode: 0644]
Console/ConfigFile.ih [new file with mode: 0644]
Console/ConfigFile.test.cc [moved from Console/Config.test.cc with 97% similarity]
Console/Executor.hh
Console/Parse.cc
Console/Parse.cci
Console/Parse.hh
Console/Parse.ih
Console/ProgramOptions.cc [new file with mode: 0644]
Console/ProgramOptions.cci [new file with mode: 0644]
Console/ProgramOptions.hh [new file with mode: 0644]
Console/ProgramOptions.ih [new file with mode: 0644]
Console/ProgramOptions.test.cc [new file with mode: 0644]

index 3d2200b..dc08724 100644 (file)
     \brief Config non-inline non-template implementation */
 
 #include "Config.hh"
-//#include "Config.ih"
+#include "Config.ih"
 
 // Custom includes
+#include "../Utils/membind.hh"
 
 //#include "Config.mpp"
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////
-// senf::console::ConfigFile
+// senf::console::detail::RestrictedExecutor
 
-#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_;
-    };
+prefix_ senf::console::detail::RestrictedExecutor::RestrictedExecutor(DirectoryNode & root)
+{
+    executor_
+        .chroot(root)
+        .policy(senf::membind(&RestrictedExecutor::policyCallback, this));
 }
 
-#endif
+prefix_ void
+senf::console::detail::RestrictedExecutor::execute(std::ostream & output,
+                                                   ParseCommandInfo const & command)
+{
+    executor_.execute(output, command);
+}
 
-prefix_ void senf::console::ConfigFile::parse(DirectoryNode & restrict)
+prefix_ void
+senf::console::detail::RestrictedExecutor::operator()(std::ostream & output,
+                                                      ParseCommandInfo const & command)
 {
-    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);
+    execute(output, command);
 }
 
-prefix_ bool senf::console::ConfigFile::parsed(GenericNode & node)
+prefix_ bool senf::console::detail::RestrictedExecutor::parsed(GenericNode & node)
     const
 {
     ParsedNodes::const_iterator i (parsedNodes_.begin());
@@ -76,18 +68,17 @@ prefix_ bool senf::console::ConfigFile::parsed(GenericNode & node)
     return false;
 }
 
-prefix_ void senf::console::ConfigFile::policyCallback(DirectoryNode::ptr restrict,
-                                                       DirectoryNode & dir,
-                                                       std::string const & name)
+prefix_ void senf::console::detail::RestrictedExecutor::policyCallback(DirectoryNode & dir,
+                                                                       std::string const & name)
 {
     if (dir.hasChild(name)) {
         GenericNode & item (dir.get(name));
-        if (restrict && ! item.isChildOf(*restrict) && ! item.isDirectory())
+        if (restrict_ && ! item.isChildOf(*restrict_) && ! item.isDirectory())
             throw Executor::IgnoreCommandException();
         if (parsed(item))
             throw Executor::IgnoreCommandException();
     }
-    else if (restrict && ! dir.isChildOf(*restrict))
+    else if (restrict_ && ! dir.isChildOf(*restrict_))
         throw Executor::IgnoreCommandException();
 }
 
@@ -103,20 +94,63 @@ namespace {
     };
 }
 
-prefix_ void senf::console::ConfigFile::insertParsedNode(DirectoryNode::ptr node)
+prefix_ void
+senf::console::detail::RestrictedExecutor::insertParsedNode(DirectoryNode & node)
 {
     parsedNodes_.erase(
-        std::remove_if(parsedNodes_.begin(), parsedNodes_.end(), RemoveNodesFn(node)),
+        std::remove_if(parsedNodes_.begin(), parsedNodes_.end(), RemoveNodesFn(node.thisptr())),
         parsedNodes_.end());
-    parsedNodes_.push_back(node);
+    parsedNodes_.push_back(node.thisptr());
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// senf::console::ConfigBundle
+
+prefix_ void senf::console::ConfigBundle::parse()
+{
+    detail::RestrictedExecutor::RestrictGuard guard (executor_);
+    parseInternal();
+}
+
+prefix_ void senf::console::ConfigBundle::parse(DirectoryNode & restrict)
+{
+    detail::RestrictedExecutor::RestrictGuard guard (executor_, restrict);
+    parseInternal();
+}
+
+prefix_ void senf::console::ConfigBundle::parseInternal()
+{
+    Sources::const_iterator i (sources_.begin());
+    Sources::const_iterator const i_end (sources_.end());
+    for (; i != i_end; ++i)
+        (*i)->parse(executor_);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::RestrictedExecutor::RestrictGuard
+
+prefix_ senf::console::detail::RestrictedExecutor::RestrictGuard::
+RestrictGuard(RestrictedExecutor & executor)
+    : executor_ (executor)
+{
+    // This MUST BE root() not chroot() since restriction does NOT follow symlinks.
+    // Therefore, if chroot() is a directory of symlinks, restricting to it will
+    // execute NOTHING.
+    executor_.restrict_ = senf::console::root().thisptr();
+}
+
+prefix_ senf::console::detail::RestrictedExecutor::RestrictGuard::
+RestrictGuard(RestrictedExecutor & executor, DirectoryNode & restrict)
+    : executor_ (executor)
+{
+    executor_.restrict_ = restrict.thisptr();
+}
 
-prefix_ void senf::console::readConfig(std::string const & filename, DirectoryNode & root)
+prefix_ senf::console::detail::RestrictedExecutor::RestrictGuard::~RestrictGuard()
 {
-    ConfigFile cfg (filename, root);
-    cfg.parse();
+    if (! std::uncaught_exception())
+        executor_.insertParsedNode( *executor_.restrict_ );
+    executor_.restrict_ = senf::console::root().thisptr();
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index 3c96a59..1fb445b 100644 (file)
@@ -23,7 +23,7 @@
 /** \file
     \brief Config inline non-template implementation */
 
-//#include "Config.ih"
+#include "Config.ih"
 
 // Custom includes
 #include "../Utils/membind.hh"
 ///////////////////////////////cci.p///////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////
-// senf::console::ConfigFile
+// senf::console::ConfigBundle
 
-prefix_ senf::console::ConfigFile::ConfigFile(std::string const & filename,
-                                              DirectoryNode & root)
-    : filename_ (filename)
+prefix_ senf::console::ConfigBundle::ConfigBundle()
+{}
+
+prefix_ senf::console::ConfigBundle::ConfigBundle(DirectoryNode & root)
+    : executor_ (root)
+{}
+
+prefix_ void senf::console::ConfigBundle::add(detail::ConfigSource::ptr source)
+{
+    sources_.push_back(source);
+}
+
+prefix_ bool senf::console::ConfigBundle::complete()
+    const
 {
-    executor_.chroot(root);
+    return executor_.complete();
 }
 
-prefix_ void senf::console::ConfigFile::parse()
+prefix_ bool senf::console::ConfigBundle::parsed(GenericNode & node)
+    const
+{
+    return executor_.parsed(node);
+}
+
+prefix_ void senf::console::ConfigBundle::reset()
 {
-    parse(executor_.chroot());
+    executor_.reset();
 }
 
-prefix_ bool senf::console::ConfigFile::complete()
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::RestrictedExecutor
+
+prefix_ bool senf::console::detail::RestrictedExecutor::complete()
     const
 {
     return parsedNodes_.size() == 1 
@@ -54,11 +74,67 @@ prefix_ bool senf::console::ConfigFile::complete()
         && *parsedNodes_[0].lock() == executor_.chroot();
 }
 
-prefix_ void senf::console::ConfigFile::reset()
+prefix_ void senf::console::detail::RestrictedExecutor::reset()
 {
     parsedNodes_.clear();
 }
 
+prefix_ senf::console::DirectoryNode & senf::console::detail::RestrictedExecutor::root()
+    const
+{
+    return executor_.chroot();
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::ConfigSource
+
+prefix_ void senf::console::detail::ConfigSource::parse(RestrictedExecutor & executor)
+{
+    v_parse(executor);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::BundleMixin
+
+prefix_ senf::console::detail::BundleMixin::BundleMixin()
+{}
+
+prefix_ senf::console::detail::BundleMixin::BundleMixin(DirectoryNode & root)
+    : bundle_ (root)
+{}
+
+prefix_ void senf::console::detail::BundleMixin::parse()
+{
+    bundle_.parse();
+}
+
+prefix_ void senf::console::detail::BundleMixin::parse(DirectoryNode & restrict)
+{
+    bundle_.parse(restrict);
+}
+
+prefix_ bool senf::console::detail::BundleMixin::complete()
+    const
+{
+    return bundle_.complete();
+}
+
+prefix_ bool senf::console::detail::BundleMixin::parsed(GenericNode & node)
+    const
+{
+    return bundle_.parsed(node);
+}
+
+prefix_ void senf::console::detail::BundleMixin::reset()
+{
+    bundle_.reset();
+}
+
+prefix_ void senf::console::detail::BundleMixin::add(ConfigSource::ptr source)
+{
+    bundle_.add(source);
+}
+
 ///////////////////////////////cci.e///////////////////////////////////////
 #undef prefix_
 
index 898a02a..e4f5c63 100644 (file)
 #include "Executor.hh"
 
 //#include "Config.mpp"
+#include "Config.ih"
 ///////////////////////////////hh.p////////////////////////////////////////
 
 namespace senf {
 namespace console {
 
-    /** \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
+    /** \brief
       */
-    class ConfigFile
-        : boost::noncopyable
+    class ConfigBundle
     {
     public:
         ///////////////////////////////////////////////////////////////////////////
+        // Types
+
+        ///////////////////////////////////////////////////////////////////////////
         ///\name Structors and default members
         ///@{
 
-        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. */
+        ConfigBundle();
+        ConfigBundle(DirectoryNode & root);
+
+        // default default constructor
+        // default copy constructor
+        // default copy assignment
+        // default destructor
+
+        // no conversion constructors
 
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
+        void add(detail::ConfigSource::ptr source);
+
         void parse();                   ///< Parse config file
                                         /**< All nodes already parsed are skipped */
         void parse(DirectoryNode & restrict); ///< Parse config file under \a restrict
@@ -79,7 +73,6 @@ namespace console {
 
         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
@@ -88,32 +81,44 @@ namespace console {
     protected:
 
     private:
-        void policyCallback(DirectoryNode::ptr restrict, DirectoryNode & dir, 
-                            std::string const & item);
+        void parseInternal();
 
-        void insertParsedNode(DirectoryNode::ptr node);
+        typedef std::vector<detail::ConfigSource::ptr> Sources;
 
-        typedef std::vector<DirectoryNode::weak_ptr> ParsedNodes;
+        Sources sources_;
+        detail::RestrictedExecutor executor_;
+    };
 
-        std::string filename_;
-        CommandParser parser_;
-        Executor executor_;
+namespace detail {
+    // hrmpf ... Can't place this into Config.ih ...
 
-        ParsedNodes parsedNodes_;
-    };
+    class BundleMixin
+    {
+    public:
+        BundleMixin();
+        BundleMixin(DirectoryNode & root);
 
-    /** \brief Read configuration file
+        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.  */
 
-        The configuration file \a filename will be loaded, interpreting all node's relative to \a
-        root as root node.
+        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. */
 
-        This function uses a local ConfigFile object to perform the parsing.
+    protected:
+        void add(ConfigSource::ptr source);
 
-        \related ConfigFile
-     */
-    void readConfig(std::string const & filename, DirectoryNode & root = root());
+    private:
+        ConfigBundle bundle_;
+    };
 
-}}
+}}}
 
 ///////////////////////////////hh.e////////////////////////////////////////
 #include "Config.cci"
diff --git a/Console/Config.ih b/Console/Config.ih
new file mode 100644 (file)
index 0000000..3a598b9
--- /dev/null
@@ -0,0 +1,143 @@
+// $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 Config internal header */
+
+#ifndef IH_Console_Config_
+#define IH_Console_Config_ 1
+
+// Custom includes
+#include <boost/utility.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include "Executor.hh"
+#include "../Utils/intrusive_refcount.hh"
+
+///////////////////////////////ih.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+namespace detail {
+
+    class RestrictedExecutor
+        : boost::noncopyable
+    {
+    public:
+        typedef void result_type;
+
+        ///////////////////////////////////////////////////////////////////////////
+        //\/name Structors and default members
+        ///\{
+
+        RestrictedExecutor(DirectoryNode & root = senf::console::root());
+
+        ///\}
+        ///////////////////////////////////////////////////////////////////////////
+
+        void execute(std::ostream & output, ParseCommandInfo const & command);
+                                        ///< Execute command
+                                        /**< Output will be written to \a output. 
+                                             Same as operator()(). */
+
+        void operator()(std::ostream & output, ParseCommandInfo const & command);
+                                        ///< Execute command
+                                        /**< Output will be written to \a output. 
+                                             Same as execute(). */
+
+        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. */
+
+        DirectoryNode & root() const;
+
+        class RestrictGuard;
+
+    protected:
+
+    private:
+        void policyCallback(DirectoryNode & dir, std::string const & item);
+        void insertParsedNode(DirectoryNode & node);
+
+        typedef std::vector<DirectoryNode::weak_ptr> ParsedNodes;
+
+        Executor executor_;
+        ParsedNodes parsedNodes_;
+        DirectoryNode::ptr restrict_;
+
+        friend class RestrictGuard;
+    };
+
+    class RestrictedExecutor::RestrictGuard
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        //\/name Structors and default members
+        ///\{
+
+        explicit RestrictGuard(RestrictedExecutor & executor);
+        RestrictGuard(RestrictedExecutor & executor, DirectoryNode & restrict);
+        ~RestrictGuard();
+
+        ///\}
+        ///////////////////////////////////////////////////////////////////////////
+
+    protected:
+
+    private:
+        RestrictedExecutor & executor_;
+
+    };
+
+    /** \brief
+      */
+    class ConfigSource
+        : public senf::intrusive_refcount
+    {
+    public:
+        typedef boost::intrusive_ptr<ConfigSource> ptr;
+
+        void parse(RestrictedExecutor & executor);
+
+    protected:
+
+    private:
+        virtual void v_parse(RestrictedExecutor & executor) = 0;
+    };
+
+}}}
+
+///////////////////////////////ih.e////////////////////////////////////////
+#endif
+
+\f
+// 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/Console/ConfigFile.cc b/Console/ConfigFile.cc
new file mode 100644 (file)
index 0000000..b20529d
--- /dev/null
@@ -0,0 +1,67 @@
+// $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 ConfigFile non-inline non-template implementation */
+
+#include "ConfigFile.hh"
+#include "ConfigFile.ih"
+
+// Custom includes
+
+//#include "ConfigFile.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::ConfigFileSource
+
+prefix_ void senf::console::detail::ConfigFileSource::v_parse(RestrictedExecutor & executor)
+{
+    if (! parser_.parseFile(filename_, boost::bind( boost::ref(executor),
+                                                    boost::ref(std::cerr),
+                                                    _1 )) )
+        throw SyntaxErrorException();
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+prefix_ void senf::console::readConfig(std::string const & filename, DirectoryNode & root)
+{
+    ConfigFile cfg (filename, root);
+    cfg.parse();
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "ConfigFile.mpp"
+
+\f
+// 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/Console/ConfigFile.cci b/Console/ConfigFile.cci
new file mode 100644 (file)
index 0000000..8e80db8
--- /dev/null
@@ -0,0 +1,76 @@
+// $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 ConfigFile inline non-template implementation */
+
+#include "ConfigFile.ih"
+
+// Custom includes
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::ConfigFileSource
+
+prefix_ senf::console::detail::ConfigFileSource::ptr
+senf::console::detail::ConfigFileSource::create(std::string const & filename)
+{
+    return ptr(new ConfigFileSource(filename));
+}
+
+prefix_ senf::console::detail::ConfigFileSource::ConfigFileSource(std::string const & filename)
+    : filename_ (filename)
+{}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::ConfigFile
+
+prefix_ senf::console::ConfigFile::ConfigFile(std::string const & filename,
+                                              DirectoryNode & root)
+    : detail::BundleMixin(root)
+{
+    add(detail::ConfigFileSource::create(filename));
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+prefix_ senf::console::detail::ConfigFileSource::ptr
+senf::console::FileConfig(std::string const & filename)
+{
+    return detail::ConfigFileSource::create(filename);
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// 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/Console/ConfigFile.hh b/Console/ConfigFile.hh
new file mode 100644 (file)
index 0000000..3e08edf
--- /dev/null
@@ -0,0 +1,104 @@
+// $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 ConfigFile public header */
+
+#ifndef HH_ConfigFile_
+#define HH_ConfigFile_ 1
+
+// Custom includes
+#include "Config.hh"
+
+//#include "ConfigFile.mpp"
+#include "ConfigFile.ih"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+
+    /** \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 
+        : public detail::BundleMixin
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+
+        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. */
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+    };
+
+    /** \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());
+
+    detail::ConfigFileSource::ptr FileConfig(std::string const & filename);
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "ConfigFile.cci"
+//#include "ConfigFile.ct"
+//#include "ConfigFile.cti"
+#endif
+
+\f
+// 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/Console/ConfigFile.ih b/Console/ConfigFile.ih
new file mode 100644 (file)
index 0000000..9bb15e2
--- /dev/null
@@ -0,0 +1,65 @@
+// $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 ConfigFile internal header */
+
+#ifndef IH_ConfigFile_
+#define IH_ConfigFile_ 1
+
+// Custom includes
+
+///////////////////////////////ih.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+namespace detail {
+
+    class ConfigFileSource : public ConfigSource
+    {
+    public:
+        static ptr create(std::string const & filename);
+
+    private:
+        ConfigFileSource(std::string const & filename);
+
+        virtual void v_parse(RestrictedExecutor & executor);
+
+        std::string filename_;
+        CommandParser parser_;
+    };
+
+}}}
+
+///////////////////////////////ih.e////////////////////////////////////////
+#endif
+
+\f
+// 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:
similarity index 97%
rename from Console/Config.test.cc
rename to Console/ConfigFile.test.cc
index df3383b..b8e68b9 100644 (file)
 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 /** \file
-    \brief Config.test unit tests */
+    \brief ConfigFile.test unit tests */
 
-//#include "Config.test.hh"
-//#include "Config.test.ih"
+//#include "ConfigFile.test.hh"
+//#include "ConfigFile.test.ih"
 
 // Custom includes
-#include "Config.hh"
+#include "ConfigFile.hh"
 #include <fstream>
 #include "ScopedDirectory.hh"
 #include "ParsedCommand.hh"
@@ -61,7 +61,6 @@ namespace {
         std::string name_;
         std::ofstream file_;
     };
-
     
 }
 
index 882792a..81049b1 100644 (file)
@@ -72,6 +72,8 @@ namespace console {
         /// Thrown by the SecurityPolicy to silently ignore a command
         struct IgnoreCommandException {};
 
+        typedef void result_type;
+
         ///////////////////////////////////////////////////////////////////////////
         //\/name Structors and default members
         ///\{
index 57aba80..b916ae0 100644 (file)
@@ -42,85 +42,70 @@ namespace detail {
 
 #ifndef DOXYGEN
 
-    struct ParserAccess
-    {
-        static void init(ParseCommandInfo & info)
-            { info.init(); }
-
-        static void setBuiltin(ParseCommandInfo & info, ParseCommandInfo::BuiltinCommand builtin)
-            { info.setBuiltin(builtin); }
-
-        static void setCommand(ParseCommandInfo & info, std::vector<Token> & commandPath)
-            { info.setCommand(commandPath); }
-
-        static void addToken(ParseCommandInfo & info, Token const & token)
-            { info.addToken(token); }
-    };
-
     struct ParseDispatcher
     {
-        ParseCommandInfo info_;
+        ParseCommandInfo * info_;
         CommandParser::Callback cb_;
 
         struct BindInfo {
-            BindInfo( ParseDispatcher & d, CommandParser::Callback cb)
-                : dispatcher (d) { dispatcher.cb_ = cb; }
-            ~BindInfo() { dispatcher.cb_  = 0; }
+            BindInfo( ParseDispatcher & d, ParseCommandInfo & info, CommandParser::Callback cb)
+                : dispatcher (d) { dispatcher.info_ = &info; dispatcher.cb_ = cb; }
+            ~BindInfo() { dispatcher.info_ = 0; dispatcher.cb_  = 0; }
 
             ParseDispatcher & dispatcher;
         };
 
         void beginCommand(std::vector<Token> & command)
-            { ParserAccess::init(info_);
-              ParserAccess::setCommand(info_, command); }
+            { info_->clear();
+              info_->command(command); }
 
         void endCommand()
-            { cb_(info_); }
+            { cb_(*info_); }
 
         void pushToken(Token const & token)
-            { ParserAccess::addToken(info_, token); }
+            { info_->addToken(token); }
 
         void builtin_cd(std::vector<Token> & path)
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinCD);
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinCD);
               setBuiltinPathArg(path);
-              cb_(info_); }
+              cb_(*info_); }
 
         void builtin_ls(std::vector<Token> & path)
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinLS);
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinLS);
               setBuiltinPathArg(path);
-              cb_(info_); }
+              cb_(*info_); }
 
         void pushDirectory(std::vector<Token> & path)
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinPUSHD);
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinPUSHD);
               setBuiltinPathArg(path);
-              cb_(info_); }
+              cb_(*info_); }
 
         void popDirectory()
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinPOPD);
-              cb_(info_); }
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinPOPD);
+              cb_(*info_); }
         
         void builtin_exit()
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinEXIT);
-              cb_(info_); }
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinEXIT);
+              cb_(*info_); }
 
         void builtin_help(std::vector<Token> & path)
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinHELP);
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinHELP);
               setBuiltinPathArg(path);
-              cb_(info_); }
+              cb_(*info_); }
 
         void setBuiltinPathArg(std::vector<Token> & path)
             {
-                pushToken(Token(Token::ArgumentGroupOpen, "("));
+                pushToken(ArgumentGroupOpenToken());
                 for (std::vector<Token>::const_iterator i (path.begin());
                      i != path.end(); ++i)
                     pushToken(*i);
-                pushToken(Token(Token::ArgumentGroupClose, ")"));
+                pushToken(ArgumentGroupCloseToken());
             }
     };
 
@@ -270,7 +255,8 @@ prefix_ senf::console::CommandParser::~CommandParser()
 
 prefix_ bool senf::console::CommandParser::parse(std::string command, Callback cb)
 {
-    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, cb);
+    ParseCommandInfo info;
+    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, cb);
     return boost::spirit::parse( command.begin(), command.end(), 
                                  impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
                                  impl().grammar.use_parser<Impl::Grammar::SkipParser>()
@@ -279,7 +265,8 @@ prefix_ bool senf::console::CommandParser::parse(std::string command, Callback c
 
 prefix_ bool senf::console::CommandParser::parseFile(std::string filename, Callback cb)
 {
-    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, cb);
+    ParseCommandInfo info;
+    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, cb);
     boost::spirit::file_iterator<> i (filename);
     if (!i) throw SystemException(ENOENT SENF_EXC_DEBUGINFO);
     boost::spirit::file_iterator<> const i_end (i.make_end());
@@ -290,6 +277,16 @@ prefix_ bool senf::console::CommandParser::parseFile(std::string filename, Callb
         ).full;
 }
 
+prefix_ bool senf::console::CommandParser::parseArguments(std::string arguments,
+                                                          ParseCommandInfo & info)
+{
+    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, 0);
+    return boost::spirit::parse( arguments.begin(), arguments.end(), 
+                                 impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
+                                 impl().grammar.use_parser<Impl::Grammar::SkipParser>()
+        ).full;
+}
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 //#include "Parse.mpp"
index 0482101..c7c6c61 100644 (file)
@@ -131,6 +131,10 @@ prefix_ senf::console::Token senf::console::WordToken(std::string const & value)
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::ParseCommandInfo
 
+prefix_ senf::console::ParseCommandInfo::ParseCommandInfo()
+    : builtin_ (NoBuiltin)
+{}
+
 prefix_ senf::console::ParseCommandInfo::BuiltinCommand
 senf::console::ParseCommandInfo::builtin()
     const
@@ -159,26 +163,25 @@ prefix_ senf::console::ParseCommandInfo::TokensRange senf::console::ParseCommand
     return boost::make_iterator_range(tokens_.begin(), tokens_.end());
 }
 
-////////////////////////////////////////
-// private members
-
-prefix_ void senf::console::ParseCommandInfo::init()
+prefix_ void senf::console::ParseCommandInfo::clear()
 {
     builtin_ = NoBuiltin;
     commandPath_.clear();
     tokens_.clear();
 }
 
-prefix_ void senf::console::ParseCommandInfo::setBuiltin(BuiltinCommand builtin)
+prefix_ void senf::console::ParseCommandInfo::builtin(BuiltinCommand builtin)
 {
     builtin_ = builtin;
+    commandPath_.clear();
 }
 
 prefix_ void
-senf::console::ParseCommandInfo::setCommand(std::vector<Token> & commandPath)
+senf::console::ParseCommandInfo::command(std::vector<Token> & commandPath)
 {
     commandPath_.clear();
     commandPath_.swap(commandPath);
+    builtin_ = NoBuiltin;
 }
 
 prefix_ void senf::console::ParseCommandInfo::addToken(Token const & token)
index 8c38dae..142060d 100644 (file)
@@ -274,7 +274,6 @@ namespace console {
 
     std::ostream & operator<<(std::ostream & os, Token const & token);
 
-    
     Token NoneToken();
     Token PathSeparatorToken();
     Token ArgumentGroupOpenToken();
@@ -325,6 +324,8 @@ namespace console {
                               BuiltinEXIT,
                               BuiltinHELP };
 
+        ParseCommandInfo();
+
         BuiltinCommand builtin() const; ///< Command type
                                         /**< \returns \c NoBuiltin, if the command is an ordinary
                                              command, otherwise the id of the built-in command */
@@ -341,21 +342,22 @@ namespace console {
         TokensRange tokens() const;     ///< All argument tokens
                                         /**< The returned range contains \e all argument tokens in a
                                              single range not divided into separate arguments. */
-    protected:
 
-    private:
-        void init();
-        void setBuiltin(BuiltinCommand builtin);
-        void setCommand(std::vector<Token> & commandPath);
+        void clear();
+
+        void builtin(BuiltinCommand builtin);
+        void command(std::vector<Token> & commandPath);
+
         void addToken(Token const & token);
 
+    protected:
+
+    private:
         struct MakeRange;
 
         std::vector<Token> commandPath_;
         BuiltinCommand builtin_;
         Tokens tokens_;
-
-        friend class detail::ParserAccess;
     };
 
     /** \brief Iterator parsing argument groups
@@ -561,6 +563,8 @@ namespace console {
                                         /**< \throws SystemException if the file cannot be
                                              read. */
 
+        bool parseArguments(std::string arguments, ParseCommandInfo & info);
+
     private:
         struct Impl;
 
index 79b30b6..ae9a193 100644 (file)
@@ -77,7 +77,7 @@ namespace detail {
         ///////////////////////////////////////////////////////////////////////////
         // Start rules
 
-        enum { CommandParser, SkipParser };
+        enum { CommandParser, SkipParser, ArgumentsParser };
 
         ///////////////////////////////////////////////////////////////////////////
         // The parse context (variables needed while parsing)
@@ -174,11 +174,12 @@ namespace detail {
         template <class Scanner>
         struct definition 
             : public boost::spirit::grammar_def< boost::spirit::rule<Scanner>, 
+                                                 boost::spirit::rule<Scanner>,
                                                  boost::spirit::rule<Scanner> >
         {
             boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token,
                 punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, 
-                skip, commands, block, statement, relpath, abspath;
+                skip, commands, block, statement, relpath, abspath, arguments;
             boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p;
             boost::spirit::distinct_parser<> keyword_p;
 
@@ -284,11 +285,15 @@ namespace detail {
                 statement
                     = eps_p                       [ self.dispatch(&PD::beginCommand, 
                                                                   boost::ref(self.context.path)) ]
-                      >> * argument
+                      >> arguments
                       >> (ch_p(';') | end_p)
                       >> eps_p                    [ self.dispatch(&PD::endCommand) ]
                     ;
 
+                arguments
+                    =    * argument
+                    ;
+
                 argument
                     =    simple_argument          [ self.dispatch(&PD::pushToken, 
                                                                   boost::ref(self.context.token)) ]
@@ -392,7 +397,8 @@ namespace detail {
 
                 start_parsers(
                     commands,           // CommandParser
-                    skip                // SkipParser
+                    skip,               // SkipParser
+                    arguments           // ArgumentsParser
                 );
 
                 BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1);
diff --git a/Console/ProgramOptions.cc b/Console/ProgramOptions.cc
new file mode 100644 (file)
index 0000000..118a774
--- /dev/null
@@ -0,0 +1,128 @@
+// $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 ProgramOptions non-inline non-template implementation */
+
+#include "ProgramOptions.hh"
+#include "ProgramOptions.ih"
+
+// Custom includes
+#include <boost/algorithm/string/predicate.hpp>
+
+//#include "ProgramOptions.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::ProgramOptionsSource
+
+prefix_ void senf::console::detail::ProgramOptionsSource::v_parse(RestrictedExecutor & executor)
+{
+    char ** argp (argv_);
+    int n (argc_);
+    for (; n; --n, ++argp) {
+        std::string arg (*argp);
+        if (boost::algorithm::starts_with(arg, std::string("--")))
+            parseLongOption(arg.substr(2), executor);
+        else
+            parseNonOption(arg, executor);
+    }
+}
+
+namespace {
+    struct MakeWordToken
+    {
+        typedef senf::console::Token result_type;
+        template <class Range>
+        result_type operator()(Range const & r) const
+            { return senf::console::WordToken(std::string(boost::begin(r), boost::end(r))); }
+    };
+}
+
+prefix_ void
+senf::console::detail::ProgramOptionsSource::parseLongOption(std::string const & arg,
+                                                             RestrictedExecutor & executor)
+{
+    std::string::size_type ix (arg.find('='));
+    std::string name (arg.substr(0,ix));
+    std::string value (ix==std::string::npos ? std::string() : arg.substr(ix+1));
+
+    typedef std::vector<Token> Path;
+
+    ParseCommandInfo cmd;
+    Path path;
+    
+    DirectoryNode::ptr cwd (executor.root().thisptr());
+    std::string::size_type b (0);
+    while (b < name.size()) {
+        std::string::size_type e (name.size());
+        for (;e != std::string::npos && e > b; e = name.rfind('-', e)) {
+            DirectoryNode::ChildrenRange completions (cwd->completions(name.substr(b,e-b)));
+            if (completions.size() == 1) {
+                path.push_back(WordToken(completions.begin()->first));
+                if (e < name.size())
+                    cwd = cwd->getDirectory(completions.begin()->first).thisptr();
+                b = e+1;
+                e = b+1;
+                break;
+            }
+        }
+        if (e == std::string::npos || e <= b) {
+            // This will produce a correct error message later
+            path.push_back(WordToken(name.substr(b)));
+            b = name.size();
+        }
+    }
+    
+    cmd.command(path);
+    parser_.parseArguments(value, cmd);
+    executor(std::cerr, cmd);
+}
+
+prefix_ void
+senf::console::detail::ProgramOptionsSource::parseNonOption(std::string const & arg,
+                                                            RestrictedExecutor & executor)
+{}
+
+///////////////////////////////////////////////////////////////////////////
+
+prefix_ void senf::console::parseOptions(int argc, char ** argv, DirectoryNode & root)
+{
+    ProgramOptions opts (argc, argv, root);
+    opts.parse();
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "ProgramOptions.mpp"
+
+\f
+// 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/Console/ProgramOptions.cci b/Console/ProgramOptions.cci
new file mode 100644 (file)
index 0000000..96415c0
--- /dev/null
@@ -0,0 +1,77 @@
+// $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 ProgramOptions inline non-template implementation */
+
+#include "ProgramOptions.ih"
+
+// Custom includes
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::ProgramOptionsSource
+
+prefix_ senf::console::detail::ProgramOptionsSource::ptr
+senf::console::detail::ProgramOptionsSource::create(int argc, char ** argv)
+{
+    return ptr(new ProgramOptionsSource(argc,argv));
+}
+
+prefix_ senf::console::detail::ProgramOptionsSource::ProgramOptionsSource(int argc,
+                                                                          char ** argv)
+    : argc_ (argc), argv_ (argv)
+{}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::ProgramOptions
+
+prefix_ senf::console::ProgramOptions::ProgramOptions(int argc, char ** argv,
+                                                      DirectoryNode & root)
+    : detail::BundleMixin (root)
+{
+    add(detail::ProgramOptionsSource::create(argc, argv));
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+prefix_ senf::console::detail::ProgramOptionsSource::ptr
+senf::console::OptionsConfig(int argc, char ** argv)
+{
+    return detail::ProgramOptionsSource::create(argc, argv);
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// 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/Console/ProgramOptions.hh b/Console/ProgramOptions.hh
new file mode 100644 (file)
index 0000000..253f1a8
--- /dev/null
@@ -0,0 +1,74 @@
+// $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 ProgramOptions public header */
+
+#ifndef HH_ProgramOptions_
+#define HH_ProgramOptions_ 1
+
+// Custom includes
+#include "Config.hh"
+
+
+//#include "ProgramOptions.mpp"
+#include "ProgramOptions.ih"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+
+    class ProgramOptions
+        : public detail::BundleMixin
+    {
+    public:
+        ///////////////////////////////////////////////////////////////////////////
+        ///\name Structors and default members
+        ///@{
+        
+        ProgramOptions(int argc, char ** argv, DirectoryNode & root = root());
+
+        ///@}
+        ///////////////////////////////////////////////////////////////////////////
+    };
+
+    void parseOptions(int argc, char ** argv, DirectoryNode & root = root());
+
+    detail::ProgramOptionsSource::ptr OptionsConfig(int argc, char ** argv);
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+#include "ProgramOptions.cci"
+//#include "ProgramOptions.ct"
+//#include "ProgramOptions.cti"
+#endif
+
+\f
+// 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/Console/ProgramOptions.ih b/Console/ProgramOptions.ih
new file mode 100644 (file)
index 0000000..36e9e55
--- /dev/null
@@ -0,0 +1,70 @@
+// $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 ProgramOptions internal header */
+
+#ifndef IH_ProgramOptions_
+#define IH_ProgramOptions_ 1
+
+// Custom includes
+#include "Parse.hh"
+
+///////////////////////////////ih.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+namespace detail {
+    
+    class ProgramOptionsSource : public ConfigSource
+    {
+    public:
+        static ptr create(int argc, char ** argv);
+        
+    private:
+        ProgramOptionsSource(int argc, char ** argv);
+        
+        virtual void v_parse(RestrictedExecutor & executor);
+
+        void parseLongOption(std::string const & arg, RestrictedExecutor & executor);
+        void parseNonOption(std::string const & arg, RestrictedExecutor & executor);
+        
+        int argc_;
+        char ** argv_;
+        CommandParser parser_;
+    };
+
+}}}
+
+///////////////////////////////ih.e////////////////////////////////////////
+#endif
+
+\f
+// 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/Console/ProgramOptions.test.cc b/Console/ProgramOptions.test.cc
new file mode 100644 (file)
index 0000000..2e24c8b
--- /dev/null
@@ -0,0 +1,106 @@
+// $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 ProgramOptions.test unit tests */
+
+//#include "ProgramOptions.test.hh"
+//#include "ProgramOptions.test.ih"
+
+// Custom includes
+#include "ProgramOptions.hh"
+#include "ScopedDirectory.hh"
+#include "ParsedCommand.hh"
+
+#include "../Utils/auto_unit_test.hh"
+#include <boost/test/test_tools.hpp>
+
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+namespace {
+    std::string val1;
+    bool val2 (false);
+
+    void fun1(std::string p)
+    { val1 = p; }
+
+    void fun2()
+    { val2 = true; }
+}
+
+BOOST_AUTO_UNIT_TEST(programOptions)
+{
+    senf::console::ScopedDirectory<> root;
+    senf::console::root().add("root", root);
+
+    root.mkdir("dir1").add("fun1", &fun1);
+    root.add("fun2", &fun2);
+    root.mkdir("name-with-dashes").add("fun-2", &fun2);
+
+    {
+        char * argv[] = { "--dir1-fun1=foo","--fun2" };
+        senf::console::ProgramOptions opts (sizeof(argv)/sizeof(argv[0]), argv, root);
+
+        SENF_CHECK_NO_THROW( opts.parse() );
+        BOOST_CHECK_EQUAL( val1, "foo" );
+        BOOST_CHECK_EQUAL( val2, true );
+    }
+
+    {
+        char * argv[] = { "--d-f=foo","--fun" };
+        senf::console::ProgramOptions opts (sizeof(argv)/sizeof(argv[0]), argv, root);
+
+        val1 = "";
+        val2 = false;
+
+        SENF_CHECK_NO_THROW( opts.parse() );
+        BOOST_CHECK_EQUAL( val1, "foo" );
+        BOOST_CHECK_EQUAL( val2, true );
+    }
+
+    {
+        char * argv[] = { "--name-w-fun" };
+        senf::console::ProgramOptions opts (sizeof(argv)/sizeof(argv[0]), argv, root);
+
+        val1 = "";
+        val2 = false;
+
+        SENF_CHECK_NO_THROW( opts.parse() );
+        BOOST_CHECK_EQUAL( val1, "" );
+        BOOST_CHECK_EQUAL( val2, true );
+    }
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+
+\f
+// 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: