text:
#include "nvwa/functional.h"
#include <algorithm>
#include <cassert>
#include <functional>
#include <iterator>
#include <list>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <boost/test/unit_test.hpp>

using namespace boost::unit_test_framework;

namespace /* unnamed */ {

auto const f = [](int v) { return v - 1.f; };
auto const g = [](float v) { return static_cast<int>(v); };

constexpr int increase(int n)
{
    return n + 1;
}

int fact(std::function<int(int)> f, int v)
{
    if (v == 0) {
        return 1;
    } else {
        return v * f(v - 1);
    }
}

constexpr int sqr(int x)
{
    return x * x;
}

template <typename Number>
struct Square {
    Number operator()(Number value) const
    {
        return value * value;
    }
};

struct SquareList {
    template <typename Data>
    auto operator()(const Data& a) const
    {
        return nvwa::fmap<std::list>(sqr, a);
    }
};

struct SumList {
    template <typename Data>
    auto operator()(const Data& a) const
    {
        return nvwa::reduce(std::plus<int>(), a);
    }
};

template <typename T>
constexpr auto sum(T x)
{
    return x;
}

template <typename T1, typename T2, typename... Targ>
constexpr auto sum(T1 x, T2 y, Targ... args)
{
    return sum(x + y, args...);
}

template <typename T>
std::ostream& print_with_space(std::ostream& os, const T& data)
{
    os << data << ' ';
    return os;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::list<T>& data)
{
    os << "[ ";
    nvwa::reduce(print_with_space<T>, data, os);;
    os << ']';
    return os;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& data)
{
    os << "( ";
    std::copy(data.begin(), data.end(),
              std::ostream_iterator<T>(os, " "));
    os << ')';
    return os;
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const nvwa::optional<T>& data)
{
    if (data) {
        os << "just " << data.value();
    } else {
        os << "invalid";
    }
    return os;
}

struct Obj {
    static int created_;
    static int copied_;
    static int moved_;

    int value;
    Obj() : Obj(0)
    {
    }
    explicit Obj(int n) : value(n)
    {
        if (value != 99) {
            BOOST_TEST_MESSAGE("Obj() :" << value);
            ++created_;
        }
    }
    Obj(const Obj& rhs) : value(rhs.value)
    {
        BOOST_TEST_MESSAGE("Obj(const Obj&) :" << value);
        ++copied_;
    }
    Obj(Obj&& rhs) : value(rhs.value)
    {
        BOOST_TEST_MESSAGE("Obj(Obj&&) :" << value);
        rhs.value = -2;
        ++moved_;
    }
    ~Obj()
    {
        assert(value != -1);
        if (value == 99) {
            BOOST_TEST_MESSAGE("Created: " << created_);
            BOOST_TEST_MESSAGE("Copied:  " << copied_);
            BOOST_TEST_MESSAGE("Moved:   " << moved_);
        } else if (value != -2) {
            BOOST_TEST_MESSAGE("~Obj() :" << value);
        }
        value = -1;
    }
};

int Obj::created_ = 0;
int Obj::copied_ = 0;
int Obj::moved_ = 0;

void test_curry(Obj& o1, Obj o2, Obj&& o3)
{
    BOOST_TEST_MESSAGE("Test currying: " << o1.value << o2.value
                                         << o3.value);
}

void test_out3(std::ostream& os, std::string a, const std::string& b,
               const std::string& c)
{
    os << a << b << c;
}

} /* unnamed namespace */

BOOST_AUTO_TEST_CASE(functional_test)
{
    Obj guard(99);  // special guard

    constexpr nvwa::optional<int> nothing;
    constexpr auto r1 = nvwa::apply(increase, nothing);
    constexpr auto r2 = nvwa::apply(increase, nvwa::make_optional(41));
    BOOST_CHECK(!r1.has_value());
    BOOST_CHECK(!r1);
    BOOST_CHECK(r2.has_value());
    BOOST_CHECK(!has_value(r1, r2));
    BOOST_CHECK(r2 && r2.value() == 42);
    auto r3 = r2;
    BOOST_CHECK(r2 && r2.value() == 42);
    BOOST_CHECK(r3 && r3.value() == 42);
    auto r4 = r3;
    BOOST_CHECK(r4 && r4.value() == 42);
    BOOST_CHECK_EQUAL(r4.value_or(-1), 42);
    r3 = 43;
    BOOST_CHECK_EQUAL(r3.value_or(-1), 43);
    r3.emplace(44);
    BOOST_CHECK_EQUAL(r3.value_or(-1), 44);
    swap(r3, r4);
    BOOST_CHECK_EQUAL(r3.value_or(-1), 42);
    BOOST_CHECK_EQUAL(r4.value_or(-1), 44);
    r3.reset();
    BOOST_CHECK_EQUAL(r3.value_or(-1), -1);
    BOOST_CHECK_EQUAL(nvwa::make_optional(42).value_or(-1), 42);
    BOOST_CHECK_EQUAL(nvwa::optional<int>().value_or(-1), -1);
    auto const inc_opt = nvwa::lift_optional(increase);
    BOOST_CHECK([](nvwa::optional<int> x) { return !x; }(inc_opt(r1)));
    BOOST_CHECK([](nvwa::optional<int> x)
                {
                    return x && x.value() == 43;
                }(inc_opt(r2)));
    BOOST_CHECK(noexcept(nvwa::optional<int>(0)));
    BOOST_CHECK(noexcept(
        nvwa::optional<int>(std::declval<nvwa::optional<int>&&>())));
    BOOST_CHECK(noexcept(nvwa::optional<Obj>()));

    auto p1 = nvwa::make_optional(std::make_unique<int>(45));
    BOOST_CHECK(p1);
    BOOST_CHECK(p1->get() != nullptr);
    BOOST_CHECK_EQUAL(**p1, 45);
    auto p2 = std::move(p1);
    BOOST_CHECK(p2);
    BOOST_CHECK(p2->get() != nullptr);
    BOOST_CHECK_EQUAL(**p2, 45);
    BOOST_CHECK(!p1);
    BOOST_CHECK(noexcept(swap(std::declval<std::unique_ptr<int>&>(),
                              std::declval<std::unique_ptr<int>&>())));
    BOOST_CHECK(noexcept(swap(p1, p2)));
    swap(p1, p2);
    BOOST_CHECK(p1->get() != nullptr && **p1 == 45);
    BOOST_CHECK(!p2);

    using namespace nvwa;
    BOOST_CHECK(noexcept(
        detail::adl_swap(std::declval<int&>(), std::declval<int&>())));
    BOOST_CHECK(noexcept(r3.swap(r4)));

    assert((nvwa::detail::can_reserve<std::vector<int>,
                                      std::list<int>>::value));
    auto id = nvwa::compose();
    int a[] = {1, 2, 3, 4, 5};
    BOOST_CHECK_EQUAL(SumList()(a), 15);
    std::vector<int> v = nvwa::fmap(id, a);
    std::ostringstream oss;  // boost cannot find my operator<< via ADL
    oss << v;
    BOOST_TEST_MESSAGE("Test vector is " << oss.str());
    oss.str(std::string());
    oss << nvwa::apply(SquareList(), nvwa::make_optional(v));
    BOOST_TEST_MESSAGE("Square list is " << oss.str());
    BOOST_CHECK_EQUAL(SumList()(v), 15);
    auto squared_sum = nvwa::compose(SumList(), SquareList());
    BOOST_CHECK_EQUAL(squared_sum(v), 55);
    using nvwa::pipeline;
    BOOST_CHECK_EQUAL(pipeline(v, squared_sum), 55);
    BOOST_CHECK_EQUAL(pipeline(v, SquareList(), SumList()), 55);
    BOOST_CHECK_EQUAL(
        pipeline(v,
                 [](const auto& x)
                 {
                     return nvwa::fmap(Square<int>(), x);
                 },
                 [](const auto& x)
                 {
                     return nvwa::reduce(std::plus<int>(), x);
                 }),
        55);

    {
        Obj obj = id(Obj());
    }

    auto inc = nvwa::compose(increase);
    BOOST_CHECK_EQUAL(nvwa::compose(inc, inc, inc)(2), 5);
    BOOST_CHECK_EQUAL(nvwa::pipeline(v,
                                     [](const std::vector<int>& v) {
                                         return nvwa::reduce(std::plus<>(),
                                                             v);
                                     },
                                     inc, inc),
                      17);
    auto plus_1 = [](int x) { return x + 1; };
    auto mult_2 = [](int x) { return x * 2; };
    BOOST_CHECK_EQUAL(nvwa::compose(plus_1, mult_2)(1), 3);
    BOOST_CHECK_EQUAL(nvwa::compose(g, f, id)(1), 0);

    (void)sum<int, int, int, int, int>;  // GCC requires this instantiation
    auto sum_and_square = nvwa::compose(sqr, sum<int, int, int, int, int>);
    BOOST_CHECK_EQUAL(sum_and_square(1, 2, 3, 4, 5), 225);
    auto square_and_sum_2 =
        nvwa::compose(nvwa::wrap_args_as_pair<int, int>(std::plus<>()),
                      [](const auto& x) { return nvwa::fmap(sqr, x); });
    auto square_and_sum_4 = nvwa::compose(
        nvwa::wrap_args_as_tuple<std::tuple<int, int, int, int>>(
            sum<int, int, int, int>),
        [](const auto& x) { return nvwa::fmap(sqr, x); });
    auto square_and_sum = nvwa::compose(
        [](const auto& x) { return nvwa::reduce(std::plus<int>(), x, 0); },
        [](const auto& x) { return nvwa::fmap(sqr, x); });
    BOOST_CHECK_EQUAL(square_and_sum_2(std::make_pair(3, 4)), 25);
    BOOST_CHECK_EQUAL(square_and_sum_4(std::make_tuple(1, 2, 3, 4)), 30);
    auto t = std::make_tuple(1, 2, 3, 4, 5);
    BOOST_CHECK_EQUAL(square_and_sum(t), 55);
    BOOST_CHECK_EQUAL(
        nvwa::apply(sum<int, int, int, int, int>, fmap(sqr, t)), 55);

    using std::function;
    typedef function<int(int)> Func;
    typedef function<Func(Func)> FuncFunc;

    FuncFunc almost_fac = [](Func f)
    {
        return Func([f](int n)
        {
            if (n <= 1) {
                return 1;
            } else {
                return n * f(n - 1);
            }
        });
    };
    FuncFunc almost_fac2 = nvwa::make_curry(fact);
    auto const fac1 = nvwa::fix_simple(almost_fac);
    auto const fac2 = nvwa::fix_curry(almost_fac);
    auto const fac3 = nvwa::fix_simple<int, int>(fact);
    auto const fac4 = nvwa::fix_curry(almost_fac2);
    auto const fac5 = nvwa::fix_fast([](auto f, int n) -> int
    {
        if (n <= 1) {
            return 1;
        } else {
            return n * f(n - 1);
        }
    });
    BOOST_CHECK_EQUAL(fac1(5), 120);
    BOOST_CHECK_EQUAL(fac2(5), 120);
    BOOST_CHECK_EQUAL(fac3(5), 120);
    BOOST_CHECK_EQUAL(fac4(5), 120);
    BOOST_CHECK_EQUAL(fac5(5), 120);

    Obj obj1(1);
    auto const middle = nvwa::make_curry(test_curry)(obj1)(Obj(2));
    middle(Obj(3));
    middle(Obj(3));
    oss.str(std::string());
    nvwa::make_curry(test_out3)(oss)("Hello ")("functional ")("world!");
    BOOST_CHECK_EQUAL(oss.str(), "Hello functional world!");
}


text:
//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2013   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2013   MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_MEMORY_DELETER_HPP_INCLUDED
#define BOOST_SIMD_MEMORY_DELETER_HPP_INCLUDED

#include <boost/simd/memory/deallocate.hpp>
#include <boost/simd/preprocessor/parameters.hpp>

