<commit_before>#ifndef ALEPH_GEOMETRY_COVER_TREE_HH__
#define ALEPH_GEOMETRY_COVER_TREE_HH__

#include <iterator>
#include <limits>
#include <memory>
#include <ostream>
#include <queue>
#include <stdexcept>
#include <vector>

// FIXME: remove after debugging
#include <iostream>

#include <cassert>
#include <cmath>

namespace aleph
{

namespace geometry
{

/**
  @class CoverTree
  @brief Generic cover tree data structure

  This class models a cover tree data structure, as described in the
  paper "Cover trees for nearest neighbor" by Beygelzimer et al.; an
  implementation is given at:

    http://hunch.net/~jl/projects/cover_tree/cover_tree.html

  This implementation attempts to be as generic as possible. It uses
  the simplified description of the cover tree, as given by Izbicki,
  Shelton in "Faster Cover Trees".
*/

template <class Point, class Metric> class CoverTree
{
public:

  /**
    Covering constant of the cover tree. It might make sense to change
    this later on in order to improve performance. Some papers set the
    constant to `1.3`.
  */

  constexpr static const double coveringConstant = 2.0;

  class Node
  {
  public:

    /** Creates a new node that stores a point */
    Node( const Point& point, unsigned level )
      : _point( point )
      , _level( level )
    {
      assert( _level >= 1 );
    }

    /** Calculates current covering distance of the node */
    double coveringDistance() const noexcept
    {
      return std::pow( coveringConstant, static_cast<double>( _level ) );
    }

    void addChild( const Point& p )
    {
      _children.push_back( std::unique_ptr<Node>( new Node( p, _level - 1 ) ) );
    }

    /** @returns true if the node is a leaf node */
    bool isLeaf() const noexcept
    {
      return _children.empty();
    }

    void insert( const Point& p )
    {
      auto d = Metric()( _point, p );

      std::cerr << __PRETTY_FUNCTION__ << ": Covering distance           = " << this->coveringDistance() << "\n";
      std::cerr << __PRETTY_FUNCTION__ << ": Distance from point to root = " << d << "\n";

      if( d > this->coveringDistance() )
      {
        while( d > 2 * this->coveringDistance() )
        {
          std::cerr << __PRETTY_FUNCTION__ << ": Distance is bigger than covering distance; need to raise level of tree\n";

          std::stack<const Node*> nodes;
          nodes.push( this );

          const Node* leaf   = nullptr;
          const Node* parent = nullptr;

          while( !nodes.empty() )
          {
            auto&& node = nodes.top();
            for( auto&& child : parent->_children )
            {
              if( child->isLeaf() )
              {
                leaf   = child.get();
                parent = node;
                break;
              }
            }

            nodes.pop();
          }

          // There is no leaf, so there is nothing to do and we just
          // skip to the bottom where we add the current node as the
          // new root of the tree.
          if( !leaf )
            break;

          assert( leaf );
          assert( parent );

          std::cerr << "Oh noes!\n";
          throw "CRAP";

          // - Find any leaf node that is accessible from the current subtree
          // - Remove said leaf $q$
          // - Make the leaf node the new root node, with the remainder of
          // the tree as a child node
        }

        // Make the new point $p$ a new root node with the existing root
        // and, consequently, the remaining tree, as the only child.

        auto oldRoot
          = std::unique_ptr<Node>( new Node( this->_point, this->_level ) );

        for( auto&& child : _children )
          oldRoot->_children.push_back( std::move( child ) );

        _point = p;
        _level = _level + 1;

        _children.clear();
        _children.push_back( std::move( oldRoot ) );

        return;
      }

      return insert_( p );
    }

    /**
      Auxiliary function for performing the recursive insertion of a new
      node into the tree.
    */

    void insert_( const Point& p )
    {
      for( auto&& child : _children )
      {
        auto d = Metric()( child->_point, p );
        if( d <= child->coveringDistance() )
        {
          std::cerr << __PRETTY_FUNCTION__ << ": Recursive enumeration of the tree\n";

          // We found a node in which the new point can be inserted
          // *without* violating the covering invariant.
          child->insert_( p );
          return;
        }
      }

      // Add the new point as a child of the current root node. This
      // might require updating levels.

      if( _children.empty() )
        _level += 1;

      std::cerr << "ADDING POINT " << p << " TO ROOT\n";

      this->addChild( p );
    }

    Point    _point; //< The point stored in the node
    unsigned _level; //< The level of the node (>= 1)

    /**
      All children of the node. Their order depends on the insertion
      order into the data set.
    */

    std::vector< std::unique_ptr<Node> > _children;
  };

  /**
    Inserts a new point into the cover tree. If the tree is empty,
    the new point will become the root of the tree. Else, it shall
    be inserted according to the covering invariant.
  */

  void insert( const Point& p )
  {
    if( !_root )
      _root = std::unique_ptr<Node>( new Node(p,1) );
    else
      _root->insert( p );

    this->updateLevels();
  }

  // Pretty-printing function for the tree; this is only meant for
  // debugging purposes and could conceivably be implemented using
  // `std::ostream`.
  void print( std::ostream& o )
  {
    std::queue<const Node*> nodes;
    nodes.push( _root.get() );

    while( !nodes.empty() )
    {
      {
        auto n = nodes.size();

        for( decltype(n) i = 0; i < n; i++ )
        {
          auto&& node = nodes.front();

          if( i == 0 )
            o << node->_level << ": ";
          else
            o << " ";

          o << node->_point;

          for( auto&& child : node->_children )
            nodes.push( child.get() );

          nodes.pop();
        }

        o << "\n";
      }
    }
  }

private:

  /**
    Updates the levels of a cover tree. This function ensures that all
    information is being represented correctly. It is not mentioned in
    the original paper but appears to be necessary.
  */

  void updateLevels()
  {
    // Forward pass ----------------------------------------------------
    //
    // Determine depth of the tree. This is required in order to assign
    // levels correctly later on.

    unsigned depth = 0;

    std::queue<const Node*> nodes;
    nodes.push( _root.get() );

    while( !nodes.empty() )
    {
      auto n = nodes.size();

      for( decltype(n) i = 0; i < n; i++ )
      {
        auto node = nodes.front();
        nodes.pop();

        for( auto&& child : node->_children )
          nodes.push( child.get() );
      }

      ++depth;
    }

    assert( nodes.empty() );

    // Backward pass ---------------------------------------------------
    //
    // Set the level of the root node and update child levels. Note that
    // all nodes will be traversed because this function is dumb.

    _root->_level = depth;

    nodes.push( _root.get() );

    while( !nodes.empty() )
    {
      {
        auto&& parent = nodes.front();

        for( auto&& child : parent->_children )
        {
          assert( parent->_level >= 1 );

          child->_level = parent->_level - 1;
          nodes.push( child.get() );
        }
      }

      nodes.pop();
    }
  }

  /** Root pointer of the tree */
  std::unique_ptr<Node> _root;
};

} // namespace geometry

} // namespace aleph

#endif
<commit_msg>Removed obsolete function<commit_after>#ifndef ALEPH_GEOMETRY_COVER_TREE_HH__
#define ALEPH_GEOMETRY_COVER_TREE_HH__

#include <iterator>
#include <limits>
#include <memory>
#include <ostream>
#include <queue>
#include <stack>
#include <stdexcept>
#include <vector>

// FIXME: remove after debugging
#include <iostream>

#include <cassert>
#include <cmath>