namespace boost { namespace simd
{
  /*!
    @brief Deleter function object

    Whenever a pointer allocated using any of the SIMD aware allocation
    function is stored in a std::shared_ptr or similar object, it is necessary
    to pass this custom deleter to this object so the memory deallocation can be
    done using the proper alignment aware deallocation function.

    @tparam T Type of data to delete
    @tparam Allocator Type of allocator used for the allocation. By default, it
            is equal to @c void, meaning no allocator have been used to perform
            the allocation.
  **/
  template< class T
          , class Allocator = void
          >
  struct deleter : Allocator
  {
    typedef void result_type;
    typedef T * argument_type;

    /// Default constructor
    deleter() {}

    /// Constructs a deleter using the proper allocator
    deleter(Allocator const& x) : Allocator(x) {}

    /// Performs the deletion of the pointer @c x
    void operator()(T * x)
    {
      boost::simd::deallocate ( static_cast<Allocator&>(*this), x );
    }
  };

  /// INTERNAL ONLY
  template<class T>
  struct deleter<T>
  {
    typedef void  result_type;
    typedef T*    argument_type;

    void operator()(T * x) const
    {
      boost::simd::deallocate( x );
    }
  };
} }

#endif

ext: hpp


text:
// rs_config.cpp

/**
*    Copyright (C) 2008 10gen Inc.
*
*    This program is free software: you can redistribute it and/or  modify
*    it under the terms of the GNU Affero General Public License, version 3,
*    as published by the Free Software Foundation.
*
*    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 Affero General Public License for more details.
*
*    You should have received a copy of the GNU Affero General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pch.h"
#include "rs.h"
#include "../../client/syncclusterconnection.h"
#include "../../util/net/hostandport.h"
#include "../dbhelpers.h"
#include "connections.h"
#include "../oplog.h"
#include "../instance.h"
#include "../../util/text.h"
#include <boost/algorithm/string.hpp>

using namespace bson;

namespace mongo {

    mongo::mutex ReplSetConfig::groupMx("RS tag group");

    void logOpInitiate(const bo&);

    void assertOnlyHas(BSONObj o, const set<string>& fields) {
        BSONObj::iterator i(o);
        while( i.more() ) {
            BSONElement e = i.next();
            if( !fields.count( e.fieldName() ) ) {
                uasserted(13434, str::stream() << "unexpected field '" << e.fieldName() << "' in object");
            }
        }
    }

    list<HostAndPort> ReplSetConfig::otherMemberHostnames() const {
        list<HostAndPort> L;
        for( vector<MemberCfg>::const_iterator i = members.begin(); i != members.end(); i++ ) {
            if( !i->h.isSelf() )
                L.push_back(i->h);
        }
        return L;
    }

    /* comment MUST only be set when initiating the set by the initiator */
    void ReplSetConfig::saveConfigLocally(bo comment) {
        checkRsConfig();
        log() << "replSet info saving a newer config version to local.system.replset" << rsLog;
        {
            Lock::GlobalWrite lk; // TODO: does this really need to be a global lock?
            Client::Context cx( rsConfigNs );
            cx.db()->flushFiles(true);

            //theReplSet->lastOpTimeWritten = ??;
            //rather than above, do a logOp()? probably
            BSONObj o = asBson();
            Helpers::putSingletonGod(rsConfigNs.c_str(), o, false/*logOp=false; local db so would work regardless...*/);
            if( !comment.isEmpty() && (!theReplSet || theReplSet->isPrimary()) )
                logOpInitiate(comment);

            cx.db()->flushFiles(true);
        }
        log() << "replSet saveConfigLocally done" << rsLog;
    }

    bo ReplSetConfig::MemberCfg::asBson() const {
        bob b;
        b << "_id" << _id;
        b.append("host", h.dynString());
        if( votes != 1 ) b << "votes" << votes;
        if( priority != 1.0 ) b << "priority" << priority;
        if( arbiterOnly ) b << "arbiterOnly" << true;
        if( slaveDelay ) b << "slaveDelay" << slaveDelay;
        if( hidden ) b << "hidden" << hidden;
        if( !buildIndexes ) b << "buildIndexes" << buildIndexes;
        if( !tags.empty() ) {
            BSONObjBuilder a;
            for( map<string,string>::const_iterator i = tags.begin(); i != tags.end(); i++ )
                a.append((*i).first, (*i).second);
            b.append("tags", a.done());
        }
        return b.obj();
    }

    void ReplSetConfig::updateMembers(List1<Member> &dest) const {
        for (vector<MemberCfg>::const_iterator source = members.begin(); source < members.end(); source++) {
            for( Member *d = dest.head(); d; d = d->next() ) {
                if (d->fullName() == (*source).h.toString()) {
                    scoped_lock lk(groupMx);
                    d->configw().groupsw() = (*source).groups();
                }
            }
        }
    }

    bo ReplSetConfig::asBson() const {
        bob b;
        b.append("_id", _id).append("version", version);

        BSONArrayBuilder a;
        for( unsigned i = 0; i < members.size(); i++ )
            a.append( members[i].asBson() );
        b.append("members", a.arr());

        if( !ho.isDefault() || !getLastErrorDefaults.isEmpty() || !rules.empty()) {
            bob settings;
            if( !rules.empty() ) {
                bob modes;
                for (map<string,TagRule*>::const_iterator it = rules.begin(); it != rules.end(); it++) {
                    bob clauses;
                    vector<TagClause*> r = (*it).second->clauses;
                    for (vector<TagClause*>::iterator it2 = r.begin(); it2 < r.end(); it2++) {
                        clauses << (*it2)->name << (*it2)->target;
                    }
                    modes << (*it).first << clauses.obj();
                }
                settings << "getLastErrorModes" << modes.obj();
            }
            if( !getLastErrorDefaults.isEmpty() )
                settings << "getLastErrorDefaults" << getLastErrorDefaults;
            b << "settings" << settings.obj();
        }

        return b.obj();
    }

    static inline void mchk(bool expr) {
        uassert(13126, "bad Member config", expr);
    }

    void ReplSetConfig::MemberCfg::check() const {
        mchk(_id >= 0 && _id <= 255);
        mchk(priority >= 0 && priority <= 1000);
        mchk(votes <= 100); // votes >= 0 because it is unsigned
        uassert(13419, "priorities must be between 0.0 and 100.0", priority >= 0.0 && priority <= 100.0);
        uassert(13437, "slaveDelay requires priority be zero", slaveDelay == 0 || priority == 0);
        uassert(13438, "bad slaveDelay value", slaveDelay >= 0 && slaveDelay <= 3600 * 24 * 366);
        uassert(13439, "priority must be 0 when hidden=true", priority == 0 || !hidden);
        uassert(13477, "priority must be 0 when buildIndexes=false", buildIndexes || priority == 0);
    }
/*
    string ReplSetConfig::TagSubgroup::toString() const {
        bool first = true;
        string result = "\""+name+"\": [";
        for (set<const MemberCfg*>::const_iterator i = m.begin(); i != m.end(); i++) {
            if (!first) {
                result += ", ";
            }
            first = false;
            result += (*i)->h.toString();
        }
        return result+"]";
    }
    */
    string ReplSetConfig::TagClause::toString() const {
        string result = name+": {";
        for (map<string,TagSubgroup*>::const_iterator i = subgroups.begin(); i != subgroups.end(); i++) {
//TEMP?            result += (*i).second->toString()+", ";
        }
        result += "TagClause toString TEMPORARILY DISABLED";
        return result + "}";
    }

    string ReplSetConfig::TagRule::toString() const {
        string result = "{";
        for (vector<TagClause*>::const_iterator it = clauses.begin(); it < clauses.end(); it++) {
            result += ((TagClause*)(*it))->toString()+",";
        }
        return result+"}";
    }

    void ReplSetConfig::TagSubgroup::updateLast(const OpTime& op) {
        RACECHECK
        if (last < op) {
            last = op;

            for (vector<TagClause*>::iterator it = clauses.begin(); it < clauses.end(); it++) {
                (*it)->updateLast(op);
            }
        }
    }

    void ReplSetConfig::TagClause::updateLast(const OpTime& op) {
        RACECHECK
        if (last >= op) {
            return;
        }

        // check at least n subgroups greater than clause.last
        int count = 0;
        map<string,TagSubgroup*>::iterator it;
        for (it = subgroups.begin(); it != subgroups.end(); it++) {
            if ((*it).second->last >= op) {
                count++;
            }
        }

        if (count >= actualTarget) {
            last = op;
            rule->updateLast(op);
        }
    }

    void ReplSetConfig::TagRule::updateLast(const OpTime& op) {
        OpTime *earliest = (OpTime*)&op;
        vector<TagClause*>::iterator it;

        for (it = clauses.begin(); it < clauses.end(); it++) {
            if ((*it)->last < *earliest) {
                earliest = &(*it)->last;
            }
        }

        // rules are simply and-ed clauses, so whatever the most-behind
        // clause is at is what the rule is at
        last = *earliest;
    }

    /** @param o old config
        @param n new config
        */
    /*static*/
    bool ReplSetConfig::legalChange(const ReplSetConfig& o, const ReplSetConfig& n, string& errmsg) {
        verify( theReplSet );

        if( o._id != n._id ) {
            errmsg = "set name may not change";
            return false;
        }
        /* TODO : wonder if we need to allow o.version < n.version only, which is more lenient.
                  if someone had some intermediate config this node doesnt have, that could be
                  necessary.  but then how did we become primary?  so perhaps we are fine as-is.
                  */
        if( o.version >= n.version ) {
            errmsg = str::stream() << "version number must increase, old: "
                                   << o.version << " new: " << n.version;
            return false;
        }

        map<HostAndPort,const ReplSetConfig::MemberCfg*> old;
        bool isLocalHost = false;
        for( vector<ReplSetConfig::MemberCfg>::const_iterator i = o.members.begin(); i != o.members.end(); i++ ) {
            if (i->h.isLocalHost()) {
                isLocalHost = true;
            }
            old[i->h] = &(*i);
        }
        int me = 0;
        for( vector<ReplSetConfig::MemberCfg>::const_iterator i = n.members.begin(); i != n.members.end(); i++ ) {
            const ReplSetConfig::MemberCfg& m = *i;
            if ( (isLocalHost && !m.h.isLocalHost()) || (!isLocalHost && m.h.isLocalHost())) {
                log() << "reconfig error, cannot switch between localhost and hostnames: "
                      << m.h.toString() << rsLog;
                uasserted(13645, "hosts cannot switch between localhost and hostname");
            }
            if( old.count(m.h) ) {
                const ReplSetConfig::MemberCfg& oldCfg = *old[m.h];
                if( oldCfg._id != m._id ) {
                    log() << "replSet reconfig error with member: " << m.h.toString() << rsLog;
                    uasserted(13432, "_id may not change for members");
                }
                if( oldCfg.buildIndexes != m.buildIndexes ) {
                    log() << "replSet reconfig error with member: " << m.h.toString() << rsLog;
                    uasserted(13476, "buildIndexes may not change for members");
                }
                /* are transitions to and from arbiterOnly guaranteed safe?  if not, we should disallow here.
                   there is a test at replsets/replsetarb3.js */
                if( oldCfg.arbiterOnly != m.arbiterOnly ) {
                    log() << "replSet reconfig error with member: " << m.h.toString() << " arbiterOnly cannot change. remove and readd the member instead " << rsLog;
                    uasserted(13510, "arbiterOnly may not change for members");
                }
            }
            if( m.h.isSelf() )
                me++;
        }

        uassert(13433, "can't find self in new replset config", me == 1);

        return true;
    }

    void ReplSetConfig::clear() {
        version = -5;
        _ok = false;
    }

    void ReplSetConfig::setMajority() {
        int total = members.size();
        int nonArbiters = total;
        int strictMajority = total/2+1;

        for (vector<MemberCfg>::iterator it = members.begin(); it < members.end(); it++) {
            if ((*it).arbiterOnly) {
                nonArbiters--;
            }
        }

        // majority should be all "normal" members if we have something like 4
        // arbiters & 3 normal members
        _majority = (strictMajority > nonArbiters) ? nonArbiters : strictMajority;
    }

    int ReplSetConfig::getMajority() const {
        return _majority;
    }

    void ReplSetConfig::checkRsConfig() const {
        uassert(13132,
                str::stream() << "nonmatching repl set name in _id field: " << _id << " vs. " << cmdLine.ourSetName(),
                _id == cmdLine.ourSetName());
        uassert(13308, "replSet bad config version #", version > 0);
        uassert(13133, "replSet bad config no members", members.size() >= 1);
        uassert(13309, "replSet bad config maximum number of members is 12", members.size() <= 12);
        {
            unsigned voters = 0;
            for( vector<MemberCfg>::const_iterator i = members.begin(); i != members.end(); ++i ) {
                if( i->votes )
                    voters++;
            }
            uassert(13612, "replSet bad config maximum number of voting members is 7", voters <= 7);
            uassert(13613, "replSet bad config no voting members", voters > 0);
        }
    }

    void ReplSetConfig::_populateTagMap(map<string,TagClause> &tagMap) {
        // create subgroups for each server corresponding to each of
        // its tags. E.g.:
        //
        // A is tagged with {"server" : "A", "dc" : "ny"}
        // B is tagged with {"server" : "B", "dc" : "ny"}
        //
        // At the end of this step, tagMap will contain:
        //
        // "server" => {"A" : [A], "B" : [B]}
        // "dc" => {"ny" : [A,B]}

        for (unsigned i=0; i<members.size(); i++) {
            MemberCfg member = members[i];

            for (map<string,string>::iterator tag = member.tags.begin(); tag != member.tags.end(); tag++) {
                string label = (*tag).first;
                string value = (*tag).second;

                TagClause& clause = tagMap[label];
                clause.name = label;

                TagSubgroup* subgroup;
                // search for "ny" in "dc"'s clause
                if (clause.subgroups.find(value) == clause.subgroups.end()) {
                    clause.subgroups[value] = subgroup = new TagSubgroup(value);
                }
                else {
                    subgroup = clause.subgroups[value];
                }

                subgroup->m.insert(&members[i]);
            }
        }
    }

    void ReplSetConfig::parseRules(const BSONObj& modes) {
        map<string,TagClause> tagMap;
        _populateTagMap(tagMap);

        for (BSONObj::iterator i = modes.begin(); i.more(); ) {
            unsigned int primaryOnly = 0;

            // ruleName : {dc : 2, m : 3}
            BSONElement rule = i.next();
            uassert(14046, "getLastErrorMode rules must be objects", rule.type() == mongo::Object);

            TagRule* r = new TagRule();

            BSONObj clauseObj = rule.Obj();
            for (BSONObj::iterator c = clauseObj.begin(); c.more(); ) {
                BSONElement clauseElem = c.next();
                uassert(14829, "getLastErrorMode criteria must be numeric", clauseElem.isNumber());

                // get the clause, e.g., "x.y" : 3
                const char *criteria = clauseElem.fieldName();
                int value = clauseElem.numberInt();
                uassert(14828, str::stream() << "getLastErrorMode criteria must be greater than 0: " << clauseElem, value > 0);

                TagClause* node = new TagClause(tagMap[criteria]);

                int numGroups = node->subgroups.size();
                uassert(14831, str::stream() << "mode " << clauseObj << " requires "
                        << value << " tagged with " << criteria << ", but only "
                        << numGroups << " with this tag were found", numGroups >= value);

                node->name = criteria;
                node->target = value;
                // if any subgroups contain "me", we can decrease the target
                node->actualTarget = node->target;

                // then we want to add pointers between clause & subgroup
                for (map<string,TagSubgroup*>::iterator sgs = node->subgroups.begin();
                     sgs != node->subgroups.end(); sgs++) {
                    bool foundMe = false;
                    (*sgs).second->clauses.push_back(node);

                    // if this subgroup contains the primary, it's automatically always up-to-date
                    for( set<MemberCfg*>::const_iterator cfg = (*sgs).second->m.begin();
                         cfg != (*sgs).second->m.end();
                         cfg++)
                    {
                        if ((*cfg)->h.isSelf()) {
                            node->actualTarget--;
                            foundMe = true;
                        }
                    }

                    scoped_lock lk(groupMx);
                    for (set<MemberCfg *>::iterator cfg = (*sgs).second->m.begin();
                         !foundMe && cfg != (*sgs).second->m.end(); cfg++) {
                        (*cfg)->groupsw().insert((*sgs).second);
                    }
                }

                // if all of the members of this clause involve the primary, it's always up-to-date
                if (node->actualTarget == 0) {
                    node->last = OpTime(INT_MAX, INT_MAX);
                    primaryOnly++;
                }

                // this is a valid clause, so we want to add it to its rule
                node->rule = r;
                r->clauses.push_back(node);
            }

            // if all of the clauses are satisfied by the primary, this rule is trivially true
            if (primaryOnly == r->clauses.size()) {
                r->last = OpTime(INT_MAX, INT_MAX);
            }

            // if we got here, this is a valid rule
            LOG(1) << "replSet new rule " << rule.fieldName() << ": " << r->toString() << rsLog;
            rules[rule.fieldName()] = r;
        }
    }

    void ReplSetConfig::from(BSONObj o) {
        static const string legal[] = {"_id","version", "members","settings"};
        static const set<string> legals(legal, legal + 4);
        assertOnlyHas(o, legals);

        md5 = o.md5();
        _id = o["_id"].String();
        if( o["version"].ok() ) {
            version = o["version"].numberInt();
            uassert(13115, "bad " + rsConfigNs + " config: version", version > 0);
        }

        set<string> hosts;
        set<int> ords;
        vector<BSONElement> members;
        try {
            members = o["members"].Array();
        }
        catch(...) {
            uasserted(13131, "replSet error parsing (or missing) 'members' field in config object");
        }

        unsigned localhosts = 0;
        for( unsigned i = 0; i < members.size(); i++ ) {
            BSONObj mobj = members[i].Obj();
            MemberCfg m;
            try {
                static const string legal[] = {
                    "_id","votes","priority","host", "hidden","slaveDelay",
                    "arbiterOnly","buildIndexes","tags","initialSync" // deprecated
                };
                static const set<string> legals(legal, legal + 10);
                assertOnlyHas(mobj, legals);

                try {
                    m._id = (int) mobj["_id"].Number();
                }
                catch(...) {
                    /* TODO: use of string exceptions may be problematic for reconfig case! */
                    throw "_id must be numeric";
                }
                try {
                    string s = mobj["host"].String();
                    boost::trim(s);
                    m.h = HostAndPort(s);
                    if ( !m.h.hasPort() ) {
                        // make port explicit even if default
                        m.h.setPort(m.h.port());
                    }
                }
                catch(...) {
                    throw string("bad or missing host field? ") + mobj.toString();
                }
                if( m.h.isLocalHost() )
                    localhosts++;
                m.arbiterOnly = mobj["arbiterOnly"].trueValue();
                m.slaveDelay = mobj["slaveDelay"].numberInt();
                if( mobj.hasElement("hidden") )
                    m.hidden = mobj["hidden"].trueValue();
                if( mobj.hasElement("buildIndexes") )
                    m.buildIndexes = mobj["buildIndexes"].trueValue();
                if( mobj.hasElement("priority") )
                    m.priority = mobj["priority"].Number();
                if( mobj.hasElement("votes") )
                    m.votes = (unsigned) mobj["votes"].Number();
                if( mobj.hasElement("tags") ) {
                    const BSONObj &t = mobj["tags"].Obj();
                    for (BSONObj::iterator c = t.begin(); c.more(); c.next()) {
                        m.tags[(*c).fieldName()] = (*c).String();
                    }
                    uassert(14827, "arbiters cannot have tags", !m.arbiterOnly || m.tags.empty() );
                }
                m.check();
            }
            catch( const char * p ) {
                log() << "replSet cfg parsing exception for members[" << i << "] " << p << rsLog;
                stringstream ss;
                ss << "replSet members[" << i << "] " << p;
                uassert(13107, ss.str(), false);
            }
            catch(DBException& e) {
                log() << "replSet cfg parsing exception for members[" << i << "] " << e.what() << rsLog;
                stringstream ss;
                ss << "bad config for member[" << i << "] " << e.what();
                uassert(13135, ss.str(), false);
            }
            if( !(ords.count(m._id) == 0 && hosts.count(m.h.toString()) == 0) ) {
                log() << "replSet " << o.toString() << rsLog;
                uassert(13108, "bad replset config -- duplicate hosts in the config object?", false);
            }
            hosts.insert(m.h.dynString());
            ords.insert(m._id);
            this->members.push_back(m);
        }
        uassert(13393, "can't use localhost in repl set member names except when using it for all members", localhosts == 0 || localhosts == members.size());
        uassert(13117, "bad " + rsConfigNs + " config", !_id.empty());

        if( o["settings"].ok() ) {
            BSONObj settings = o["settings"].Obj();
            if( settings["getLastErrorModes"].ok() ) {
                parseRules(settings["getLastErrorModes"].Obj());
            }
            ho.check();
            try { getLastErrorDefaults = settings["getLastErrorDefaults"].Obj().copy(); }
            catch(...) { }
        }

        // figure out the majority for this config
        setMajority();
    }

    static inline void configAssert(bool expr) {
        uassert(13122, "bad repl set config?", expr);
    }

    ReplSetConfig::ReplSetConfig(BSONObj cfg, bool force) :
        _ok(false),_majority(-1)
    {
        _constructed = false;
        clear();
        from(cfg);
        if( force ) {
            version += rand() % 100000 + 10000;
        }
        configAssert( version < 0 /*unspecified*/ || (version >= 1) );
        if( version < 1 )
            version = 1;
        _ok = true;
        _constructed = true;
    }

    ReplSetConfig::ReplSetConfig(const HostAndPort& h) :
      _ok(false),_majority(-1)
    {
        LOG(2) << "ReplSetConfig load " << h.toStringLong() << rsLog;

        _constructed = false;
        clear();
        int level = 2;
        DEV level = 0;

        BSONObj cfg;
        int v = -5;
        try {
            if( h.isSelf() ) {
                ;
            }
            else {
                /* first, make sure other node is configured to be a replset. just to be safe. */
                string setname = cmdLine.ourSetName();
                BSONObj cmd = BSON( "replSetHeartbeat" << setname );
                int theirVersion;
                BSONObj info;
                log() << "trying to contact " << h.toString() << rsLog;
                bool ok = requestHeartbeat(setname, "", h.toString(), info, -2, theirVersion);
                if( info["rs"].trueValue() ) {
                    // yes, it is a replicate set, although perhaps not yet initialized
                }
                else {
                    if( !ok ) {
                        log() << "replSet TEMP !ok heartbeating " << h.toString() << " on cfg load" << rsLog;
                        if( !info.isEmpty() )
                            log() << "replSet info " << h.toString() << " : " << info.toString() << rsLog;
                        return;
                    }
                    {
                        stringstream ss;
                        ss << "replSet error: member " << h.toString() << " is not in --replSet mode";
                        msgassertedNoTrace(13260, ss.str().c_str()); // not caught as not a user exception - we want it not caught
                        //for python err# checker: uassert(13260, "", false);
                    }
                }
            }

            v = -4;
            unsigned long long count = 0;
            try {
                ScopedConn conn(h.toString());
                v = -3;
                cfg = conn.findOne(rsConfigNs, Query()).getOwned();
                count = conn.count(rsConfigNs);
            }
            catch ( DBException& ) {
                if ( !h.isSelf() ) {
                    throw;
                }

                // on startup, socket is not listening yet
                DBDirectClient cli;
                cfg = cli.findOne( rsConfigNs, Query() ).getOwned();
                count = cli.count(rsConfigNs);
            }

            if( count > 1 )
                uasserted(13109, str::stream() << "multiple rows in " << rsConfigNs << " not supported host: " << h.toString());

            if( cfg.isEmpty() ) {
                version = EMPTYCONFIG;
                return;
            }
            version = -1;
        }
        catch( DBException& e) {
            version = v;
            log(level) << "replSet load config couldn't get from " << h.toString() << ' ' << e.what() << rsLog;
            return;
        }

        from(cfg);
        checkRsConfig();
        _ok = true;
        log(level) << "replSet load config ok from " << (h.isSelf() ? "self" : h.toString()) << rsLog;
        _constructed = true;
    }

}