namespace aleph
{

namespace geometry
{

/**
  @class CoverTree
  @brief Generic cover tree data structure

  This class models a cover tree data structure, as described in the
  paper "Cover trees for nearest neighbor" by Beygelzimer et al.; an
  implementation is given at:

    http://hunch.net/~jl/projects/cover_tree/cover_tree.html

  This implementation attempts to be as generic as possible. It uses
  the simplified description of the cover tree, as given by Izbicki,
  Shelton in "Faster Cover Trees".
*/

template <class Point, class Metric> class CoverTree
{
public:

  /**
    Covering constant of the cover tree. It might make sense to change
    this later on in order to improve performance. Some papers set the
    constant to `1.3`.
  */

  constexpr static const double coveringConstant = 2.0;

  class Node
  {
  public:

    /** Creates a new node that stores a point */
    Node( const Point& point, unsigned level )
      : _point( point )
      , _level( level )
    {
      assert( _level >= 1 );
    }

    /** Calculates current covering distance of the node */
    double coveringDistance() const noexcept
    {
      return std::pow( coveringConstant, static_cast<double>( _level ) );
    }

    /** @returns true if the node is a leaf node */
    bool isLeaf() const noexcept
    {
      return _children.empty();
    }

    void insert( const Point& p )
    {
      auto d = Metric()( _point, p );

      std::cerr << __PRETTY_FUNCTION__ << ": Covering distance           = " << this->coveringDistance() << "\n";
      std::cerr << __PRETTY_FUNCTION__ << ": Distance from point to root = " << d << "\n";

      if( d > this->coveringDistance() )
      {
        while( d > 2 * this->coveringDistance() )
        {
          std::cerr << __PRETTY_FUNCTION__ << ": Distance is bigger than covering distance; need to raise level of tree\n";

          std::stack<const Node*> nodes;
          nodes.push( this );

          const Node* leaf   = nullptr;
          const Node* parent = nullptr;

          while( !nodes.empty() )
          {
            auto&& node = nodes.top();
            for( auto&& child : parent->_children )
            {
              if( child->isLeaf() )
              {
                leaf   = child.get();
                parent = node;
                break;
              }
            }

            nodes.pop();
          }

          // There is no leaf, so there is nothing to do and we just
          // skip to the bottom where we add the current node as the
          // new root of the tree.
          if( !leaf )
            break;

          assert( leaf );
          assert( parent );

          std::cerr << "Oh noes!\n";
          throw "CRAP";

          // - Find any leaf node that is accessible from the current subtree
          // - Remove said leaf $q$
          // - Make the leaf node the new root node, with the remainder of
          // the tree as a child node
        }

        // Make the new point $p$ a new root node with the existing root
        // and, consequently, the remaining tree, as the only child.

        auto oldRoot
          = std::unique_ptr<Node>( new Node( this->_point, this->_level ) );

        for( auto&& child : _children )
          oldRoot->_children.push_back( std::move( child ) );

        _point = p;
        _level = _level + 1;

        _children.clear();
        _children.push_back( std::move( oldRoot ) );

        return;
      }

      return insert_( p );
    }

    /**
      Auxiliary function for performing the recursive insertion of a new
      node into the tree.
    */

    void insert_( const Point& p )
    {
      for( auto&& child : _children )
      {
        auto d = Metric()( child->_point, p );
        if( d <= child->coveringDistance() )
        {
          std::cerr << __PRETTY_FUNCTION__ << ": Recursive enumeration of the tree\n";

          // We found a node in which the new point can be inserted
          // *without* violating the covering invariant.
          child->insert_( p );
          return;
        }
      }

      // Add the new point as a child of the current root node. This
      // might require updating levels.

      if( _children.empty() )
        _level += 1;

      _children.push_back( std::unique_ptr<Node>( new Node( p, _level - 1 ) ) );
    }

    Point    _point; //< The point stored in the node
    unsigned _level; //< The level of the node (>= 1)

    /**
      All children of the node. Their order depends on the insertion
      order into the data set.
    */

    std::vector< std::unique_ptr<Node> > _children;
  };

  /**
    Inserts a new point into the cover tree. If the tree is empty,
    the new point will become the root of the tree. Else, it shall
    be inserted according to the covering invariant.
  */

  void insert( const Point& p )
  {
    if( !_root )
      _root = std::unique_ptr<Node>( new Node(p,1) );
    else
      _root->insert( p );

    this->updateLevels();
  }

  // Pretty-printing function for the tree; this is only meant for
  // debugging purposes and could conceivably be implemented using
  // `std::ostream`.
  void print( std::ostream& o )
  {
    std::queue<const Node*> nodes;
    nodes.push( _root.get() );

    while( !nodes.empty() )
    {
      {
        auto n = nodes.size();

        for( decltype(n) i = 0; i < n; i++ )
        {
          auto&& node = nodes.front();

          if( i == 0 )
            o << node->_level << ": ";
          else
            o << " ";

          o << node->_point;

          for( auto&& child : node->_children )
            nodes.push( child.get() );

          nodes.pop();
        }

        o << "\n";
      }
    }
  }

private:

  /**
    Updates the levels of a cover tree. This function ensures that all
    information is being represented correctly. It is not mentioned in
    the original paper but appears to be necessary.
  */

  void updateLevels()
  {
    // Forward pass ----------------------------------------------------
    //
    // Determine depth of the tree. This is required in order to assign
    // levels correctly later on.

    unsigned depth = 0;

    std::queue<const Node*> nodes;
    nodes.push( _root.get() );

    while( !nodes.empty() )
    {
      auto n = nodes.size();

      for( decltype(n) i = 0; i < n; i++ )
      {
        auto node = nodes.front();
        nodes.pop();

        for( auto&& child : node->_children )
          nodes.push( child.get() );
      }

      ++depth;
    }

    assert( nodes.empty() );

    // Backward pass ---------------------------------------------------
    //
    // Set the level of the root node and update child levels. Note that
    // all nodes will be traversed because this function is dumb.

    _root->_level = depth;

    nodes.push( _root.get() );

    while( !nodes.empty() )
    {
      {
        auto&& parent = nodes.front();

        for( auto&& child : parent->_children )
        {
          assert( parent->_level >= 1 );

          child->_level = parent->_level - 1;
          nodes.push( child.get() );
        }
      }

      nodes.pop();
    }
  }

  /** Root pointer of the tree */
  std::unique_ptr<Node> _root;
};

} // namespace geometry

} // namespace aleph

#endif
<|endoftext|>


<commit_before><commit_msg>Create Fid_Base.cpp<commit_after><|endoftext|>


<commit_before>/*
 * simple_shell_parser.hpp - Parser designed to make simple shells
 *
 *   Copyright 2010 Jesús Torres <jmtorres@ull.es>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SIMPLE_SHELL_PARSER_HPP_
#define SIMPLE_SHELL_PARSER_HPP_

#include <cerrno>
#include <string>
#include <vector>

#include <libintl.h>    // TODO: To use Boost.Locale when available
#define translate(str) ::gettext(str)

//#define BOOST_SPIRIT_DEBUG

#include <boost/algorithm/string/join.hpp>
#include <boost/function.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/phoenix/bind/bind_member_function.hpp>
#include <boost/spirit/home/phoenix/bind/bind_function.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/spirit/home/phoenix/operator/comparison.hpp>
#include <boost/spirit/home/phoenix/operator/if_else.hpp>
#include <boost/spirit/home/phoenix/statement/sequence.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>

#include <cli/callbacks.hpp>
#include <cli/glob.hpp>

namespace cli { namespace parser
{
    //
    // Class Command
    //
    // Stores the information provided by the parser SimpleShellParser
    // that is required for command execution.
    //

    struct Command
    {
        Command() : terminator(NORMAL){}

        struct VariableAssignment
        {
            std::string name;
            std::string value;
        };

        struct StdioRedirection
        {
            enum TypeOfRedirection
            {
                TRUNCATED_INPUT,    // command <filename
                TRUNCATED_OUTPUT,   // command >filename
                APPENDED_OUTPUT     // command >>filename
            };

            TypeOfRedirection type;
            std::string argument;
        };

        enum TypeOfTerminator
        {
            NORMAL,                 // command ;
            BACKGROUNDED,           // command &
            PIPED                   // command1 | command2
        };

        VariableAssignment variable;
        std::vector<std::string> arguments;
        std::vector<StdioRedirection> redirections;
        TypeOfTerminator terminator;
    };

    //
    // Overload of insertion operator (<<) for struct Command
    // It is required to debug the parser rules
    //

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& out, const Command& command)
    {
        return out << "{variable: "     << command.variable
                  << ", arguments: "    << command.arguments
                  << ", redirections: " << command.redirections
                  << ", terminator: "   << command.terminator << '}';
    }

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& out,
        const Command::StdioRedirection& redirection)
    {
        return out << "{type: "     << redirection.type
                  << ", argument: " << redirection.argument << '}';
    }

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& out,
        const Command::VariableAssignment& variable)
    {
        return out << "{name: "  << variable.name
                  << ", value: " << variable.value << '}';
    }
}}

//
// Adaptors from Command classes to Boost.Fusion sequences. They are required
// by the parser SimpleShellParser. Must be defined at global scope.
//

BOOST_FUSION_ADAPT_STRUCT(
    cli::parser::Command::VariableAssignment,
    (std::string, name)
    (std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
    cli::parser::Command::StdioRedirection,
    (cli::parser::Command::StdioRedirection::TypeOfRedirection, type)
    (std::string, argument)
)

BOOST_FUSION_ADAPT_STRUCT(
    cli::parser::Command,
    (cli::parser::Command::VariableAssignment, variable)
    (std::vector<std::string>, arguments)
    (std::vector<cli::parser::Command::StdioRedirection>, redirections)
    (cli::parser::Command::TypeOfTerminator, terminator)
)

namespace cli { namespace parser
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;

    //
    // Class SimpleShellParser
    //

    template <typename Iterator>
    struct SimpleShellParser
        : qi::grammar<Iterator, std::vector<Command>(), ascii::space_type>
    {
        SimpleShellParser() : SimpleShellParser::base_type(start)
        {
            using qi::_1;
            using qi::_2;
            using qi::_3;
            using qi::_4;
            using qi::_a;
            using qi::_val;
            using qi::_r1;
            using qi::eps;
            using qi::eoi;
            using qi::fail;
            using qi::lexeme;
            using qi::lit;
            using qi::on_error;
            using qi::raw;
            using ascii::char_;
            using ascii::space;
            using phoenix::at;
            using phoenix::at_c;
            using phoenix::begin;
            using phoenix::bind;
            using phoenix::construct;
            using phoenix::end;
            using phoenix::empty;
            using phoenix::if_else;
            using phoenix::insert;
            using phoenix::push_back;
            using phoenix::size;
            using phoenix::val;

            eol = eoi;
            character %= char_;
            dereference = '$';
            special %= dereference | redirectors | terminators;
            escape %= '\\' > character;

            name %= char_("a-zA-Z") >> *char_("a-zA-Z0-9");
            variable =
                eps[_a = false] >>
                dereference >> (
                    -lit('{')[_a = true] >
                    name[_val = bind(&Type::variableLookup, *this, _1)]
                ) >> ((eps(_a) > '}') | eps(!_a));

            quotedString %= '\'' >> *(char_ - '\'') > '\'';
            doubleQuotedString = '"' >> *(
                variable                    [_val += _1] |
                (
                    char_('\'')             [push_back(_val, _1)] >>
                    *((char_ - '\'' - '"')  [push_back(_val, _1)]) >>
                    char_('\'')             [push_back(_val, _1)]
                ) |
                (char_ - '"')               [push_back(_val, _1)]
            ) > '"';

            word = +(
                variable            [_val += _1] |
                quotedString        [_val += bind(&Type::globEscape, _1)] |
                doubleQuotedString  [_val += bind(&Type::globEscape, _1)] |
                escape                      [push_back(_val, _1)] |
                (char_ - space - special)   [push_back(_val, _1)]
            );

            expandedWord = word
                [_val = bind(&Type::pathnameExpansion, *this, _1)];
            variableValue = expandedWord[_val = bind(&Type::stringsJoin, _1)];
            unambiguousRedirection = eps(_r1);

            assignment %= name >> '=' >> -variableValue;
            redirection =
                redirectors     [at_c<0>(_val) = _1] >
                expandedWord	[at_c<1>(_val) = at(_1, 0), _a = size(_1)] >
                unambiguousRedirection(_a == 1);
            ending %= terminators;

            // This statement doesn't work as expected in boost 1.45.0
            // command %= assignment || +word || +redirection;
            command = (
                assignment      [at_c<0>(_val) = _1] ||
                (
                    +expandedWord
                    [insert(at_c<1>(_val), end(at_c<1>(_val)),
                        begin(_1), end(_1))]
                ) ||
                (+redirection)  [at_c<2>(_val) = _1]
            ) >> (
                ending          [at_c<3>(_val) = _1, _r1 = true ] |
                eps                                 [_r1 = false]
            );
            expressions = +(
                command(_a)[push_back(_val, _1)] >>
                ((eps(!_a) > eol) | eps(_a))
            ) > eol;

            // start rule with locals doesn't compile in boost 1.44.0
            start %= expressions;

            character.name(translate("character"));
            name.name(translate("name"));
            expandedWord.name(translate("word"));
            variableValue.name(translate("word"));
            unambiguousRedirection.name(translate("unambiguous redirection"));
            eol.name(translate("end-of-line"));

            on_error<fail>(
                start,
                std::cerr
                    << val(::program_invocation_short_name)
                    << val(": ")
                    << val(translate("parse error, expecting"))
                    << val(" ")
                    << _4
                    << val(" ")
                    << val(translate("at"))
                    << val(": ")
                    << if_else(_3 == _2, val(translate("<end-of-line>")),
                        construct<std::string>(_3, _2))
                    << std::endl
            );

//            BOOST_SPIRIT_DEBUG_NODE(name);
//            BOOST_SPIRIT_DEBUG_NODE(variable);
//            BOOST_SPIRIT_DEBUG_NODE(quotedString);
//            BOOST_SPIRIT_DEBUG_NODE(doubleQuotedString);
//            BOOST_SPIRIT_DEBUG_NODE(expandedWord);
//            BOOST_SPIRIT_DEBUG_NODE(word);
//            BOOST_SPIRIT_DEBUG_NODE(assignment);
//            BOOST_SPIRIT_DEBUG_NODE(redirection);
//            BOOST_SPIRIT_DEBUG_NODE(ending);
//            BOOST_SPIRIT_DEBUG_NODE(command);
            BOOST_SPIRIT_DEBUG_NODE(expressions);
        }

        //
        // Parser rules
        //

        struct Redirections
            : qi::symbols<char, Command::StdioRedirection::TypeOfRedirection>
        {
            Redirections()
            {
                add
                    ("<",  Command::StdioRedirection::TRUNCATED_INPUT)
                    (">",  Command::StdioRedirection::TRUNCATED_OUTPUT)
                    (">>", Command::StdioRedirection::APPENDED_OUTPUT)
                ;
            }
        } redirectors;

        struct Terminators
            : qi::symbols<char, Command::TypeOfTerminator>
        {
            Terminators()
            {
                add
                    (";", Command::NORMAL)
                    ("&", Command::BACKGROUNDED)
                    ("|", Command::PIPED)
                ;
            }
        } terminators;

        qi::rule<Iterator> eol;
        qi::rule<Iterator, char()> character;
        qi::rule<Iterator, char()> dereference;
        qi::rule<Iterator, char()> special;
        qi::rule<Iterator, char()> escape;
        qi::rule<Iterator, std::string()> name;
        qi::rule<Iterator, std::string(), qi::locals<bool> > variable;
        qi::rule<Iterator, std::string()> quotedString;
        qi::rule<Iterator, std::string()> doubleQuotedString;
        qi::rule<Iterator, std::string()> word;
        qi::rule<Iterator, std::vector<std::string>()> expandedWord;
        qi::rule<Iterator, std::string()> variableValue;
        qi::rule<Iterator, void(bool)> unambiguousRedirection;
        qi::rule<Iterator, Command::VariableAssignment()> assignment;
        qi::rule<Iterator, Command::StdioRedirection(),
            qi::locals<int>, ascii::space_type> redirection;
        qi::rule<Iterator, Command::TypeOfTerminator(),
            ascii::space_type> ending;
        qi::rule<Iterator, Command(bool&), ascii::space_type> command;
        qi::rule<Iterator, std::vector<Command>(),
            qi::locals<bool>, ascii::space_type> expressions;
        qi::rule<Iterator, std::vector<Command>(), ascii::space_type> start;

        //
        // Callback functions
        //

        typedef SimpleShellParser<Iterator> Type;

        typedef typename callbacks::VariableLookupCallback<Type>::Type
            VariableLookupCallback;
        typedef typename callbacks::PathnameExpansionCallback<Type>::Type
            PathnameExpansionCallback;

        protected:

            //
            // Hook methods
            //

            std::string variableLookup(const std::string& name);
            std::vector<std::string> pathnameExpansion(
                const std::string& pattern);

        private:

            //
            // Callback functions objects
            //

            boost::function<VariableLookupCallback> variableLookupCallback_;
            boost::function<PathnameExpansionCallback>
                pathnameExpansionCallback_;

            //
            // Auxiliary methods
            //

            static std::string globEscape(const std::string& pattern)
                { return glob::Glob::escape(pattern); }

            static std::string stringsJoin(const std::vector<std::string>& v)
                { return boost::algorithm::join(v, std::string(1, ' ')); }
    };

    template <typename Iterator>
    std::string SimpleShellParser<Iterator>::variableLookup(
        const std::string& name)
    {
        return variableLookupCallback_.empty() ?
            std::string() : variableLookupCallback_(name);
    }

    template <typename Iterator>
    std::vector<std::string> SimpleShellParser<Iterator>::pathnameExpansion(
        const std::string& pattern)
    {
        if (! pathnameExpansionCallback_.empty()) {
            return pathnameExpansionCallback_(pattern);
        }

        using namespace glob;

        Glob glob(pattern, Glob::EXPAND_BRACE_EXPRESSIONS |
            Glob::NO_PATH_NAMES_CHECK | Glob::EXPAND_TILDE_WITH_CHECK);

        typename Glob::ErrorsType errors = glob.getErrors();
        for (Glob::ErrorsType::const_iterator iter = errors.begin();
            iter < errors.end(); ++iter)
        {
            std::cerr
                << ::program_invocation_short_name
                << ": "
                << translate("i/o error at")
                << " "
                << iter->first
                << ": "
                << iter->second.message();
        }

        return glob;
    }
}}

#endif /* SIMPLE_SHELL_PARSER_HPP_ */
<commit_msg>show unambiguous redirection error at exact position where it happens.<commit_after>/*
 * simple_shell_parser.hpp - Parser designed to make simple shells
 *
 *   Copyright 2010 Jesús Torres <jmtorres@ull.es>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SIMPLE_SHELL_PARSER_HPP_
#define SIMPLE_SHELL_PARSER_HPP_

#include <cerrno>
#include <string>
#include <vector>

#include <libintl.h>    // TODO: To use Boost.Locale when available
#define translate(str) ::gettext(str)

//#define BOOST_SPIRIT_DEBUG

#include <boost/algorithm/string/join.hpp>
#include <boost/function.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/phoenix/bind/bind_member_function.hpp>
#include <boost/spirit/home/phoenix/bind/bind_function.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/spirit/home/phoenix/operator/comparison.hpp>
#include <boost/spirit/home/phoenix/operator/if_else.hpp>
#include <boost/spirit/home/phoenix/statement/sequence.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>

#include <cli/callbacks.hpp>
#include <cli/glob.hpp>

namespace cli { namespace parser
{
    //
    // Class Command
    //
    // Stores the information provided by the parser SimpleShellParser
    // that is required for command execution.
    //

    struct Command
    {
        Command() : terminator(NORMAL){}

        struct VariableAssignment
        {
            std::string name;
            std::string value;
        };

        struct StdioRedirection
        {
            enum TypeOfRedirection
            {
                TRUNCATED_INPUT,    // command <filename
                TRUNCATED_OUTPUT,   // command >filename
                APPENDED_OUTPUT     // command >>filename
            };

            TypeOfRedirection type;
            std::string argument;
        };

        enum TypeOfTerminator
        {
            NORMAL,                 // command ;
            BACKGROUNDED,           // command &
            PIPED                   // command1 | command2
        };

        VariableAssignment variable;
        std::vector<std::string> arguments;
        std::vector<StdioRedirection> redirections;
        TypeOfTerminator terminator;
    };

    //
    // Overload of insertion operator (<<) for struct Command
    // It is required to debug the parser rules
    //

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& out, const Command& command)
    {
        return out << "{variable: "     << command.variable
                  << ", arguments: "    << command.arguments
                  << ", redirections: " << command.redirections
                  << ", terminator: "   << command.terminator << '}';
    }

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& out,
        const Command::StdioRedirection& redirection)
    {
        return out << "{type: "     << redirection.type
                  << ", argument: " << redirection.argument << '}';
    }

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& out,
        const Command::VariableAssignment& variable)
    {
        return out << "{name: "  << variable.name
                  << ", value: " << variable.value << '}';
    }
}}

//
// Adaptors from Command classes to Boost.Fusion sequences. They are required
// by the parser SimpleShellParser. Must be defined at global scope.
//

BOOST_FUSION_ADAPT_STRUCT(
    cli::parser::Command::VariableAssignment,
    (std::string, name)
    (std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
    cli::parser::Command::StdioRedirection,
    (cli::parser::Command::StdioRedirection::TypeOfRedirection, type)
    (std::string, argument)
)

BOOST_FUSION_ADAPT_STRUCT(
    cli::parser::Command,
    (cli::parser::Command::VariableAssignment, variable)
    (std::vector<std::string>, arguments)
    (std::vector<cli::parser::Command::StdioRedirection>, redirections)
    (cli::parser::Command::TypeOfTerminator, terminator)
)

namespace cli { namespace parser
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;

    //
    // Class SimpleShellParser
    //

    template <typename Iterator>
    struct SimpleShellParser
        : qi::grammar<Iterator, std::vector<Command>(), ascii::space_type>
    {
        SimpleShellParser() : SimpleShellParser::base_type(start)
        {
            using qi::_1;
            using qi::_2;
            using qi::_3;
            using qi::_4;
            using qi::_a;
            using qi::_val;
            using qi::_r1;
            using qi::eps;
            using qi::eoi;
            using qi::fail;
            using qi::lexeme;
            using qi::lit;
            using qi::on_error;
            using qi::raw;
            using ascii::char_;
            using ascii::space;
            using phoenix::at;
            using phoenix::at_c;
            using phoenix::begin;
            using phoenix::bind;
            using phoenix::construct;
            using phoenix::end;
            using phoenix::empty;
            using phoenix::if_else;
            using phoenix::insert;
            using phoenix::push_back;
            using phoenix::size;
            using phoenix::val;

            eol = eoi;
            character %= char_;
            dereference = '$';
            special %= dereference | redirectors | terminators;
            escape %= '\\' > character;

            name %= char_("a-zA-Z") >> *char_("a-zA-Z0-9");
            variable =
                eps[_a = false] >>
                dereference >> (
                    -lit('{')[_a = true] >
                    name[_val = bind(&Type::variableLookup, *this, _1)]
                ) >> ((eps(_a) > '}') | eps(!_a));

            quotedString %= '\'' >> *(char_ - '\'') > '\'';
            doubleQuotedString = '"' >> *(
                variable                    [_val += _1] |
                (
                    char_('\'')             [push_back(_val, _1)] >>
                    *((char_ - '\'' - '"')  [push_back(_val, _1)]) >>
                    char_('\'')             [push_back(_val, _1)]
                ) |
                (char_ - '"')               [push_back(_val, _1)]
            ) > '"';

            word = +(
                variable            [_val += _1] |
                quotedString        [_val += bind(&Type::globEscape, _1)] |
                doubleQuotedString  [_val += bind(&Type::globEscape, _1)] |
                escape                      [push_back(_val, _1)] |
                (char_ - space - special)   [push_back(_val, _1)]
            );

            expandedWord = word
                [_val = bind(&Type::pathnameExpansion, *this, _1)];
            variableValue = expandedWord[_val = bind(&Type::stringsJoin, _1)];
            unambiguousRedirection = eps(_r1);
            redirectionArgument = (
                (
                    &expandedWord[_a = size(_1)] >
                    unambiguousRedirection(_a == 1)

                ) >> expandedWord[_val = at(_1, 0)]
            ) | (eps > expandedWord);

            assignment %= name >> '=' >> -variableValue;
            redirection %= redirectors >> redirectionArgument;
            ending %= terminators;

            // This statement doesn't work as expected in boost 1.45.0
            // command %= assignment || +word || +redirection;
            command = (
                assignment      [at_c<0>(_val) = _1] ||
                (
                    +expandedWord
                    [insert(at_c<1>(_val), end(at_c<1>(_val)),
                        begin(_1), end(_1))]
                ) ||
                (+redirection)  [at_c<2>(_val) = _1]
            ) >> (
                ending          [at_c<3>(_val) = _1, _r1 = true ] |
                eps                                 [_r1 = false]
            );
            expressions = +(
                command(_a)[push_back(_val, _1)] >>
                ((eps(!_a) > eol) | eps(_a))
            ) > eol;

            // start rule with locals doesn't compile in boost 1.44.0
            start %= expressions;

            character.name(translate("character"));
            name.name(translate("name"));
            expandedWord.name(translate("word"));
            variableValue.name(translate("word"));
            unambiguousRedirection.name(translate("unambiguous redirection"));
            eol.name(translate("end-of-line"));

            on_error<fail>(
                start,
                std::cerr
                    << val(::program_invocation_short_name)
                    << val(": ")
                    << val(translate("parse error, expecting"))
                    << val(" ")
                    << _4
                    << val(" ")
                    << val(translate("at"))
                    << val(": ")
                    << if_else(_3 == _2, val(translate("<end-of-line>")),
                        construct<std::string>(_3, _2))
                    << std::endl
            );

//            BOOST_SPIRIT_DEBUG_NODE(name);
//            BOOST_SPIRIT_DEBUG_NODE(variable);
//            BOOST_SPIRIT_DEBUG_NODE(quotedString);
//            BOOST_SPIRIT_DEBUG_NODE(doubleQuotedString);
//            BOOST_SPIRIT_DEBUG_NODE(expandedWord);
//            BOOST_SPIRIT_DEBUG_NODE(word);
//            BOOST_SPIRIT_DEBUG_NODE(assignment);
//            BOOST_SPIRIT_DEBUG_NODE(redirection);
//            BOOST_SPIRIT_DEBUG_NODE(ending);
//            BOOST_SPIRIT_DEBUG_NODE(command);
            BOOST_SPIRIT_DEBUG_NODE(expressions);
        }

        //
        // Parser rules
        //

        struct Redirections
            : qi::symbols<char, Command::StdioRedirection::TypeOfRedirection>
        {
            Redirections()
            {
                add
                    ("<",  Command::StdioRedirection::TRUNCATED_INPUT)
                    (">",  Command::StdioRedirection::TRUNCATED_OUTPUT)
                    (">>", Command::StdioRedirection::APPENDED_OUTPUT)
                ;
            }
        } redirectors;

        struct Terminators
            : qi::symbols<char, Command::TypeOfTerminator>
        {
            Terminators()
            {
                add
                    (";", Command::NORMAL)
                    ("&", Command::BACKGROUNDED)
                    ("|", Command::PIPED)
                ;
            }
        } terminators;

        qi::rule<Iterator> eol;
        qi::rule<Iterator, char()> character;
        qi::rule<Iterator, char()> dereference;
        qi::rule<Iterator, char()> special;
        qi::rule<Iterator, char()> escape;
        qi::rule<Iterator, std::string()> name;
        qi::rule<Iterator, std::string(), qi::locals<bool> > variable;
        qi::rule<Iterator, std::string()> quotedString;
        qi::rule<Iterator, std::string()> doubleQuotedString;
        qi::rule<Iterator, std::string()> word;
        qi::rule<Iterator, std::vector<std::string>()> expandedWord;
        qi::rule<Iterator, std::string()> variableValue;
        qi::rule<Iterator, void(bool)> unambiguousRedirection;
        qi::rule<Iterator, std::string(),
            qi::locals<int> > redirectionArgument;
        qi::rule<Iterator, Command::VariableAssignment()> assignment;
        qi::rule<Iterator, Command::StdioRedirection(),
            ascii::space_type> redirection;
        qi::rule<Iterator, Command::TypeOfTerminator(),
            ascii::space_type> ending;
        qi::rule<Iterator, Command(bool&), ascii::space_type> command;
        qi::rule<Iterator, std::vector<Command>(),
            qi::locals<bool>, ascii::space_type> expressions;
        qi::rule<Iterator, std::vector<Command>(), ascii::space_type> start;

        //
        // Callback functions
        //

        typedef SimpleShellParser<Iterator> Type;

        typedef typename callbacks::VariableLookupCallback<Type>::Type
            VariableLookupCallback;
        typedef typename callbacks::PathnameExpansionCallback<Type>::Type
            PathnameExpansionCallback;

        protected:

            //
            // Hook methods
            //

            std::string variableLookup(const std::string& name);
            std::vector<std::string> pathnameExpansion(
                const std::string& pattern);

        private:

            //
            // Callback functions objects
            //

            boost::function<VariableLookupCallback> variableLookupCallback_;
            boost::function<PathnameExpansionCallback>
                pathnameExpansionCallback_;

            //
            // Auxiliary methods
            //

            static std::string globEscape(const std::string& pattern)
                { return glob::Glob::escape(pattern); }

            static std::string stringsJoin(const std::vector<std::string>& v)
                { return boost::algorithm::join(v, std::string(1, ' ')); }
    };

    template <typename Iterator>
    std::string SimpleShellParser<Iterator>::variableLookup(
        const std::string& name)
    {
        return variableLookupCallback_.empty() ?
            std::string() : variableLookupCallback_(name);
    }

    template <typename Iterator>
    std::vector<std::string> SimpleShellParser<Iterator>::pathnameExpansion(
        const std::string& pattern)
    {
        if (! pathnameExpansionCallback_.empty()) {
            return pathnameExpansionCallback_(pattern);
        }

        using namespace glob;

        Glob glob(pattern, Glob::EXPAND_BRACE_EXPRESSIONS |
            Glob::NO_PATH_NAMES_CHECK | Glob::EXPAND_TILDE_WITH_CHECK);

        typename Glob::ErrorsType errors = glob.getErrors();
        for (Glob::ErrorsType::const_iterator iter = errors.begin();
            iter < errors.end(); ++iter)
        {
            std::cerr
                << ::program_invocation_short_name
                << ": "
                << translate("i/o error at")
                << " "
                << iter->first
                << ": "
                << iter->second.message();
        }

        return glob;
    }
}}

#endif /* SIMPLE_SHELL_PARSER_HPP_ */
<|endoftext|>