ext: cpp

max_stars_repo_name: sayfullah/MongoPi

max_issues_repo_name: sayfullah/MongoPi

max_forks_repo_name: sayfullah/MongoPi


text:
#include "object_namer.hpp"

#include <llpm/block.hpp>
#include <llpm/ports.hpp>
#include <llpm/module.hpp>

#include <boost/format.hpp>
#include <cctype>

using namespace std;

namespace llpm {

static std::string addContext(std::string base, Block* b, Module* ctxt) {
    if (ctxt == nullptr)
        return base;
    std::string name = base;
    Module* mod = b->module();
    while (b != ctxt && mod != ctxt && mod != NULL) {
        name = mod->name() + "_" + name;
        mod = mod->module();
        if (mod == NULL) {
            throw InvalidArgument("Module ctxt does not appear to be a parent of Block b!");
        }
    }

    return name;
}

void ObjectNamer::assignName(const Port* p, Module* ctxt, std::string name) {
    _portNames[make_pair(ctxt, p)] = name;
    _existingNames.insert(make_pair(ctxt, name));
}

static std::string sanitize(std::string s) {
    for (unsigned i = 0; i < s.size(); i++) {
        if (!isalnum(s[i])) {
            s[i] = '_';
        }
    }
    return s;
}

std::string ObjectNamer::historicalName(Block* b) {
    if (b->name() != "")
        return b->name();
    if (b->history().src() == BlockHistory::Refinement &&
        b->history().hasSrcBlock()) {
        return historicalName(b->history().srcBlock()) + "r";
    } else if (b->history().src() == BlockHistory::Optimization &&
               b->history().hasSrcBlock()) {
        return historicalName(b->history().srcBlock()) + "o";
    }

    return str(boost::format("anonBlock%1%") % ++anonBlockCounter);
}

std::string ObjectNamer::primBlockName(Block* b, Module* ctxt) {
    std::string& base = _blockNames[make_pair(ctxt, b)];
    if (base == "") {
        base = b->name();
        if (base == "") {
            base = historicalName(b);
        }
        base = sanitize(base);

        size_t ctr = 0;
        string orig_base = base;
        while (_existingNames.count(std::make_pair(ctxt, base)) > 0 ||
               _existingNames.count(std::make_pair(nullptr, base)) > 0) {
            base = str(boost::format("%1%_%2%") % orig_base % ++ctr);
        }
        _existingNames.insert(make_pair(ctxt, base));
    }
    return base;
}

std::string ObjectNamer::getName(Block* b, Module* ctxt, bool) {
    std::string base = primBlockName(b, ctxt);
    return addContext(base, b, ctxt);
}

std::string ObjectNamer::getName(const InputPort* p, Module* ctxt, bool io) {
    if (io)
        return p->name();

    std::string& base = _portNames[make_pair(ctxt, p)];
    if (base == "") {
        base = p->name();
        if (base == "") {
            unsigned count = 0;
            for (auto&& ip: p->owner()->inputs()) {
                if (ip == p)
                    break;
                count++;
            }
            assert(count < p->owner()->inputs().size());
            base = str(boost::format("%1%") % count);
        }

        if (!io)
            base = str(boost::format("%1%_input%2%")
                        % primBlockName(p->owner(), ctxt)
                        % base);
    }
    return addContext(base, p->owner(), ctxt);
}

std::string ObjectNamer::getName(const OutputPort* p, Module* ctxt, bool io) {
    if (io)
        return p->name();

    std::string& base = _portNames[make_pair(ctxt, p)];
    if (base == "") {
        base = p->name();
        if (base == "") {
            unsigned count = 0;
            for (auto&& op: p->owner()->outputs()) {
                if (op == p)
                    break;
                count++;
            }
            assert(count < p->owner()->outputs().size());
            base = str(boost::format("%1%") % count);
        }

        if (!io)
            base = str(boost::format("%1%_output%2%")
                       % primBlockName(p->owner(), ctxt)
                       % base);
    }
    return addContext(base, p->owner(), ctxt);
}

}

max_stars_repo_name: llpm/llpm

max_issues_repo_name: llpm/llpm

max_forks_repo_name: llpm/llpm


text:
// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <asiolink/io_error.h>

#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/algorithm/string/predicate.hpp>

#include <sstream>
#include <string>

using namespace isc::process;
using namespace isc::data;

namespace isc {
namespace d2 {

// *********************** D2Params  *************************

D2Params::D2Params(const isc::asiolink::IOAddress& ip_address,
                   const size_t port,
                   const size_t dns_server_timeout,
                   const dhcp_ddns::NameChangeProtocol& ncr_protocol,
                   const dhcp_ddns::NameChangeFormat& ncr_format)
    : ip_address_(ip_address),
    port_(port),
    dns_server_timeout_(dns_server_timeout),
    ncr_protocol_(ncr_protocol),
    ncr_format_(ncr_format) {
    validateContents();
}

D2Params::D2Params()
    : ip_address_(isc::asiolink::IOAddress("127.0.0.1")),
     port_(53001), dns_server_timeout_(100),
     ncr_protocol_(dhcp_ddns::NCR_UDP),
     ncr_format_(dhcp_ddns::FMT_JSON) {
    validateContents();
}

D2Params::~D2Params(){};

void
D2Params::validateContents() {
    if ((ip_address_.toText() == "0.0.0.0") || (ip_address_.toText() == "::")) {
        isc_throw(D2CfgError,
                  "D2Params: IP address cannot be \"" << ip_address_ << "\"");
    }

    if (port_ == 0) {
        isc_throw(D2CfgError, "D2Params: port cannot be 0");
    }

    if (dns_server_timeout_ < 1) {
        isc_throw(D2CfgError,
                  "D2Params: DNS server timeout must be larger than 0");
    }

    if (ncr_format_ != dhcp_ddns::FMT_JSON) {
        isc_throw(D2CfgError, "D2Params: NCR Format:"
                  << dhcp_ddns::ncrFormatToString(ncr_format_)
                  << " is not yet supported");
    }

    if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
        isc_throw(D2CfgError, "D2Params: NCR Protocol:"
                  << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
                  << " is not yet supported");
    }
}

std::string
D2Params::getConfigSummary() const {
    std::ostringstream s;
    s << "listening on " << getIpAddress() << ", port " << getPort()
      << ", using " << ncrProtocolToString(ncr_protocol_);
    return (s.str());
}

bool
D2Params::operator == (const D2Params& other) const {
    return ((ip_address_ == other.ip_address_) &&
            (port_ == other.port_) &&
            (dns_server_timeout_ == other.dns_server_timeout_) &&
            (ncr_protocol_ == other.ncr_protocol_) &&
            (ncr_format_ == other.ncr_format_));
}

bool
D2Params::operator != (const D2Params& other) const {
    return (!(*this == other));
}

std::string
D2Params::toText() const {
    std::ostringstream stream;

    stream << ", ip-address: " << ip_address_.toText()
           << ", port: " << port_
           << ", dns-server-timeout_: " << dns_server_timeout_
           << ", ncr-protocol: "
           << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
           << ", ncr-format: " << ncr_format_
           << dhcp_ddns::ncrFormatToString(ncr_format_);

    return (stream.str());
}

std::ostream&
operator<<(std::ostream& os, const D2Params& config) {
    os << config.toText();
    return (os);
}

// *********************** TSIGKeyInfo  *************************
// Note these values match corresponding values for Bind9's
// dnssec-keygen
const char* TSIGKeyInfo::HMAC_MD5_STR = "HMAC-MD5";
const char* TSIGKeyInfo::HMAC_SHA1_STR = "HMAC-SHA1";
const char* TSIGKeyInfo::HMAC_SHA224_STR = "HMAC-SHA224";
const char* TSIGKeyInfo::HMAC_SHA256_STR = "HMAC-SHA256";
const char* TSIGKeyInfo::HMAC_SHA384_STR = "HMAC-SHA384";
const char* TSIGKeyInfo::HMAC_SHA512_STR = "HMAC-SHA512";

TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm,
                         const std::string& secret, uint32_t digestbits)
    :name_(name), algorithm_(algorithm), secret_(secret),
     digestbits_(digestbits), tsig_key_() {
    remakeKey();
}

TSIGKeyInfo::~TSIGKeyInfo() {
}

const dns::Name&
TSIGKeyInfo::stringToAlgorithmName(const std::string& algorithm_id) {
    if (boost::iequals(algorithm_id, HMAC_MD5_STR)) {
        return (dns::TSIGKey::HMACMD5_NAME());
    } else if (boost::iequals(algorithm_id, HMAC_SHA1_STR)) {
        return (dns::TSIGKey::HMACSHA1_NAME());
    } else if (boost::iequals(algorithm_id, HMAC_SHA224_STR)) {
        return (dns::TSIGKey::HMACSHA224_NAME());
    } else if (boost::iequals(algorithm_id, HMAC_SHA256_STR)) {
        return (dns::TSIGKey::HMACSHA256_NAME());
    } else if (boost::iequals(algorithm_id, HMAC_SHA384_STR)) {
        return (dns::TSIGKey::HMACSHA384_NAME());
    } else if (boost::iequals(algorithm_id, HMAC_SHA512_STR)) {
        return (dns::TSIGKey::HMACSHA512_NAME());
    }

    isc_throw(BadValue, "Unknown TSIG Key algorithm: " << algorithm_id);
}

void
TSIGKeyInfo::remakeKey() {
    try {
        // Since our secret value is base64 encoded already, we need to
        // build the input string for the appropriate TSIGKey constructor.
        // If secret isn't a valid base64 value, the constructor will throw.
        std::ostringstream stream;
        stream << dns::Name(name_).toText() << ":"
               << secret_ << ":"
               << stringToAlgorithmName(algorithm_);
        if (digestbits_ > 0) {
            stream << ":" << digestbits_;
        }

        tsig_key_.reset(new dns::TSIGKey(stream.str()));
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "Cannot make TSIGKey: " << ex.what());
    }
}

ElementPtr
TSIGKeyInfo::toElement() const {
    ElementPtr result = Element::createMap();
    // Set user-context
    contextToElement(result);
    // Set name
    result->set("name", Element::create(name_));
    // Set algorithm
    result->set("algorithm", Element::create(algorithm_));
    // Set secret
    result->set("secret", Element::create(secret_));
    // Set digest-bits
    result->set("digest-bits",
                Element::create(static_cast<int64_t>(digestbits_)));

    return (result);
}

// *********************** DnsServerInfo  *************************
DnsServerInfo::DnsServerInfo(const std::string& hostname,
                             isc::asiolink::IOAddress ip_address, uint32_t port,
                             bool enabled)
    :hostname_(hostname), ip_address_(ip_address), port_(port),
    enabled_(enabled) {
}

DnsServerInfo::~DnsServerInfo() {
}

std::string
DnsServerInfo::toText() const {
    std::ostringstream stream;
    stream << (getIpAddress().toText()) << " port:" << getPort();
    return (stream.str());
}

ElementPtr
DnsServerInfo::toElement() const {
    ElementPtr result = Element::createMap();
    // Set user-context
    contextToElement(result);
    // Set hostname
    result->set("hostname", Element::create(hostname_));
    // Set ip-address
    result->set("ip-address", Element::create(ip_address_.toText()));
    // Set port
    result->set("port", Element::create(static_cast<int64_t>(port_)));

    return (result);
}

std::ostream&
operator<<(std::ostream& os, const DnsServerInfo& server) {
    os << server.toText();
    return (os);
}

// *********************** DdnsDomain  *************************

DdnsDomain::DdnsDomain(const std::string& name,
                       DnsServerInfoStoragePtr servers,
                       const TSIGKeyInfoPtr& tsig_key_info)
    : name_(name), servers_(servers),
      tsig_key_info_(tsig_key_info) {
}

DdnsDomain::~DdnsDomain() {
}

const std::string
DdnsDomain::getKeyName() const {
    if (tsig_key_info_) {
        return (tsig_key_info_->getName());
    }

    return ("");
}