<commit_before>
// Copyright (c) 2014-2017 niXman (i dot nixman dog gmail dot com). All
// rights reserved.
//
// This file is part of CONFIG-CTOR(https://github.com/niXman/config-ctor) project.
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
//
//
// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef __config_ctor__config_ctor_hpp
#define __config_ctor__config_ctor_hpp

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/comparison/greater_equal.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/preprocessor/tuple/elem.hpp>

#include <cstdint>
#include <string>
#include <iosfwd>
#include <map>

#ifdef _WIN32
#   include <windows.h>
#else
#   include <unistd.h>
#   include <stdlib.h>
#endif

/***************************************************************************/

namespace construct_config {
namespace {

// for other types (strings)
template<bool ok>
struct get_concrete_value {
    template<typename T>
    static T get(const char *key, const boost::property_tree::ptree &ini, const char *default_value) {
        std::string res = default_value != nullptr
           ? ini.get<T>(key, default_value)
           : ini.get<T>(key)
        ;

        static auto get_home = []() -> std::string { return ::getenv("HOME"); };
        static auto get_user = []() -> std::string { return ::getenv("USER"); };
        static auto get_cwd  = []() -> std::string {
            char buf[1024];
            return ::getcwd(buf, sizeof(buf));
        };
        static auto get_temp = []() -> std::string {
        #ifdef _WIN32
            char buf[MAX_PATH+1+1];
            ::GetTempPath(sizeof(buf), buf);
            return buf;
        #elif defined(__linux__)
            if (const char *temp = ::getenv("TMPDIR")) {
                return temp;
            } else if (const char *temp = ::getenv("TEMP")) {
                return temp;
            } else if (const char *temp = ::getenv("TMP")) {
                return temp;
            }
            return "/tmp";
        #else
        #   error UNKNOWN HOST
        #endif
        };
        static auto get_pid  = []() -> std::string {
            ::pid_t pid = ::getpid();
            return std::to_string(pid);
        };
        static auto get_path = []() -> std::string { return ::getenv("PATH"); };
        static auto get_proc_name = []() -> std::string {
            char buf[1024] = "\0";
        #ifdef _WIN32
            ::GetModuleFileName(nullptr, buf, sizeof(buf)-1);
            const char *p = std::strrchr(buf, '\\');
        #elif defined(__linux__)
            ::readlink("/proc/self/exe", buf, sizeof(buf)-1);
            const char *p = std::strrchr(buf, '/');
        #else
        #   error UNKNOWN HOST
        #endif
            return p ? p+1 : buf;
        };

        static const std::map<std::string, std::function<std::string()>> map = {
             {"{HOME}", std::move(get_home)}
            ,{"{USER}", std::move(get_user)}
            ,{"{CWD}" , std::move(get_cwd )}
            ,{"{TEMP}", std::move(get_temp)}
            ,{"{PID}" , std::move(get_pid )}
            ,{"{PATH}", std::move(get_path)}
            ,{"{PROC}", std::move(get_proc_name)}
        };

        static auto replace = [](std::string &str, const std::string &ostr, const std::string &nstr) {
            std::string::size_type pos = 0u;
            while ( (pos = str.find(ostr, pos)) != std::string::npos ) {
                str.replace(pos, ostr.length(), nstr);
                pos += nstr.length();
            }
        };

        for (const auto &it: map) {
            replace(res, it.first, it.second());
        }

        return res;
    }
};

// specialization for arithmetic types
template<>
struct get_concrete_value<true> {
    template<typename T>
    static T get(const char *key, const boost::property_tree::ptree &ini, const char *default_value) {
        // handle bool types
        if (std::is_same<T, bool>::value) {
            return default_value != nullptr
               ? ini.get<bool>(key, (std::strcmp(default_value, "true") == 0))
               : ini.get<bool>(key)
            ;
        }

        // handle other arithmetic types
        std::string val = default_value != nullptr
            ? ini.get<std::string>(key, default_value)
            : ini.get<std::string>(key)
        ;
        if (val.empty()) return T{};

        if ((std::is_signed<T>::value || std::is_unsigned<T>::value) && !std::is_floating_point<T>::value) {
            std::size_t mult = 1u;
            switch (val.back()) {
                case 't': case 'T': mult *= 1024u;
                case 'g': case 'G': mult *= 1024u;
                case 'm': case 'M': mult *= 1024u;
                case 'k': case 'K': mult *= 1024u;

                val.pop_back();
            }

            return static_cast<T>((std::is_signed<T>::value ? std::stol(val) : std::stoul(val)) * mult);
        }

        return static_cast<T>(std::stod(val));
    }
};

} // anon ns

template<typename T>
static T get_value(const char *key, const boost::property_tree::ptree &cfg, const char *default_value) {
    using TT = typename std::remove_cv<T>::type;
    return get_concrete_value<std::is_arithmetic<TT>::value>::template get<TT>(key, cfg, default_value);
}

template<bool ok>
struct print_value {
    template<typename T>
    static void print(const T &v, std::ostream &os) {
        os << '\"' << v << '\"';
    }
};

template<>
struct print_value<true> {
    template<typename T>
    static void print(const T &v, std::ostream &os) {
        os << std::boolalpha << v;
    }
};

} // ns construct_config

/***************************************************************************/

#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_X(...) \
    ((__VA_ARGS__)) _CONSTRUCT_CONFIG__WRAP_SEQUENCE_Y
#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_Y(...) \
    ((__VA_ARGS__)) _CONSTRUCT_CONFIG__WRAP_SEQUENCE_X

#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_X0
#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_Y0

/***************************************************************************/

#define _CONSTRUCT_CONFIG__GENERATE_MEMBERS(unused, data, idx, elem) \
    BOOST_PP_TUPLE_ELEM(0, elem) /* type */ BOOST_PP_TUPLE_ELEM(1, elem) /* var name */ ;

#define _CONSTRUCT_CONFIG__INIT_MEMBERS_WITH_DEFAULT(...) \
    ,BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(2, __VA_ARGS__))

#define _CONSTRUCT_CONFIG__INIT_MEMBERS_WITHOUT_DEFAULT(...) \
    ,nullptr

#define _CONSTRUCT_CONFIG__INIT_MEMBERS(unused, data, idx, elem) \
    BOOST_PP_COMMA_IF(idx) \
        ::construct_config::get_value<BOOST_PP_TUPLE_ELEM(0, elem)>( \
             BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)) \
            ,cfg \
            BOOST_PP_IF( \
                 BOOST_PP_GREATER_EQUAL(BOOST_PP_TUPLE_SIZE(elem), 3) \
                ,_CONSTRUCT_CONFIG__INIT_MEMBERS_WITH_DEFAULT \
                ,_CONSTRUCT_CONFIG__INIT_MEMBERS_WITHOUT_DEFAULT \
            )(elem) \
        )