ElementPtr
DdnsDomain::toElement() const {
    ElementPtr result = Element::createMap();
    // Set user-context
    contextToElement(result);
    // Set name
    result->set("name", Element::create(name_));
    // Set servers
    ElementPtr servers = Element::createList();
    for (DnsServerInfoStorage::const_iterator server = servers_->begin();
         server != servers_->end(); ++server) {
        ElementPtr dns_server = (*server)->toElement();
        servers->add(dns_server);
    }
    // the dns server list may not be empty
    if (!servers->empty()) {
        result->set("dns-servers", servers);
    }
    // Set key-name
    if (tsig_key_info_) {
        result->set("key-name", Element::create(tsig_key_info_->getName()));
    }

    return (result);
}

// *********************** DdnsDomainLstMgr  *************************

const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";

DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name),
    domains_(new DdnsDomainMap()) {
}

DdnsDomainListMgr::~DdnsDomainListMgr () {
}

void
DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
    if (!domains) {
        isc_throw(D2CfgError,
                  "DdnsDomainListMgr::setDomains: Domain list may not be null");
    }

    domains_ = domains;

    // Look for the wild card domain. If present, set the member variable
    // to remember it.  This saves us from having to look for it every time
    // we attempt a match.
    DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_);
    if (gotit != domains_->end()) {
            wildcard_domain_ = gotit->second;
    }
}

bool
DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
    // First check the case of one domain to rule them all.
    if ((size() == 1) && (wildcard_domain_)) {
        domain = wildcard_domain_;
        return (true);
    }

    // Iterate over the domain map looking for the domain which matches
    // the longest portion of the given fqdn.

    size_t req_len = fqdn.size();
    size_t match_len = 0;
    DdnsDomainMapPair map_pair;
    DdnsDomainPtr best_match;
    BOOST_FOREACH (map_pair, *domains_) {
        std::string domain_name = map_pair.first;
        size_t dom_len = domain_name.size();

        // If the domain name is longer than the fqdn, then it cant be match.
        if (req_len < dom_len) {
            continue;
        }

        // If the lengths are identical and the names match we're done.
        if (req_len == dom_len) {
            if (boost::iequals(fqdn, domain_name)) {
                // exact match, done
                domain = map_pair.second;
                return (true);
            }
        } else {
            // The fqdn is longer than the domain name.  Adjust the start
            // point of comparison by the excess in length.  Only do the
            // comparison if the adjustment lands on a boundary. This
            // prevents "onetwo.net" from matching "two.net".
            size_t offset = req_len - dom_len;
            if ((fqdn[offset - 1] == '.')  &&
               (boost::iequals(fqdn.substr(offset), domain_name))) {
                // Fqdn contains domain name, keep it if its better than
                // any we have matched so far.
                if (dom_len > match_len) {
                    match_len = dom_len;
                    best_match = map_pair.second;
                }
            }
        }
    }

    if (!best_match) {
        // There's no match. If they specified a wild card domain use it
        // otherwise there's no domain for this entry.
        if (wildcard_domain_) {
            domain = wildcard_domain_;
            return (true);
        }

        LOG_WARN(dhcp_to_d2_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
        return (false);
    }

    domain = best_match;
    return (true);
}

ElementPtr
DdnsDomainListMgr::toElement() const {
    ElementPtr result = Element::createList();
    // Iterate on ddns domains
    for (DdnsDomainMap::const_iterator domain = domains_->begin();
         domain != domains_->end(); ++domain) {
        ElementPtr ddns_domain = domain->second->toElement();
        result->add(ddns_domain);
    }

    return (result);
}

// *************************** PARSERS ***********************************

// *********************** TSIGKeyInfoParser  *************************

TSIGKeyInfoPtr
TSIGKeyInfoParser::parse(ConstElementPtr key_config) {
    std::string name = getString(key_config, "name");
    std::string algorithm = getString(key_config, "algorithm");
    uint32_t digestbits = getInteger(key_config, "digest-bits");
    std::string secret = getString(key_config, "secret");
    ConstElementPtr user_context = key_config->get("user-context");

    // Algorithm must be valid.
    try {
        TSIGKeyInfo::stringToAlgorithmName(algorithm);
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "tsig-key : " << ex.what()
                  << " (" << getPosition("algorithm", key_config) << ")");
    }

    // Non-zero digest-bits must be an integral number of octets, greater
    // than 80 and at least half of the algorithm key length. It defaults
    // to zero and JSON parsing ensures it's a multiple of 8.
    if ((digestbits > 0) &&
        ((digestbits < 80) ||
         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR)
          && (digestbits < 112)) ||
         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR)
          && (digestbits < 128)) ||
         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA384_STR)
          && (digestbits < 192)) ||
         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA512_STR)
          && (digestbits < 256)))) {
        isc_throw(D2CfgError, "tsig-key: digest-bits too small : ("
                                  << getPosition("digest-bits", key_config)
                                  << ")");
        }

    // Everything should be valid, so create the key instance.
    // It is possible for the asiodns::dns::TSIGKey create to fail such as
    // with an invalid secret content.
    TSIGKeyInfoPtr key_info;
    try {
        key_info.reset(new TSIGKeyInfo(name, algorithm, secret, digestbits));
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, ex.what() << " ("
                  << key_config->getPosition() << ")");
    }

    // Add user-context
    if (user_context) {
        key_info->setContext(user_context);
    }

    return (key_info);
}

// *********************** TSIGKeyInfoListParser  *************************

TSIGKeyInfoMapPtr
TSIGKeyInfoListParser::parse(ConstElementPtr key_list) {
    TSIGKeyInfoMapPtr keys(new TSIGKeyInfoMap());
    ConstElementPtr key_config;
    TSIGKeyInfoParser key_parser;
    BOOST_FOREACH(key_config, key_list->listValue()) {
        TSIGKeyInfoPtr key = key_parser.parse(key_config);

        // Duplicates are not allowed and should be flagged as an error.
        if (keys->find(key->getName()) != keys->end()) {
            isc_throw(D2CfgError, "Duplicate TSIG key name specified : "
                      << key->getName()
                      << " (" << getPosition("name", key_config) << ")");
        }

        (*keys)[key->getName()] = key;
    }

    return (keys);
}

// *********************** DnsServerInfoParser  *************************

DnsServerInfoPtr
DnsServerInfoParser::parse(ConstElementPtr server_config) {
    std::string hostname = getString(server_config, "hostname");
    std::string ip_address = getString(server_config, "ip-address");
    uint32_t port = getInteger(server_config, "port");
    ConstElementPtr user_context = server_config->get("user-context");

    // The configuration must specify one or the other.
    if (hostname.empty() == ip_address.empty()) {
        isc_throw(D2CfgError, "Dns Server must specify one or the other"
                  " of hostname or IP address"
                  << " (" << server_config->getPosition() << ")");
    }

    DnsServerInfoPtr server_info;
    if (!hostname.empty()) {
        /// @todo when resolvable hostname is supported we create the entry
        /// as follows:
        ///
        /// @code
        /// // When  hostname is specified, create a valid, blank IOAddress
        /// // and then create the DnsServerInfo.
        /// serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
        ///
        /// @endcode
        ///
        /// Resolution will be done prior to connection during transaction
        /// processing.
        /// Until then we'll throw unsupported.
        isc_throw(D2CfgError, "Dns Server : hostname is not yet supported"
                  << " (" << getPosition("hostname", server_config) << ")");
    } else {
        try {
            // Create an IOAddress from the IP address string given and then
            // create the DnsServerInfo.
            isc::asiolink::IOAddress io_addr(ip_address);
            server_info.reset(new DnsServerInfo(hostname, io_addr, port));
        } catch (const isc::asiolink::IOError& ex) {
            isc_throw(D2CfgError, "Dns Server : invalid IP address : "
                      << ip_address
                      << " (" << getPosition("ip-address", server_config) << ")");
        }
    }

    // Add user-context
    if (user_context) {
        server_info->setContext(user_context);
    }

    return (server_info);
}

// *********************** DnsServerInfoListParser  *************************

DnsServerInfoStoragePtr
DnsServerInfoListParser::parse(ConstElementPtr server_list) {
    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
    ConstElementPtr server_config;
    DnsServerInfoParser parser;
    BOOST_FOREACH(server_config, server_list->listValue()) {
        DnsServerInfoPtr server = parser.parse(server_config);
        servers->push_back(server);
    }

    return (servers);
}

// *********************** DdnsDomainParser  *************************

DdnsDomainPtr DdnsDomainParser::parse(ConstElementPtr domain_config,
                                      const TSIGKeyInfoMapPtr keys) {
    std::string name = getString(domain_config, "name");
    std::string key_name = getString(domain_config, "key-name");
    ConstElementPtr user_context = domain_config->get("user-context");

    // Key name is optional. If it is not blank, then find the key in the
    // list of defined keys.
    TSIGKeyInfoPtr tsig_key_info;
    if (!key_name.empty()) {
        if (keys) {
            TSIGKeyInfoMap::iterator kit = keys->find(key_name);
            if (kit != keys->end()) {
                tsig_key_info = kit->second;
            }
        }

        if (!tsig_key_info) {
            isc_throw(D2CfgError, "DdnsDomain : " << name
                      << " specifies an undefined key: " << key_name
                      << " (" << getPosition("key-name", domain_config) << ")");
        }
    }

    // Parse the list of DNS servers
    ConstElementPtr servers_config;
    try {
        servers_config = domain_config->get("dns-servers");
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "DdnsDomain : missing dns-server list"
                      << " (" << servers_config->getPosition() << ")");
    }

    DnsServerInfoListParser server_parser;
    DnsServerInfoStoragePtr servers =  server_parser.parse(servers_config);
    if (servers->size() == 0) {
        isc_throw(D2CfgError, "DNS server list cannot be empty"
                    << servers_config->getPosition());
    }

    // Instantiate the new domain and add it to domain storage.
    DdnsDomainPtr domain(new DdnsDomain(name, servers, tsig_key_info));

    // Add user-context
    if (user_context) {
        domain->setContext(user_context);
    }

    return (domain);
}

// *********************** DdnsDomainListParser  *************************

DdnsDomainMapPtr DdnsDomainListParser::parse(ConstElementPtr domain_list,
                                             const TSIGKeyInfoMapPtr keys) {
    DdnsDomainMapPtr domains(new DdnsDomainMap());
    DdnsDomainParser parser;
    ConstElementPtr domain_config;
    BOOST_FOREACH(domain_config, domain_list->listValue()) {
        DdnsDomainPtr domain = parser.parse(domain_config, keys);

        // Duplicates are not allowed
        if (domains->find(domain->getName()) != domains->end()) {
            isc_throw(D2CfgError, "Duplicate domain specified:"
                      << domain->getName()
                      << " (" << getPosition("name", domain_config) << ")");
        }

        (*domains)[domain->getName()] = domain;
    }

    return (domains);
}

// *********************** DdnsDomainListMgrParser  *************************

DdnsDomainListMgrPtr
DdnsDomainListMgrParser::parse(ConstElementPtr mgr_config,
                               const std::string& mgr_name,
                               const TSIGKeyInfoMapPtr keys) {
    DdnsDomainListMgrPtr mgr(new DdnsDomainListMgr(mgr_name));

    // Parse the list of domains
    ConstElementPtr domains_config = mgr_config->get("ddns-domains");
    if (domains_config) {
        DdnsDomainListParser domain_parser;
        DdnsDomainMapPtr domains =  domain_parser.parse(domains_config, keys);

        // Add the new domain to the domain storage.
        mgr->setDomains(domains);
    }

    return(mgr);
}

}; // end of isc::dhcp namespace
}; // end of isc namespace

ext: cc

max_stars_repo_name: piskyscan/kea

max_issues_repo_name: piskyscan/kea

max_forks_repo_name: piskyscan/kea


text:
////////////////////////////////////////////////////////////////////////////////
//
// Package:   RoadNarrows Robotics ROS Chess Engine Package
//
// Link:      https://github.com/roadnarrows-robotics/chess_engine
//
// Library:   libchessengine
//
// File:      ceEngine.cpp
//
/*! \file
 *
 * \brief The GNU chess gnuchess backend engine implimentation.
 *
 * \author Robin Knight (robin.knight@roadnarrows.com)
 *
 * \par Copyright:
 * (C) 2013-2017  RoadNarrows
 * (http://www.roadnarrows.com)
 * \n All Rights Reserved
 *
 * \par Licence:
 * MIT
 */