#define _CONSTRUCT_CONFIG__ENUM_MEMBERS(unused, data, idx, elem) \
    os << BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)) "="; \
    ::construct_config::print_value< \
        std::is_arithmetic<decltype(BOOST_PP_TUPLE_ELEM(1, elem))>::value \
    >::print(BOOST_PP_TUPLE_ELEM(1, elem), os); \
    os << std::endl;

/***************************************************************************/

#define _CONSTRUCT_CONFIG__ENUM_WRITE_MEMBERS(unused, data, idx, elem) \
    ptree.put(BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)), cfg.BOOST_PP_TUPLE_ELEM(1, elem));

/***************************************************************************/

#define _CONSTRUCT_CONFIG__GENERATE_STRUCT(fmt, name, seq) \
    struct name { \
        BOOST_PP_SEQ_FOR_EACH_I( \
             _CONSTRUCT_CONFIG__GENERATE_MEMBERS \
            ,~ \
            ,seq \
        ) \
        \
        static name read(std::istream &is) { \
            boost::property_tree::ptree cfg; \
            boost::property_tree::read_##fmt(is, cfg); \
            \
            return { \
                BOOST_PP_SEQ_FOR_EACH_I( \
                     _CONSTRUCT_CONFIG__INIT_MEMBERS \
                    ,~ \
                    ,seq \
                ) \
            }; \
        } \
        static name read(const std::string &fname) { \
            boost::property_tree::ptree cfg; \
            boost::property_tree::read_##fmt(fname, cfg); \
            \
            return { \
                BOOST_PP_SEQ_FOR_EACH_I( \
                     _CONSTRUCT_CONFIG__INIT_MEMBERS \
                    ,~ \
                    ,seq \
                ) \
            }; \
        } \
        \
        static void write(const std::string &fname, const name &cfg) { \
            boost::property_tree::ptree ptree; \
            BOOST_PP_SEQ_FOR_EACH_I( \
                 _CONSTRUCT_CONFIG__ENUM_WRITE_MEMBERS \
                ,~ \
                ,seq \
            ) \
            boost::property_tree::write_##fmt(fname, ptree); \
        } \
        static void write(std::ostream &os, const name &cfg) { \
            boost::property_tree::ptree ptree; \
            BOOST_PP_SEQ_FOR_EACH_I( \
                 _CONSTRUCT_CONFIG__ENUM_WRITE_MEMBERS \
                ,~ \
                ,seq \
            ) \
            boost::property_tree::write_##fmt(os, ptree); \
        } \
        \
        void dump(std::ostream &os) const { \
            BOOST_PP_SEQ_FOR_EACH_I( \
                 _CONSTRUCT_CONFIG__ENUM_MEMBERS \
                ,~ \
                ,seq \
            ) \
        } \
    };

/***************************************************************************/

#define CONSTRUCT_CONFIG(\
     fmt  /* config file format */ \
, name /* config struct name */ \
, seq  /* sequence of vars */ \
) \
    _CONSTRUCT_CONFIG__GENERATE_STRUCT( \
         fmt \
        ,name \
        ,BOOST_PP_CAT(_CONSTRUCT_CONFIG__WRAP_SEQUENCE_X seq, 0) \
    )

#define CONSTRUCT_INI_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(ini, name, seq)

#define CONSTRUCT_JSON_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(json, name, seq)

#define CONSTRUCT_XML_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(xml, name, seq)

#define CONSTRUCT_INFO_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(info, name, seq)

/***************************************************************************/

#endif // __config_ctor__config_ctor_hpp
<commit_msg>Update config-ctor.hpp<commit_after>
// Copyright (c) 2014-2017 niXman (i dot nixman dog gmail dot com). All
// rights reserved.
//
// This file is part of CONFIG-CTOR(https://github.com/niXman/config-ctor) project.
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
//
//
// Boost Software License - Version 1.0 - August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef __config_ctor__config_ctor_hpp
#define __config_ctor__config_ctor_hpp

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/comparison/greater_equal.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/preprocessor/tuple/elem.hpp>

#include <cstdint>
#include <string>
#include <iosfwd>
#include <map>

#ifdef _WIN32
#   include <windows.h>
#else
#   include <unistd.h>
#   include <stdlib.h>
#endif

/***************************************************************************/

namespace construct_config {
namespace {

// for other types (strings)
template<bool ok>
struct get_concrete_value {
    template<typename T>
    static T get(const char *key, const boost::property_tree::ptree &ini, const char *default_value) {
        std::string res = default_value != nullptr
           ? ini.get<T>(key, default_value)
           : ini.get<T>(key)
        ;

        static auto get_home = []() -> std::string {
        #ifdef _WIN32
            return ::getenv("HOMEPATH");
        #elif defined(__linux__)
            return ::getenv("HOME");
        #else
        #   error UNKNOWN HOST
        #endif
        };
        static auto get_user = []() -> std::string {
        #ifdef _WIN32
            return ::getenv("USERNAME");
        #elif defined(__linux__)
            return ::getenv("USER");
        #else
        #   error UNKNOWN HOST
        #endif
        };
        static auto get_cwd  = []() -> std::string {
            char buf[1024];
            return ::getcwd(buf, sizeof(buf));
        };
        static auto get_temp = []() -> std::string {
        #ifdef _WIN32
            char buf[MAX_PATH+1+1];
            ::GetTempPath(sizeof(buf), buf);
            return buf;
        #elif defined(__linux__)
            if (const char *temp = ::getenv("TMPDIR")) {
                return temp;
            } else if (const char *temp = ::getenv("TEMP")) {
                return temp;
            } else if (const char *temp = ::getenv("TMP")) {
                return temp;
            }
            return "/tmp";
        #else
        #   error UNKNOWN HOST
        #endif
        };
        static auto get_pid  = []() -> std::string {
            ::pid_t pid = ::getpid();
            return std::to_string(pid);
        };
        static auto get_path = []() -> std::string { return ::getenv("PATH"); };
        static auto get_proc_name = []() -> std::string {
            char buf[1024] = "\0";
        #ifdef _WIN32
            ::GetModuleFileName(nullptr, buf, sizeof(buf)-1);
            const char *p = std::strrchr(buf, '\\');
        #elif defined(__linux__)
            ::readlink("/proc/self/exe", buf, sizeof(buf)-1);
            const char *p = std::strrchr(buf, '/');
        #else
        #   error UNKNOWN HOST
        #endif
            return p ? p+1 : buf;
        };

        static const std::map<std::string, std::function<std::string()>> map = {
             {"{HOME}", std::move(get_home)}
            ,{"{USER}", std::move(get_user)}
            ,{"{CWD}" , std::move(get_cwd )}
            ,{"{TEMP}", std::move(get_temp)}
            ,{"{PID}" , std::move(get_pid )}
            ,{"{PATH}", std::move(get_path)}
            ,{"{PROC}", std::move(get_proc_name)}
        };

        static auto replace = [](std::string &str, const std::string &ostr, const std::string &nstr) {
            std::string::size_type pos = 0u;
            while ( (pos = str.find(ostr, pos)) != std::string::npos ) {
                str.replace(pos, ostr.length(), nstr);
                pos += nstr.length();
            }
        };

        for (const auto &it: map) {
            replace(res, it.first, it.second());
        }

        return res;
    }
};

// specialization for arithmetic types
template<>
struct get_concrete_value<true> {
    template<typename T>
    static T get(const char *key, const boost::property_tree::ptree &ini, const char *default_value) {
        // handle bool types
        if (std::is_same<T, bool>::value) {
            return default_value != nullptr
               ? ini.get<bool>(key, (std::strcmp(default_value, "true") == 0))
               : ini.get<bool>(key)
            ;
        }

        // handle other arithmetic types
        std::string val = default_value != nullptr
            ? ini.get<std::string>(key, default_value)
            : ini.get<std::string>(key)
        ;
        if (val.empty()) return T{};

        if ((std::is_signed<T>::value || std::is_unsigned<T>::value) && !std::is_floating_point<T>::value) {
            std::size_t mult = 1u;
            switch (val.back()) {
                case 't': case 'T': mult *= 1024u;
                case 'g': case 'G': mult *= 1024u;
                case 'm': case 'M': mult *= 1024u;
                case 'k': case 'K': mult *= 1024u;

                val.pop_back();
            }

            return static_cast<T>((std::is_signed<T>::value ? std::stol(val) : std::stoul(val)) * mult);
        }

        return static_cast<T>(std::stod(val));
    }
};

} // anon ns

template<typename T>
static T get_value(const char *key, const boost::property_tree::ptree &cfg, const char *default_value) {
    using TT = typename std::remove_cv<T>::type;
    return get_concrete_value<std::is_arithmetic<TT>::value>::template get<TT>(key, cfg, default_value);
}

template<bool ok>
struct print_value {
    template<typename T>
    static void print(const T &v, std::ostream &os) {
        os << '\"' << v << '\"';
    }
};

template<>
struct print_value<true> {
    template<typename T>
    static void print(const T &v, std::ostream &os) {
        os << std::boolalpha << v;
    }
};

} // ns construct_config