/*
 * @EulaBegin@
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that
 * (1) The above copyright notice and the following two paragraphs
 * appear in all copies of the source code and (2) redistributions
 * including binaries reproduces these notices in the supporting
 * documentation.   Substantial modifications to this software may be
 * copyrighted by their authors and need not follow the licensing terms
 * described here, provided that the new terms are clearly indicated in
 * all files where they apply.
 *
 * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
 * OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
 * EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
 * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * @EulaEnd@
 */
////////////////////////////////////////////////////////////////////////////////

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <signal.h>
#include <libgen.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <pthread.h>

#include <string>
#include <vector>
#include <ostream>

#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>

#include "rnr/appkit/LogStream.h"

#include "chess_engine/ceTypes.h"
#include "chess_engine/ceError.h"
#include "chess_engine/ceUtils.h"
#include "chess_engine/ceEngine.h"

using namespace std;
using namespace chess_engine;

// ---------------------------------------------------------------------------
// Private Interface
// ---------------------------------------------------------------------------

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// GNU Chess Tracing Macros
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

#define GC_TRACE_ENABLE   ///< tracing on/off switch

#ifdef GC_TRACE_ENABLE

#define GC_TRACE_FILE "gctrace.log"   ///< tracing output file name

static FILE *FpGcTrace = NULL;        ///< tracing output file pointer

/*!
 * \brief
 */