/***************************************************************************/

#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_X(...) \
    ((__VA_ARGS__)) _CONSTRUCT_CONFIG__WRAP_SEQUENCE_Y
#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_Y(...) \
    ((__VA_ARGS__)) _CONSTRUCT_CONFIG__WRAP_SEQUENCE_X

#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_X0
#define _CONSTRUCT_CONFIG__WRAP_SEQUENCE_Y0

/***************************************************************************/

#define _CONSTRUCT_CONFIG__GENERATE_MEMBERS(unused, data, idx, elem) \
    BOOST_PP_TUPLE_ELEM(0, elem) /* type */ BOOST_PP_TUPLE_ELEM(1, elem) /* var name */ ;

#define _CONSTRUCT_CONFIG__INIT_MEMBERS_WITH_DEFAULT(...) \
    ,BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(2, __VA_ARGS__))

#define _CONSTRUCT_CONFIG__INIT_MEMBERS_WITHOUT_DEFAULT(...) \
    ,nullptr

#define _CONSTRUCT_CONFIG__INIT_MEMBERS(unused, data, idx, elem) \
    BOOST_PP_COMMA_IF(idx) \
        ::construct_config::get_value<BOOST_PP_TUPLE_ELEM(0, elem)>( \
             BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)) \
            ,cfg \
            BOOST_PP_IF( \
                 BOOST_PP_GREATER_EQUAL(BOOST_PP_TUPLE_SIZE(elem), 3) \
                ,_CONSTRUCT_CONFIG__INIT_MEMBERS_WITH_DEFAULT \
                ,_CONSTRUCT_CONFIG__INIT_MEMBERS_WITHOUT_DEFAULT \
            )(elem) \
        )

#define _CONSTRUCT_CONFIG__ENUM_MEMBERS(unused, data, idx, elem) \
    os << BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)) "="; \
    ::construct_config::print_value< \
        std::is_arithmetic<decltype(BOOST_PP_TUPLE_ELEM(1, elem))>::value \
    >::print(BOOST_PP_TUPLE_ELEM(1, elem), os); \
    os << std::endl;

/***************************************************************************/

#define _CONSTRUCT_CONFIG__ENUM_WRITE_MEMBERS(unused, data, idx, elem) \
    ptree.put(BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(1, elem)), cfg.BOOST_PP_TUPLE_ELEM(1, elem));

/***************************************************************************/

#define _CONSTRUCT_CONFIG__GENERATE_STRUCT(fmt, name, seq) \
    struct name { \
        BOOST_PP_SEQ_FOR_EACH_I( \
             _CONSTRUCT_CONFIG__GENERATE_MEMBERS \
            ,~ \
            ,seq \
        ) \
        \
        static name read(std::istream &is) { \
            boost::property_tree::ptree cfg; \
            boost::property_tree::read_##fmt(is, cfg); \
            \
            return { \
                BOOST_PP_SEQ_FOR_EACH_I( \
                     _CONSTRUCT_CONFIG__INIT_MEMBERS \
                    ,~ \
                    ,seq \
                ) \
            }; \
        } \
        static name read(const std::string &fname) { \
            boost::property_tree::ptree cfg; \
            boost::property_tree::read_##fmt(fname, cfg); \
            \
            return { \
                BOOST_PP_SEQ_FOR_EACH_I( \
                     _CONSTRUCT_CONFIG__INIT_MEMBERS \
                    ,~ \
                    ,seq \
                ) \
            }; \
        } \
        \
        static void write(const std::string &fname, const name &cfg) { \
            boost::property_tree::ptree ptree; \
            BOOST_PP_SEQ_FOR_EACH_I( \
                 _CONSTRUCT_CONFIG__ENUM_WRITE_MEMBERS \
                ,~ \
                ,seq \
            ) \
            boost::property_tree::write_##fmt(fname, ptree); \
        } \
        static void write(std::ostream &os, const name &cfg) { \
            boost::property_tree::ptree ptree; \
            BOOST_PP_SEQ_FOR_EACH_I( \
                 _CONSTRUCT_CONFIG__ENUM_WRITE_MEMBERS \
                ,~ \
                ,seq \
            ) \
            boost::property_tree::write_##fmt(os, ptree); \
        } \
        \
        void dump(std::ostream &os) const { \
            BOOST_PP_SEQ_FOR_EACH_I( \
                 _CONSTRUCT_CONFIG__ENUM_MEMBERS \
                ,~ \
                ,seq \
            ) \
        } \
    };

/***************************************************************************/

#define CONSTRUCT_CONFIG(\
     fmt  /* config file format */ \
, name /* config struct name */ \
, seq  /* sequence of vars */ \
) \
    _CONSTRUCT_CONFIG__GENERATE_STRUCT( \
         fmt \
        ,name \
        ,BOOST_PP_CAT(_CONSTRUCT_CONFIG__WRAP_SEQUENCE_X seq, 0) \
    )

#define CONSTRUCT_INI_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(ini, name, seq)

#define CONSTRUCT_JSON_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(json, name, seq)

#define CONSTRUCT_XML_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(xml, name, seq)

#define CONSTRUCT_INFO_CONFIG(name, seq) \
    CONSTRUCT_CONFIG(info, name, seq)

/***************************************************************************/

#endif // __config_ctor__config_ctor_hpp
<|endoftext|>


<commit_before>/*
    This file is part of Akregator.

    Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net>
                  2005 Frank Osterfeld <osterfeld@kde.org>

    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "feediconmanager.h"

#include <kapplication.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <k3staticdeleter.h>
#include <kurl.h>

#include <QIcon>
#include <QList>
#include <QMultiHash>
#include <QPixmap>
#include <QtDBus/QtDBus>

#include <cassert>

#define FAVICONINTERFACE "org.kde.FavIcon"

using namespace Akregator;

FaviconListener::~FaviconListener() {}

class FeedIconManager::Private
{
    FeedIconManager* const q;
public:

    static FeedIconManager* m_instance;

    explicit Private( FeedIconManager* qq );
    ~Private();

    void loadIcon( const QString& url );
    QString iconLocation( const KUrl& ) const;

    QHash<FaviconListener*,QString> m_listeners;
    QMultiHash<QString, FaviconListener*> urlDict;
    QDBusInterface *m_favIconsModule;
};

namespace {

QString getIconUrl( const KUrl& url )
{
    return "http://" + url.host() + '/';
}

}

FeedIconManager::Private::Private( FeedIconManager* qq ) : q( qq )
{
    QDBusConnection::sessionBus().registerObject("/FeedIconManager", q, QDBusConnection::ExportScriptableSlots);
    m_favIconsModule = new QDBusInterface("org.kde.kded", "/modules/favicons", FAVICONINTERFACE);
    Q_ASSERT( m_favIconsModule );
    q->connect( m_favIconsModule, SIGNAL( iconChanged( bool, QString, QString ) ),
                q, SLOT( slotIconChanged( bool, QString, QString ) ) );
}

FeedIconManager::Private::~Private()
{
    delete m_favIconsModule;
}

FeedIconManager *FeedIconManager::Private::m_instance = 0;

QString FeedIconManager::Private::iconLocation(const KUrl & url) const
{
    QDBusReply<QString> reply = m_favIconsModule->call( "iconForUrl", url.url() );
    return reply.isValid() ? reply.value() : QString();
}

void FeedIconManager::Private::loadIcon( const QString & url_ )
{
    const KUrl url(url_);

    QString iconFile = iconLocation( url );

    if ( iconFile.isEmpty() ) // cache miss
    {
        const QDBusReply<void> reply = m_favIconsModule->call( "downloadHostIcon", url.url() );
        if ( !reply.isValid() )
            kWarning() << "Couldn't reach favicon service. Request favicon for " << url << " failed";
    }
    else {
        q->slotIconChanged( false, url.host(), iconFile );
    }
}

static K3StaticDeleter<FeedIconManager> feediconmanagersd;

FeedIconManager* FeedIconManager::self()
{
    if (!Private::m_instance)
        Private::m_instance = feediconmanagersd.setObject(Private::m_instance, new FeedIconManager);
    return Private::m_instance;
}

void FeedIconManager::addListener( const KUrl& url, FaviconListener* listener )
{
    assert( listener );
    removeListener( listener );
    const QString iconUrl = getIconUrl( url );
    d->m_listeners.insert( listener, iconUrl );
    d->urlDict.insert( url.host(), listener );
    QMetaObject::invokeMethod( this, "loadIcon", Qt::QueuedConnection, Q_ARG( QString, iconUrl ) );
}

void FeedIconManager::removeListener( FaviconListener* listener )
{
    assert( listener );
    if ( !d->m_listeners.contains( listener ) )
        return;
    const QString url = d->m_listeners.value( listener );
    d->urlDict.remove( url, listener );
    d->m_listeners.remove( listener );
}

FeedIconManager::FeedIconManager()
:  QObject(), d( new Private( this ) )
{
}

FeedIconManager::~FeedIconManager()
{
    delete d;
}

void FeedIconManager::slotIconChanged( bool isHost,
                                       const QString& hostOrUrl,
                                       const QString& iconName )
{
    Q_UNUSED( isHost );
    const QIcon icon( KGlobal::dirs()->findResource( "cache", iconName+".png" ) );
    Q_FOREACH( FaviconListener* l, d->urlDict.values( hostOrUrl ) )
        l->setFavicon( icon );
}

#include "feediconmanager.moc"
<commit_msg>do not crash when opening (stale Frame* pointers)<commit_after>/*
    This file is part of Akregator.

    Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net>
                  2005 Frank Osterfeld <osterfeld@kde.org>

    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "feediconmanager.h"

#include <kapplication.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <k3staticdeleter.h>
#include <kurl.h>

#include <QIcon>
#include <QList>
#include <QMultiHash>
#include <QPixmap>
#include <QtDBus/QtDBus>

#include <cassert>

#define FAVICONINTERFACE "org.kde.FavIcon"

using namespace Akregator;

FaviconListener::~FaviconListener() {}

class FeedIconManager::Private
{
    FeedIconManager* const q;
public:

    static FeedIconManager* m_instance;

    explicit Private( FeedIconManager* qq );
    ~Private();

    void loadIcon( const QString& url );
    QString iconLocation( const KUrl& ) const;

    QHash<FaviconListener*,QString> m_listeners;
    QMultiHash<QString, FaviconListener*> urlDict;
    QDBusInterface *m_favIconsModule;
};

namespace {

QString getIconUrl( const KUrl& url )
{
    return "http://" + url.host() + '/';
}

}

FeedIconManager::Private::Private( FeedIconManager* qq ) : q( qq )
{
    QDBusConnection::sessionBus().registerObject("/FeedIconManager", q, QDBusConnection::ExportScriptableSlots);
    m_favIconsModule = new QDBusInterface("org.kde.kded", "/modules/favicons", FAVICONINTERFACE);
    Q_ASSERT( m_favIconsModule );
    q->connect( m_favIconsModule, SIGNAL( iconChanged( bool, QString, QString ) ),
                q, SLOT( slotIconChanged( bool, QString, QString ) ) );
}

FeedIconManager::Private::~Private()
{
    delete m_favIconsModule;
}

FeedIconManager *FeedIconManager::Private::m_instance = 0;

QString FeedIconManager::Private::iconLocation(const KUrl & url) const
{
    QDBusReply<QString> reply = m_favIconsModule->call( "iconForUrl", url.url() );
    return reply.isValid() ? reply.value() : QString();
}

void FeedIconManager::Private::loadIcon( const QString & url_ )
{
    const KUrl url(url_);

    QString iconFile = iconLocation( url );

    if ( iconFile.isEmpty() ) // cache miss
    {
        const QDBusReply<void> reply = m_favIconsModule->call( "downloadHostIcon", url.url() );
        if ( !reply.isValid() )
            kWarning() << "Couldn't reach favicon service. Request favicon for " << url << " failed";
    }
    else {
        q->slotIconChanged( false, url.host(), iconFile );
    }
}

static K3StaticDeleter<FeedIconManager> feediconmanagersd;

FeedIconManager* FeedIconManager::self()
{
    if (!Private::m_instance)
        Private::m_instance = feediconmanagersd.setObject(Private::m_instance, new FeedIconManager);
    return Private::m_instance;
}

void FeedIconManager::addListener( const KUrl& url, FaviconListener* listener )
{
    assert( listener );
    removeListener( listener );
    const QString iconUrl = getIconUrl( url );
    d->m_listeners.insert( listener, iconUrl );
    d->urlDict.insert( iconUrl, listener );
    d->urlDict.insert( url.host(), listener );
    QMetaObject::invokeMethod( this, "loadIcon", Qt::QueuedConnection, Q_ARG( QString, iconUrl ) );
}

void FeedIconManager::removeListener( FaviconListener* listener )
{
    assert( listener );
    if ( !d->m_listeners.contains( listener ) )
        return;
    const QString url = d->m_listeners.value( listener );
    d->urlDict.remove( KUrl( url ).host(), listener );
    d->urlDict.remove( url, listener );
    d->m_listeners.remove( listener );
}

FeedIconManager::FeedIconManager()
    : QObject()
    , d( new Private( this ) )
{
}

FeedIconManager::~FeedIconManager()
{
    delete d;
}

void FeedIconManager::slotIconChanged( bool isHost,
                                       const QString& hostOrUrl,
                                       const QString& iconName )
{
    Q_UNUSED( isHost );
    const QIcon icon( KGlobal::dirs()->findResource( "cache", iconName+".png" ) );
    Q_FOREACH( FaviconListener* const l, d->urlDict.values( hostOrUrl ) )
        l->setFavicon( icon );
}

#include "feediconmanager.moc"
<|endoftext|>


<commit_before>/*
The MIT License (MIT)
Copyright (c) 2017 Alexey Yegorov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

#include "des/meta/options.h"

DES_SYSTEM_DETAILS_BEGIN

namespace /* anonymous */
{
    namespace keys
    {
        constexpr auto system = meta::options::key_v<0>;
        constexpr auto input = meta::options::key_v<1>;
        constexpr auto output = meta::options::key_v<2>;
        constexpr auto dependencies = meta::options::key_v<3>;
    }
}

template<typename _Data>
template<typename... _Input>
inline constexpr auto node<_Data>::
    input(_Input && ... input) const noexcept
{
    using type = decltype(meta::options::set(data_, keys::input,
        std::make_tuple(std::forward<_Input>(input)...)));
    return node<type>{};
}

template<typename _Data>
template<typename... _Output>
inline constexpr auto node<_Data>::
    output(_Output && ... output) const noexcept
{
    using type = decltype(meta::options::set(data_, keys::output,
        std::make_tuple(std::forward<_Output>(output)...)));
    return node<type>{};
}

template<typename _Data>
template<typename... _Dependencies>
inline constexpr auto node<_Data>::
    dependencies(_Dependencies && ... dependencies) const noexcept
{
    using type = decltype(meta::options::set(data_, keys::dependencies,
        std::make_tuple(std::forward<_Dependencies>(dependencies)...)));
    return node<type>{};
}

template<typename _Data>
inline constexpr auto node<_Data>::system() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::system));
    return result{};
}

template<typename _Data>
inline constexpr auto node<_Data>::input_list() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::input));
    return result{};
}

template<typename _Data>
inline constexpr auto node<_Data>::output_list() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::output));
    return result{};
}

template<typename _Data>
inline constexpr auto node<_Data>::dependency_list() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::dependencies));
    return result{};
}

template<typename _System>
inline constexpr auto make_node(_System && system) noexcept
{
    using type = decltype(meta::options::set(meta::options::empty_v,
        keys::system, system));
    return node<type>{};
}

DES_SYSTEM_DETAILS_END
<commit_msg>[des][system] fix node initialization<commit_after>/*
The MIT License (MIT)
Copyright (c) 2017 Alexey Yegorov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

#include "des/meta/options.h"

DES_SYSTEM_DETAILS_BEGIN

namespace /* anonymous */
{
    namespace keys
    {
        constexpr auto system = meta::options::key_v<0>;
        constexpr auto input = meta::options::key_v<1>;
        constexpr auto output = meta::options::key_v<2>;
        constexpr auto dependencies = meta::options::key_v<3>;
    }
}

template<typename _Data>
template<typename... _Input>
inline constexpr auto node<_Data>::
    input(_Input && ... input) const noexcept
{
    using type = decltype(meta::options::set(data_, keys::input,
        std::make_tuple(std::forward<_Input>(input)...)));
    return node<type>{};
}

template<typename _Data>
template<typename... _Output>
inline constexpr auto node<_Data>::
    output(_Output && ... output) const noexcept
{
    using type = decltype(meta::options::set(data_, keys::output,
        std::make_tuple(std::forward<_Output>(output)...)));
    return node<type>{};
}

template<typename _Data>
template<typename... _Dependencies>
inline constexpr auto node<_Data>::
    dependencies(_Dependencies && ... dependencies) const noexcept
{
    using type = decltype(meta::options::set(data_, keys::dependencies,
        std::make_tuple(std::forward<_Dependencies>(dependencies)...)));
    return node<type>{};
}

template<typename _Data>
inline constexpr auto node<_Data>::system() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::system));
    return result{};
}

template<typename _Data>
inline constexpr auto node<_Data>::input_list() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::input));
    return result{};
}

template<typename _Data>
inline constexpr auto node<_Data>::output_list() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::output));
    return result{};
}

template<typename _Data>
inline constexpr auto node<_Data>::dependency_list() const noexcept
{
    using result = decltype(meta::options::get(data_, keys::dependencies));
    return result{};
}

template<typename _System>
inline constexpr auto make_node(_System && system) noexcept
{
    using type  = decltype(meta::options::set(meta::options::empty_v, keys::system, system));
    using type1 = decltype(meta::options::set(type{}, keys::input, std::make_tuple()));
    using type2 = decltype(meta::options::set(type1{}, keys::output, std::make_tuple()));
    using type3 = decltype(meta::options::set(type2{}, keys::dependencies, std::make_tuple()));
    return node<type3>{};
}

DES_SYSTEM_DETAILS_END
<|endoftext|>