#define GC_TRACE(fmt, ...) \
  do \
  { \
    if( FpGcTrace != NULL )\
    { \
      fprintf(FpGcTrace, fmt, ##__VA_ARGS__); \
      fflush(FpGcTrace); \
    } \
  } while(0)

/*!
 * \brief
 */
#define GC_TRACE_OPEN() \
  do \
  { \
    if( (FpGcTrace = fopen(GC_TRACE_FILE, "w")) != NULL ) \
    { \
      GC_TRACE("### Start GNU Chess (GC) Tracing.\n\n"); \
    } \
  } while(0)

/*!
 * \brief
 */
#define GC_TRACE_CLOSE() \
  do \
  { \
    if( FpGcTrace != NULL )\
    { \
      fclose(FpGcTrace); \
      GC_TRACE("### End GNU Chess Backend (GC) Tracing.\n\n"); \
    } \
  } while(0)

#else

#define GC_TRACE(fmt, ...)
#define GC_TRACE_OPEN()
#define GC_TRACE_CLOSE()

#endif // GC_TRACE_ENABLE

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Error Handling Macros
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

/*!
 * \brief Macro to try an expression.
 *
 * On failure, log error, unlock mutex, and return from function.
 *
 * Must be used within the ChessEngine derived classes with unlock() defined.
 *
 * \param expr  Express to evaluate.
 * \param ecode Function return error code on failure.
 * \param fmt   Logging format string.
 * \param ...   Option specific variable arguments for fmt.
 */
#define ENGINE_TRY(expr, ecode, fmt, ...) \
  do \
  { \
    if( !(expr) ) \
    { \
      LOGERROR(fmt, ##__VA_ARGS__); \
      unlock(); \
      return (ecode) < 0? (ecode): -(ecode); \
    } \
  } while(0)

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Response regualar expressions.
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

/*! null response */
static boost::regex reNull("(^$)");

/*! any response */
static boost::regex reAny("(.*)");

/*! pre-response: 'TimeLimit...' (for versions >= 6) */
static boost::regex reTimeLimit("(^[tT]ime[lL]imit.*)");

/*! 'version' response: 'gnu chess VERSION' */
static boost::regex reVersion("^gnu chess (.*)");

/*! 'depth DEPTH' response: 'search to a depth of DEPTH' */
static boost::regex reDepth("^search to a depth of ([0-9]+)");

/*! engine's numbered move response: 'N. ... AN' */
static boost::regex reNumMoveEngine("^([0-9]+)\\.\\s+\\.\\.\\.\\s+(\\S+)");

/*! players's numbered move response: 'N. AN' */
static boost::regex reNumMovePlayer("^([0-9]+)\\.\\s+(\\S+)");

/*! engines move response: 'my move is : SAN' */
static boost::regex reEngineMove("^my move is\\s*:\\s*"
                                 "([a-h][1-8][a-h][1-8]\\S*)");

/*! engine resigns: 'resign ...' */
static boost::regex reResign("(resign).*");

/*! engine determines a draw: '1/2-1/2 ...' */
static boost::regex reDraw("(1/2-1/2).*");

/*! white checkmates: '1-0 ...' */
static boost::regex reWhiteWins("(1-0).*");

/*! black checkmates: '0-1 ...' */
static boost::regex reBlackWins("(0-1).*");

/*! 'show game' response header: 'white black ...' */
static boost::regex reWhiteBlackHdr("\\s*(white)\\s+(black)\\s*");

/*! 'show game' response 1 move: 'N. AN' */
static boost::regex reWhiteBlackMove1("\\s*([0-9]+)\\.\\s+(\\S+)\\s*");

/*! 'show game' response 2 moves: 'N. AN AN' */
static boost::regex reWhiteBlackMove2("\\s*([0-9]+)\\.\\s+"
                                      "(\\S+)\\s+(\\S+)\\s*");

/*! 'show board' response status line: 'COLOR [CASTLING] ...' */
static boost::regex reCastling("\\s*(\\S+)\\s+([KQkq]*)\\s*");

/*! bad move response: 'invalid move: SAN' or 'illegal move: SAN' */
static boost::regex reInvalidMove("^([iI]nvalid [mM]ove:|"
                                  "[iI]llegal [mM]ove:)\\s+(.*)");

/*! unsupported command response: 'command 'CMD' is currently not supported.' */
static boost::regex reNotSupported("^command '(.*)' "
                                   "is currently not supported.");

//
// Turn off annoying warning with some compilers
//
//PRAGMA_IGNORED(sign-conversion)
/*!
 * \brief FD_SET() wrapper with no annoying warnings.
 * \param fd    File descriptor to add to set.
 * \param pset  Pointer to fd set.
 */
static inline void fdset_nowarn(int fd, fd_set *pset)
{
  FD_SET(fd, pset);
}
//PRAGMA_WARNING(sign-conversion)

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Timing Functions
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

/*!
 * \brief Mark the current time. Resolution is microseconds.
 *
 * \param pTvMark   Pointer to timeval structure to be populated with
 *                  the current system seconds and useconds.
 */
static inline void timer_mark(struct timeval *pTvMark)
{
  if( gettimeofday(pTvMark, NULL) != 0 )
  {
    LOGERROR("gettimeofday() %s(errno=%d)", strerror(errno), errno);
    timerclear(pTvMark);
  }
}

/*!
 * \brief Calculate the elapsed time between the given time mark and this call.
 *
 * \param pTvMark   Pointer to timeval holding time mark.
 *
 * \return
 * Number of microseconds elasped. If the marked time is invalid or the current
 * time cannot be ascertained, UINT_MAX is returned.
 */
unsigned int timer_elapsed(struct timeval *pTvMark)
{
  struct timeval  tvEnd, tvDelta;

  timer_mark(&tvEnd);

  if( !timerisset(pTvMark) || !timerisset(&tvEnd) )
  {
    return UINT_MAX;
  }

  tvDelta.tv_sec = tvEnd.tv_sec - pTvMark->tv_sec;
  if( tvEnd.tv_usec < pTvMark->tv_usec )
  {
    tvDelta.tv_sec--;
    tvEnd.tv_usec += 1000000;
  }
  tvDelta.tv_usec = tvEnd.tv_usec - pTvMark->tv_usec;

  return (unsigned int)(tvDelta.tv_sec * 1000000 + tvDelta.tv_usec);
}

static void setTs(int64_t nsec, timespec &ts)
{
  ts.tv_sec  = (time_t)(nsec / 1000000000);
  ts.tv_nsec = (time_t)(nsec % 1000000000);
}

static bool AbortFlagged = false;

static void abort_handler(int sig)
{
  //fprintf(stderr, "abort_handler(%d)\n", sig);
  AbortFlagged = true;
}

// -----------------------------------------------------------------------------
// Class BeParsedVars
// -----------------------------------------------------------------------------

void BeParsedVars::clear()
{
  m_nMoveNum = 0;
  m_strAN.clear();
  m_strSAN.clear();
  m_eEoGResult = NoResult;
  m_eEoGWinner = NoColor;
}

namespace chess_engine
{
  ostream &operator<<(ostream &os, const BeParsedVars &obj)
  {
    os << "{" << endl;
    os << "  m_nMoveNum:   " << obj.m_nMoveNum << endl;
    os << "  m_strAN:      " << obj.m_strAN << endl;
    os << "  m_strSAN:     " << obj.m_strSAN << endl;
    os << "  m_eEoGResult: "
      << nameOfResult(obj.m_eEoGResult) << "(" << (char)obj.m_eEoGResult << ")"
      << endl;
    os << "  m_eEoGWinner: "
      << nameOfColor(obj.m_eEoGWinner) << "(" << (char)obj.m_eEoGWinner << ")"
      << endl;
    os << "}";

    return os;
  }
}

// -----------------------------------------------------------------------------
// Class ChessEngine
// -----------------------------------------------------------------------------

ChessEngine::ChessEngine(string strEngineName) :
    m_strEngineName(strEngineName),
    m_strEngineVersion("0.0.0")
{
  m_bIsConn     = false;
  m_bIsPlaying  = false;
  m_bIsBusy     = false;
  m_fDifficulty = 1.0;
  m_eColorTurn  = NoColor;
  m_nMoveNum    = 0;
  m_eEoGReason  = NoGame;
  m_eWinner     = NoColor;
}

ChessEngine::~ChessEngine()
{
}

int ChessEngine::setGameDifficulty(float fDifficulty)
{
  // cap
  if( fDifficulty < 1.0 )
  {
    fDifficulty = 1.0;
  }
  else if( fDifficulty > 10.0 )
  {
    fDifficulty = 10.0;
  }

  m_fDifficulty = fDifficulty;

  return CE_OK;
}

int ChessEngine::startNewGame()
{
  if( isConnected() )
  {
    m_bIsPlaying  = true;
    m_eColorTurn  = White;
    m_nMoveNum    = 1;
    m_eEoGReason  = Ok;
    m_eWinner     = NoColor;

    return CE_OK;
  }

  return -CE_ECODE_NO_EXEC;
}

int ChessEngine::endCurrentGame(ChessResult eReason, ChessColor eWinner)
{
  if( isPlayingAGame() )
  {
    m_bIsPlaying  = false;
    m_eColorTurn  = NoColor;
    m_eEoGReason  = eReason;
    m_eWinner     = eWinner;
  }

  return CE_OK;
}

int ChessEngine::resign(const ChessColor ePlayer)
{
  return endCurrentGame(Resign, opponent(ePlayer));
}

ChessColor ChessEngine::alternateTurns(const ChessColor ePlayerThatMoved)
{
  if( isPlayingAGame() )
  {
    if( ePlayerThatMoved == White )
    {
      m_eColorTurn = Black;
    }
    else // Black
    {
      m_eColorTurn = White;
      ++m_nMoveNum;
    }
  }
  else
  {
    m_eColorTurn = NoColor;
  }

  return m_eColorTurn;
}

// -----------------------------------------------------------------------------
// Class ChessEngineGnu
// -----------------------------------------------------------------------------

const float ChessEngineGnu::DepthDifficulty = 2.0;  ///< depth/difficulty ratio

ChessEngineGnu::ChessEngineGnu() : ChessEngine("gnuchess")
{
  m_pipeToChess[0]    = -1;
  m_pipeToChess[1]    = -1;
  m_pipeFromChess[0]  = -1;
  m_pipeFromChess[1]  = -1;
  m_pidParent         = getpid();
  m_pidChild          = -1;

  m_nDepth            = DepthDft;
  m_bNeedPrompting    = false;

  m_bHasRandom        = true;
  m_bHasTimeLimits    = true;

  pthread_mutex_init(&m_mutexIF, NULL);
}

ChessEngineGnu::~ChessEngineGnu()
{
  closeConnection();

  pthread_mutex_destroy(&m_mutexIF);
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Base Interface
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

int ChessEngineGnu::openConnection()
{
  char   *argv[8];

  // make chess server node to chess engine backend pipe
  if( pipe(m_pipeToChess) == -1 )
  {
    LOGERROR("Failed to create pipe to %s. %s(errno=%d)",
        m_strEngineName.c_str(), strerror(errno), errno);
    return -CE_ECODE_SYS;
   }

  // make chess engine backend to chess server node pipe
  if ( pipe(m_pipeFromChess) == -1 )
  {
    LOGERROR("Failed to create pipe from %s. %s(errno=%d)",
        m_strEngineName.c_str(), strerror(errno), errno);
    return -CE_ECODE_SYS;
  }

  // fork calling application
  m_pidChild = fork();

  // fork failed
  if( m_pidChild == -1 )
  {
    LOGERROR("Could not fork. %s(errno=%d)", strerror(errno), errno);
    return -CE_ECODE_SYS;
  }

  //
  // Child Process
  //
  if( m_pidChild == 0 )
  {
    // close unused pipe ends
    close(m_pipeToChess[1]);
    close(m_pipeFromChess[0]);

    // duplciate pipe ends to standard input and output
    dup2(m_pipeToChess[0], 0);
    dup2(m_pipeFromChess[1], 1);

    // build exec arguments
    argv[0] = (char *)m_strEngineName.c_str();
    //argv[1] = (char *)"--xboard";
    argv[1] = NULL;
    argv[2] = NULL;

    // exec chess application
    execvp(argv[0], argv);

    // if here then exec failed
    exit(1);
  }

  //
  // Parent Process
  //
  else
  {
    LOGDIAG1("Forked-exec'ed %s, child pid=%u.",
        m_strEngineName.c_str(), m_pidChild);

    // close unused pipe ends
    close(m_pipeToChess[0]);
    close(m_pipeFromChess[1]);

    if( fcntl(m_pipeFromChess[0], F_SETFL, O_NONBLOCK) < 0 )
    {
      LOGERROR("fcntl(%d,F_SETFL,O_NONBLOCK) %s(errno=%d)",
          m_pipeFromChess[0], strerror(errno), errno);
      return -CE_ECODE_SYS;
    }

    GC_TRACE_OPEN();
    GC_TRACE("Application: %s\n", m_strEngineName.c_str());

    sleep(1);

    configure();

    m_bIsConn     = true;
    m_bIsPlaying  = false;

    return CE_OK;
  }
}

int ChessEngineGnu::closeConnection()
{
  if( m_pidChild > 0 )
  {
    killApp();
  }

  // close pipe ends
  if( m_pipeToChess[1] >= 0 )
  {
    close(m_pipeToChess[1]);
  }

  if( m_pipeFromChess[0] >=  0 )
  {
    close(m_pipeFromChess[0]);
  }

  m_strEngineName.clear();

  m_pipeToChess[0]    = -1;
  m_pipeToChess[1]    = -1;
  m_pipeFromChess[0]  = -1;
  m_pipeFromChess[1]  = -1;
  m_pidChild          = -1;

  GC_TRACE("closeConnection()\n");

  m_bIsConn     = false;
  m_bIsPlaying  = false;
  m_bIsBusy     = false;

  return CE_OK;
}

int ChessEngineGnu::setGameDifficulty(float fDifficulty)
{
  int   rc;

  lock();

  ChessEngine::setGameDifficulty(fDifficulty);

  m_nDepth = (int)(DepthDifficulty * m_fDifficulty);

  // ok to delay setting engine's search depth
  if( isConnected() )
  {
    rc = cmdDepth(m_nDepth);
  }
  else
  {
    rc = CE_OK;
  }

  unlock();

  return rc;
}

int ChessEngineGnu::startNewGame()
{
  int   rc;

  lock();

  ENGINE_TRY(isConnected(), CE_ECODE_NO_EXEC, "Not connected.");

  flushInput();

  if( (rc = cmdNew()) == CE_OK )
  {
    cmdDepth(m_nDepth);
    //cmdRandom(); // N.B. not supported in v6 and poorly in v5
    ChessEngine::startNewGame();
  }

  m_bNeedPrompting = true;

  unlock();

  return rc;
}

int ChessEngineGnu::makePlayersMove(const ChessColor ePlayer, string &strAN)
{
  int     rc;

  lock();

  //
  // Pre-checks
  //
  ENGINE_TRY(isConnected(), CE_ECODE_NO_EXEC, "Not connected.");
  ENGINE_TRY(isPlayingAGame(), CE_ECODE_CHESS_NO_GAME, "No game.");
  ENGINE_TRY(whoseTurn() == ePlayer, CE_ECODE_CHESS_OUT_OF_TURN,
                                              "Move out-of-turn.");

  // clear old parsed variables
  m_parsed.clear();

  // send move command
  rc = cmdMove(strAN);

  ENGINE_TRY(rc == CE_OK, rc, "Player's move command failed.");

  // out-of-sync with chess engine
  ENGINE_TRY(m_parsed.m_nMoveNum == m_nMoveNum, CE_ECODE_CHESS_FATAL,
      "Response: Move %d != expected move %d.",
      m_parsed.m_nMoveNum, m_nMoveNum);

  // the move in, out should match, but try to continue if not the case
  if( m_parsed.m_strAN != strAN )
  {
    LOGWARN("Response to move: '%s' != '%s'.",
        m_parsed.m_strAN.c_str(), strAN.c_str());
  }

  // get SAN
  rc = cmdShowGame(m_nMoveNum, ePlayer);

  ENGINE_TRY(rc == CE_OK, CE_ECODE_CHESS_FATAL, "Show game command failed.");

  // copy SAN (from cmdShowGame) to AN
  strAN = m_parsed.m_strSAN;

  // alternate turns
  alternateTurns(ePlayer);

  // chess engine's move response is automatic after player makes a move
  m_bNeedPrompting = false;

  unlock();

  return CE_OK;
}

int ChessEngineGnu::computeEnginesMove(string &strAN)
{
  ChessColor  eMoveColor;
  int         rc;

  lock();

  //
  // Pre-checks
  //
  ENGINE_TRY(isConnected(), CE_ECODE_NO_EXEC, "Not connected.");
  ENGINE_TRY(isPlayingAGame(), CE_ECODE_CHESS_NO_GAME, "No game.");

  // engine's color this round
  eMoveColor = whoseTurn();

  // tell engine to generate next move, its automatic after a player's move
  if( m_bNeedPrompting )
  {
    cmdGo( ((m_nMoveNum == 1) && (eMoveColor == White)) );
  }

  // clear old parsed variables
  m_parsed.clear();

  // wait for move response from engine
  rc = rspEnginesMove(eMoveColor);

  ENGINE_TRY(rc == CE_OK, rc, "Engine's move failed.");

  // out-of-sync with chess engine
  ENGINE_TRY(m_parsed.m_nMoveNum == m_nMoveNum, CE_ECODE_CHESS_FATAL,
      "Response: Move %d != expected move %d.",
      m_parsed.m_nMoveNum, m_nMoveNum);

  // get SAN
  rc = cmdShowGame(m_nMoveNum, eMoveColor);

  ENGINE_TRY(rc == CE_OK, CE_ECODE_CHESS_FATAL, "Show game command failed.");

  // copy SAN
  strAN = m_parsed.m_strSAN;

  // alternate turns
  alternateTurns(eMoveColor);

  // chess engine's move is not automatic after it makes a move
  m_bNeedPrompting = true;

  unlock();

  return rc;
}

int ChessEngineGnu::getCastlingOptions(list_of_castling &availWhite,
                                       list_of_castling &availBlack)
{
  string  strOptions;
  int     rc;

  lock();

  availWhite.clear();
  availBlack.clear();

  //
  // Pre-checks
  //
  ENGINE_TRY(isConnected(), CE_ECODE_NO_EXEC, "Not connected.");
  ENGINE_TRY(isPlayingAGame(), CE_ECODE_CHESS_NO_GAME, "No game.");

  if( (rc = cmdShowBoard(strOptions)) == CE_OK )
  {
    for(size_t i=0; i<strOptions.size(); ++i)
    {
      switch( strOptions[i] )
      {
        // white
        case 'K':
          availWhite.push_back(KingSide);
        case 'Q':
          availWhite.push_back(QueenSide);
          break;
        // black
        case 'k':
          availBlack.push_back(KingSide);
        case 'q':
          availBlack.push_back(QueenSide);
          break;
        // ignore
        default:
          break;
      }
    }
  }

  unlock();

  return rc;
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Extended Interface
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

int ChessEngineGnu::getEnginesEoGDecl(ChessResult &eResult, ChessColor &eWinner)
{
  eResult = m_parsed.m_eEoGResult;
  eWinner = m_parsed.m_eEoGWinner;

  return CE_OK;
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Low-Level Public I/O Interface
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

int ChessEngineGnu::readline(string &strLine, unsigned int msec)
{
  char  buf[MaxLineSize];
  int   n;

  strLine.clear();

  if( (n = readline(buf, sizeof(buf), msec)) > 0 )
  {
    strLine = buf;
  }

  return n;
}

int ChessEngineGnu::readline(char buf[], size_t sizeBuf, unsigned int msec)
{
  int             fd = m_pipeFromChess[0];
  int             nFd;
  ssize_t         n;
  ssize_t         nBytes = 0;
  bool            bDone = false;

  buf[0] = 0;

  //
  // Read the data until either 1) count bytes are read, 2) a time out occurs,
  // or 3) an error occurs.
  //
  while( (nBytes < sizeBuf-1) && !bDone )
  {
    nFd = waitForPipe(msec);

    // system error occurred on select - interpret
    if( nFd < 0 )
    {
      switch(errno)
      {
        case EAGAIN:        // non-blocking timeout, retry
          break;
        case EINTR:         // read was interrupted
          buf[0] = 0;
          return -CE_ECODE_SYS;
        default:            // non-recoverable error
          buf[nBytes] = 0;
          LOGERROR("select(%d,...) %s(errno=%d)", fd, strerror(errno), errno);
          GC_TRACE("readline() %s\n", buf);
          return -CE_ECODE_SYS;
      }
    }

    // timeout occurred
    else if( nFd == 0 )
    {
      LOGDIAG2("select() on read timed out.");
      bDone = true;
    }

    // read the available data from the pipe
    else if( (n = read(fd, buf+nBytes, (size_t)1)) == 1 )
    {
      switch( buf[nBytes] )
      {
        case '\n':    // received a response line
          buf[nBytes] = 0;
          //LOGDIAG2("readline() %zd bytes response line.", nBytes);
          GC_TRACE("readline() %s\n", buf);
          return (int)nBytes;
        case '\r':    // received a carriage return, but ignore
          break;
        default:      // received a character
          // got some data
          nBytes += 1;
          break;
      }
    }

    // system error occurred on read - interpret
    else if( n < 0 )
    {
      switch(errno)
      {
        case EAGAIN:        // non-blocking timeout, try again
        case EINTR:         // read was interrupted, try again
          break;
        default:            // non-recoverable error
          buf[nBytes] = 0;
          LOGERROR("select(%d,...) %s(errno=%d)", fd, strerror(errno), errno);
          GC_TRACE("readline() %s\n", buf);
          return -CE_ECODE_SYS;
      }
    }

    // got a select but no bytes
    else if( n == 0 )
    {
      LOGDIAG2("select() ok, but read()=0.");
      bDone = true;
    }
  }

  LOGDIAG2("readline(): %zd bytes partial response line.", nBytes);

  buf[nBytes] = 0;
  GC_TRACE("readline() %s\n", buf);

  return -CE_ECODE_TIMEDOUT;
}

int ChessEngineGnu::writeline(const char buf[], size_t len)
{
  ssize_t m, n;

  m = len > 0?  write(m_pipeToChess[1], buf, len): 0;
  n = write(m_pipeToChess[1], "\n", 1);

  GC_TRACE("writeline() %*s\n", (int)len, buf);

  return (m >= 0) && (n > 0)? m+n: -CE_ECODE_SYS;
}

void ChessEngineGnu::flushInput()
{
  char    buf[256];

  GC_TRACE("flushInput() Begin\n");
  m_strLastLine.clear();
  while( readline(buf, sizeof(buf)) > 0 )
  {
    //GC_TRACE("%s\n", buf);
  }
  GC_TRACE("flushInput() End\n");
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Initialization and Clean Up Functions
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

void ChessEngineGnu::configure()
{
  setupSignals();

  flushInput();

  // determine version
  cmdVersion();

  GC_TRACE("GNU Chess Engine %s\n", m_strEngineVersion.c_str());

  // version 5.y.z
  if( isVer5() )
  {
    m_bHasTimeLimits  = false;
    m_bHasRandom      = true;
  }
  // version 6.y.z
  else if( isVer6() )
  {
    m_bHasTimeLimits  = true;
    m_bHasRandom      = false;
  }
  // version ?
  else
  {
    LOGWARN("GNU Chess %s unsupported. Assuming version 6.x protocol.",
        m_strEngineVersion.c_str());
    m_bHasTimeLimits  = true;
    m_bHasRandom      = false;
  }

  // place in xboard
  cmdXboard();

  return;
}

void ChessEngineGnu::killApp()
{
  int   status;

  kill(m_pidChild, SIGTERM);
  waitpid(m_pidChild, &status, WEXITED);

  if( WIFEXITED(status) )
  {
    //fprintf(stderr, "DBG: good exit\n");
  }

  m_pidChild = -1;
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Commands
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

int ChessEngineGnu::cmdVersion()
{
  static const char *cmd = "version";

  vector<string>  matches;
  int             n;

  writeline(cmd);

  if( rspFirstLine(reVersion, matches) == 1 )
  {
    m_strEngineVersion = matches[0];
    return CE_OK;
  }
  else
  {
    return -CE_ECODE_CHESS_RSP;
  }
}

int ChessEngineGnu::cmdXboard()
{
  static const char *cmd = "xboard on";

  vector<string>  matches;
  int             n;

  writeline(cmd);

  flushInput();

  return CE_OK;
}

int ChessEngineGnu::cmdNew()
{
  static const char *cmd = "new";

  vector<string>  matches;
  int             n;

  writeline(cmd);

  rspFirstLine(reAny, matches);

  return CE_OK;
}

int ChessEngineGnu::cmdDepth(int nDepth)
{
  static const char *cmd = "depth";

  char            buf[MaxLineSize];
  vector<string>  matches;

  sprintf(buf, "%s %d", cmd, m_nDepth);

  writeline(buf);

  if( rspFirstLine(reDepth, matches) == 1 )
  {
    return CE_OK;
  }
  else if( isVer6() )
  {
    return -CE_ECODE_CHESS_RSP;
  }
  else
  {
    return CE_OK;
  }
}

int ChessEngineGnu::cmdRandom()
{
  static const char *cmd = "random";

  vector<string>  matches;

  if( !m_bHasRandom )
  {
    return CE_OK;
  }

  writeline(cmd);

  if( rspFirstLine(reNotSupported, matches) == 1 )
  {
    m_bHasRandom = false;
  }

  return CE_OK;
}

int ChessEngineGnu::cmdGo(bool bIsFirstGo)
{
  static const char *cmd = "go";

  vector<string>  matches;

  writeline(cmd);

  // delayed 'depth...' response.
  if( bIsFirstGo && isVer5() )
  {
    rspFirstLine(reDepth, matches);
  }

  return CE_OK;
}

int ChessEngineGnu::cmdMove(const string &strAN)
{
  vector<string>  matches;

  writeline(strAN.c_str());

  if( rspFirstLine(reNumMovePlayer, matches, 100, false) == 2 )
  {
    m_parsed.m_nMoveNum = atoi(matches[0].c_str());
    m_parsed.m_strAN    = matches[1];

    return CE_OK;
  }
  else if( match(m_strLastLine, reInvalidMove, matches) > 0 )
  {
    m_strLastLine.clear();
    return -CE_ECODE_CHESS_BAD_MOVE;
  }
  else
  {
    return -CE_ECODE_CHESS_RSP;
  }
}

int ChessEngineGnu::cmdShowGame(int nMoveNum, ChessColor eColor)
{
  static const char *cmd = "show game";

  vector<string>  matches;
  int             n;

  writeline(cmd);

  // white black
  if( rspFirstLine(reWhiteBlackHdr, matches) != 2 )
  {
    flushInput();
    return -CE_ECODE_CHESS_RSP;
  }

  // n. <white>
  // n. <white> <black>
  for(n = 0; n < nMoveNum; ++n)
  {
    if( rspNextLine(reWhiteBlackMove2, matches, false) != 3 )
    {
      if( rspNextLine(reWhiteBlackMove1, matches, false) != 2 )
      {
        flushInput();
        return -CE_ECODE_CHESS_RSP;
      }
    }
  }

  flushInput();

  n = atoi(matches[0].c_str());

  if( n != nMoveNum )
  {
    LOGERROR("Response to 'show game': last move %d != expected move %d.",
        n, nMoveNum);
    return -CE_ECODE_CHESS_RSP;
  }

  if( eColor == White )
  {
    m_parsed.m_strSAN = matches[1];
  }
  else if( matches.size() == 3 )
  {
    m_parsed.m_strSAN = matches[2];
  }
  else
  {
    LOGERROR("Response to 'show game': Bad move line.");
    return -CE_ECODE_CHESS_RSP;
  }

  return CE_OK;
}

int ChessEngineGnu::cmdShowBoard(string &strCastling)
{
  static const char *cmd = "show board";

  vector<string>  matches;
  int             rc = CE_OK;

  strCastling.clear();

  writeline(cmd);

  // null line
  if( rspFirstLine(reNull, matches) != 1 )
  {
    rc = -CE_ECODE_CHESS_RSP;
  }

  // COLOR [CASTLING] ...
  else if( rspNextLine(reCastling, matches, false) != 2 )
  {
    rc = -CE_ECODE_CHESS_RSP;
  }

  else
  {
    strCastling = matches[1];
    rc = CE_OK;
  }

  // flush board grid
  flushInput();

  return rc;
}

int ChessEngineGnu::rspEnginesMove(ChessColor colorMove)
{
  unsigned int    msec = (unsigned int)(m_nDepth * 4 * 1000);
  vector<string>  matches;
  string          strMyMoveAN;

  // n. ... AN
  if( rspFirstLine(reNumMoveEngine, matches, msec) == 2 )
  {
    m_parsed.m_nMoveNum = atoi(matches[0].c_str());
    m_parsed.m_strAN    = matches[1];
  }
  else
  {
    return -CE_ECODE_CHESS_RSP;
  }

  // my move is : AN
  if( rspNextLine(reEngineMove, matches) == 1 )
  {
    strMyMoveAN = matches[0];
  }

  else
  {
    return -CE_ECODE_CHESS_RSP;
  }

  //
  // Optional extra line of information produced by gnuchess.
  //
  if( rspNextLine(reNull, matches) == 1 )
  {
    // no extra info
  }
  else if( rspNextLine(reResign, matches) == 1 )
  {
    m_parsed.m_eEoGResult = Resign;
    m_parsed.m_eEoGWinner = opponent(colorMove);;
  }
  else if( rspNextLine(reDraw, matches) == 1 )
  {
    m_parsed.m_eEoGResult = Draw;
    m_parsed.m_eEoGWinner = NoColor;
  }
  else if( rspNextLine(reWhiteWins, matches) == 1 )
  {
    m_parsed.m_eEoGResult = Checkmate;
    m_parsed.m_eEoGWinner = White;
  }
  else if( rspNextLine(reBlackWins, matches) == 1 )
  {
    m_parsed.m_eEoGResult = Checkmate;
    m_parsed.m_eEoGWinner = Black;
  }

  return CE_OK;
}

int ChessEngineGnu::rspFirstLine(boost::regex   &reRsp,
                                 vector<string> &matches,
                                 unsigned int   msec,
                                 bool           bLower)
{
  int   n;

  //
  // Pre-response nonsense.
  //
  while( !m_strLastLine.empty() || (readline(m_strLastLine, msec) > 0) )
  {
    if( bLower )
    {
      boost::algorithm::to_lower(m_strLastLine);
    }

    if( !m_bHasTimeLimits || (match(m_strLastLine, reTimeLimit, matches) == 0) )
    {
      break;
    }
    else
    {
      m_strLastLine.clear();
    }
  }

  if( (n = match(m_strLastLine, reRsp, matches)) > 0 )
  {
    m_strLastLine.clear();
  }

  return n;
}

int ChessEngineGnu::rspNextLine(boost::regex   &reRsp,
                                vector<string> &matches,
                                bool            bLower)
{
  int   n;

  if( m_strLastLine.empty() )
  {
    readline(m_strLastLine);
  }

  if( bLower )
  {
    boost::algorithm::to_lower(m_strLastLine);
  }

  if( (n = match(m_strLastLine, reRsp, matches)) > 0 )
  {
    m_strLastLine.clear();
  }

  return n;
}

int ChessEngineGnu::match(const string   &str,
                          boost::regex   &re,
                          vector<string> &matches)
{
  boost::cmatch what;

  matches.clear();

  GC_TRACE("match(): %s\n", re.str().c_str());

  if( boost::regex_match(str.c_str(), what, re) )
  {
    // what[0] is the whole string
    for(size_t i=1; i<what.size(); ++i)
    {
      GC_TRACE("  \"%s\"\n", what[i].str().c_str());
      matches.push_back(what[i].str());
    }
  }

  GC_TRACE("  %zu matches\n", matches.size());

  return (int)matches.size();
}

void ChessEngineGnu::setupSignals()
{
  sigset_t          blockset;
  struct sigaction  sa;

  // block signals
  sigemptyset(&blockset);
  sigaddset(&blockset, SIGUSR1);
  sigprocmask(SIG_BLOCK, &blockset, NULL);
  //pthread_sigmask(SIG_BLOCK, &blockset, NULL);

  // install signal handlers
  sa.sa_handler = abort_handler;
  sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
  sigaction(SIGUSR1, &sa, NULL);
}

int ChessEngineGnu::waitForPipe(unsigned int msec)
{
  static int64_t  nsecStep = 10000000;  // tenth of a second in nanoseconds

  int64_t         nsecTotal;    // total nsecs to wait on pipe read
  int64_t         nsecWait;     // wait increment
  int             fd;           // pipe fd
  fd_set          rset;         // read set
  struct timespec timeout;      // select timeout
  sigset_t        emptyset;     // blocked signals
  int             nFd;          // number of fds changed on read

  // pipe from chess backend engine
  fd = m_pipeFromChess[0];

  // milliseconds to nanoseconds
  nsecTotal = (int64_t)msec * 1000000;

  // make set of unblock signals
  sigemptyset(&emptyset);

  //
  // Pseudo block for msec milliseconds.
  //
  // Note:  The pselect() call does not return after signal, even though handler
  //        works. So, break up block time into acceptable step delay sizes.
  //
  while( nsecTotal > 0 )
  {
    nsecWait = nsecTotal >= nsecStep?  nsecStep: nsecTotal;

    nsecTotal -= nsecStep;

    setTs(nsecWait, timeout);

    FD_ZERO(&rset);
    fdset_nowarn(fd, &rset);

    //
    // pselect atomically:
    //
    // sigset_t save;
    // sigprocmask(SIG_BLOCK, &emptyset, &save);
    // select(...)
    // sigprocmask(SIG_BLOCK, &save, NULL);
    //
    nFd = pselect(fd+1, &rset, NULL, NULL, &timeout, &emptyset);

    //
    // AbortFlagged can only be set in the signal handler which is
    // unblocked only in pselect(). N.B. signals are queued however.
    //
    if( AbortFlagged )
    {
      AbortFlagged  = false;
      errno         = EINTR;
      return -1;
    }

    // read ready or error
    else if( nFd != 0 )
    {
      return nFd;
    }
  }

  // timeout return
  return 0;
}