<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2019 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#define CAF_SUITE ipv6_endpoint

#include "caf/ipv6_endpoint.hpp"

#include "caf/test/unit_test.hpp"

#include <cassert>
#include <vector>

#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/byte.hpp"
#include "caf/ipv6_address.hpp"
#include "caf/serializer_impl.hpp"
#include "caf/span.hpp"

using namespace caf;

namespace {

ipv6_endpoint operator"" _ep(const char* str, size_t) {
  union {
    std::array<uint8_t, 16> xs_bytes;
    std::array<uint16_t, 8> xs;
  };
  union {
    std::array<uint8_t, 16> ys_bytes;
    std::array<uint16_t, 8> ys;
  };
  memset(xs_bytes.data(), 0, xs_bytes.size());
  memset(ys_bytes.data(), 0, xs_bytes.size());
  assert(*str == '[');
  ++str;
  char* next = nullptr;
  auto pull = [&] {
    auto x = static_cast<uint16_t>(strtol(str, &next, 16));
    return detail::to_network_order(x);
  };
  size_t n = 0;
  do {
    xs[n++] = pull();
    assert(next != nullptr);
    str = next + 1;
  } while (*str != ':' && *str != ']');
  if (*str == ':') {
    ++str;
    do {
      ys[0] = pull();
      std::rotate(ys.begin(), ys.begin() + 1, ys.end());
      assert(next != nullptr);
      str = next + 1;
    } while (*next != ']');
  }
  assert(*next == ']');
  assert(*str == ':');
  ++str;
  auto port = static_cast<uint16_t>(strtol(str, nullptr, 10));
  std::array<uint8_t, 16> bytes;
  for (size_t i = 0; i < 16; ++i)
    bytes[i] = xs_bytes[i] | ys_bytes[i];
  return ipv6_endpoint{ipv6_address{bytes}, port};
}

struct fixture {
  actor_system_config cfg;
  actor_system sys{cfg};

  template <class T>
  T roundtrip(T x) {
    using container_type = std::vector<byte>;
    container_type buf;
    serializer_impl<container_type> sink(sys, buf);
    if (auto err = sink(x))
      CAF_FAIL("serialization failed: " << sys.render(err));
    binary_deserializer source(sys, make_span(buf));
    T y;
    if (auto err = source(y))
      CAF_FAIL("deserialization failed: " << sys.render(err));
    return y;
  }
};

#define CHECK_TO_STRING(addr) CAF_CHECK_EQUAL(addr, to_string(addr##_ep))

#define CHECK_COMPARISON(addr1, addr2)                                         \
  CAF_CHECK_GREATER(addr2##_ep, addr1##_ep);                                   \
  CAF_CHECK_GREATER_OR_EQUAL(addr2##_ep, addr1##_ep);                          \
  CAF_CHECK_GREATER_OR_EQUAL(addr1##_ep, addr1##_ep);                          \
  CAF_CHECK_GREATER_OR_EQUAL(addr2##_ep, addr2##_ep);                          \
  CAF_CHECK_EQUAL(addr1##_ep, addr1##_ep);                                     \
  CAF_CHECK_EQUAL(addr2##_ep, addr2##_ep);                                     \
  CAF_CHECK_LESS_OR_EQUAL(addr1##_ep, addr2##_ep);                             \
  CAF_CHECK_LESS_OR_EQUAL(addr1##_ep, addr1##_ep);                             \
  CAF_CHECK_LESS_OR_EQUAL(addr2##_ep, addr2##_ep);                             \
  CAF_CHECK_NOT_EQUAL(addr1##_ep, addr2##_ep);                                 \
  CAF_CHECK_NOT_EQUAL(addr2##_ep, addr1##_ep);

#define CHECK_SERIALIZATION(addr)                                              \
  CAF_CHECK_EQUAL(addr##_ep, roundtrip(addr##_ep))

} // namespace

CAF_TEST_FIXTURE_SCOPE(comparison_scope, fixture)

CAF_TEST(constructing assigning and hash_code) {
  const uint16_t port = 8888;
  ipv6_address::array_type bytes{0, 0, 0, 0, 0, 0, 0, 0,
                                 0, 0, 0, 0, 0, 0, 0, 1};
  auto addr = ipv6_address{bytes};
  ipv6_endpoint ep1(addr, port);
  CAF_CHECK_EQUAL(ep1.address(), addr);
  CAF_CHECK_EQUAL(ep1.port(), port);
  ipv6_endpoint ep2;
  ep2.address(addr);
  ep2.port(port);
  CAF_CHECK_EQUAL(ep2.address(), addr);
  CAF_CHECK_EQUAL(ep2.port(), port);
  CAF_CHECK_EQUAL(ep1, ep2);
  CAF_CHECK_EQUAL(ep1.hash_code(), ep2.hash_code());
}

CAF_TEST(to_string) {
  CHECK_TO_STRING("[::1]:8888");
  CHECK_TO_STRING("[4e::d00:0:ed00:0:1]:1234");
  CHECK_TO_STRING("[::1]:1111");
  CHECK_TO_STRING("[4432::33:1]:8732");
  CHECK_TO_STRING("[::2]:8888");
  CHECK_TO_STRING("[4f::d00:12:ed00:0:1]:1234");
  CHECK_TO_STRING("[4f::1]:2222");
  CHECK_TO_STRING("[4432:8d::33:1]:8732");
  CHECK_TO_STRING("[4e::d00:0:ed00:0:1]:5678");
  CHECK_TO_STRING("[::1]:2221");
  CHECK_TO_STRING("[::1]:2222");
  CHECK_TO_STRING("[4432::33:1]:872");
  CHECK_TO_STRING("[4432::33:1]:999");
}

CAF_TEST(comparison) {
  CHECK_COMPARISON("[::1]:8888", "[::2]:8888");
  CHECK_COMPARISON("[4e::d00:0:ed00:0:1]:1234", "[4f::d00:12:ed00:0:1]:1234");
  CHECK_COMPARISON("[::1]:1111", "[4f::1]:2222");
  CHECK_COMPARISON("[4432::33:1]:8732", "[4432:8d::33:1]:8732");
  CHECK_COMPARISON("[::1]:1111", "[::1]:8888");
  CHECK_COMPARISON("[4e::d00:0:ed00:0:1]:1234", "[4e::d00:0:ed00:0:1]:5678");
  CHECK_COMPARISON("[::1]:2221", "[::1]:2222");
  CHECK_COMPARISON("[4432::33:1]:872", "[4432::33:1]:999");
}

CAF_TEST(serialization) {
  CHECK_SERIALIZATION("[::1]:8888");
  CHECK_SERIALIZATION("[4e::d00:0:ed00:0:1]:1234");
  CHECK_SERIALIZATION("[::1]:1111");
  CHECK_SERIALIZATION("[4432::33:1]:8732");
  CHECK_SERIALIZATION("[::2]:8888");
  CHECK_SERIALIZATION("[4f::d00:12:ed00:0:1]:1234");
  CHECK_SERIALIZATION("[4f::1]:2222");
  CHECK_SERIALIZATION("[4432:8d::33:1]:8732");
  CHECK_SERIALIZATION("[4e::d00:0:ed00:0:1]:5678");
  CHECK_SERIALIZATION("[::1]:2221");
  CHECK_SERIALIZATION("[::1]:2222");
  CHECK_SERIALIZATION("[4432::33:1]:872");
  CHECK_SERIALIZATION("[4432::33:1]:999");
}

CAF_TEST_FIXTURE_SCOPE_END()<commit_msg>Add newline<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2019 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#define CAF_SUITE ipv6_endpoint

#include "caf/ipv6_endpoint.hpp"

#include "caf/test/unit_test.hpp"

#include <cassert>
#include <vector>

#include "caf/actor_system.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/byte.hpp"
#include "caf/ipv6_address.hpp"
#include "caf/serializer_impl.hpp"
#include "caf/span.hpp"

using namespace caf;

namespace {

ipv6_endpoint operator"" _ep(const char* str, size_t) {
  union {
    std::array<uint8_t, 16> xs_bytes;
    std::array<uint16_t, 8> xs;
  };
  union {
    std::array<uint8_t, 16> ys_bytes;
    std::array<uint16_t, 8> ys;
  };
  memset(xs_bytes.data(), 0, xs_bytes.size());
  memset(ys_bytes.data(), 0, xs_bytes.size());
  assert(*str == '[');
  ++str;
  char* next = nullptr;
  auto pull = [&] {
    auto x = static_cast<uint16_t>(strtol(str, &next, 16));
    return detail::to_network_order(x);
  };
  size_t n = 0;
  do {
    xs[n++] = pull();
    assert(next != nullptr);
    str = next + 1;
  } while (*str != ':' && *str != ']');
  if (*str == ':') {
    ++str;
    do {
      ys[0] = pull();
      std::rotate(ys.begin(), ys.begin() + 1, ys.end());
      assert(next != nullptr);
      str = next + 1;
    } while (*next != ']');
  }
  assert(*next == ']');
  assert(*str == ':');
  ++str;
  auto port = static_cast<uint16_t>(strtol(str, nullptr, 10));
  std::array<uint8_t, 16> bytes;
  for (size_t i = 0; i < 16; ++i)
    bytes[i] = xs_bytes[i] | ys_bytes[i];
  return ipv6_endpoint{ipv6_address{bytes}, port};
}

struct fixture {
  actor_system_config cfg;
  actor_system sys{cfg};

  template <class T>
  T roundtrip(T x) {
    using container_type = std::vector<byte>;
    container_type buf;
    serializer_impl<container_type> sink(sys, buf);
    if (auto err = sink(x))
      CAF_FAIL("serialization failed: " << sys.render(err));
    binary_deserializer source(sys, make_span(buf));
    T y;
    if (auto err = source(y))
      CAF_FAIL("deserialization failed: " << sys.render(err));
    return y;
  }
};

#define CHECK_TO_STRING(addr) CAF_CHECK_EQUAL(addr, to_string(addr##_ep))

#define CHECK_COMPARISON(addr1, addr2)                                         \
  CAF_CHECK_GREATER(addr2##_ep, addr1##_ep);                                   \
  CAF_CHECK_GREATER_OR_EQUAL(addr2##_ep, addr1##_ep);                          \
  CAF_CHECK_GREATER_OR_EQUAL(addr1##_ep, addr1##_ep);                          \
  CAF_CHECK_GREATER_OR_EQUAL(addr2##_ep, addr2##_ep);                          \
  CAF_CHECK_EQUAL(addr1##_ep, addr1##_ep);                                     \
  CAF_CHECK_EQUAL(addr2##_ep, addr2##_ep);                                     \
  CAF_CHECK_LESS_OR_EQUAL(addr1##_ep, addr2##_ep);                             \
  CAF_CHECK_LESS_OR_EQUAL(addr1##_ep, addr1##_ep);                             \
  CAF_CHECK_LESS_OR_EQUAL(addr2##_ep, addr2##_ep);                             \
  CAF_CHECK_NOT_EQUAL(addr1##_ep, addr2##_ep);                                 \
  CAF_CHECK_NOT_EQUAL(addr2##_ep, addr1##_ep);

#define CHECK_SERIALIZATION(addr)                                              \
  CAF_CHECK_EQUAL(addr##_ep, roundtrip(addr##_ep))

} // namespace

CAF_TEST_FIXTURE_SCOPE(comparison_scope, fixture)

CAF_TEST(constructing assigning and hash_code) {
  const uint16_t port = 8888;
  ipv6_address::array_type bytes{0, 0, 0, 0, 0, 0, 0, 0,
                                 0, 0, 0, 0, 0, 0, 0, 1};
  auto addr = ipv6_address{bytes};
  ipv6_endpoint ep1(addr, port);
  CAF_CHECK_EQUAL(ep1.address(), addr);
  CAF_CHECK_EQUAL(ep1.port(), port);
  ipv6_endpoint ep2;
  ep2.address(addr);
  ep2.port(port);
  CAF_CHECK_EQUAL(ep2.address(), addr);
  CAF_CHECK_EQUAL(ep2.port(), port);
  CAF_CHECK_EQUAL(ep1, ep2);
  CAF_CHECK_EQUAL(ep1.hash_code(), ep2.hash_code());
}

CAF_TEST(to_string) {
  CHECK_TO_STRING("[::1]:8888");
  CHECK_TO_STRING("[4e::d00:0:ed00:0:1]:1234");
  CHECK_TO_STRING("[::1]:1111");
  CHECK_TO_STRING("[4432::33:1]:8732");
  CHECK_TO_STRING("[::2]:8888");
  CHECK_TO_STRING("[4f::d00:12:ed00:0:1]:1234");
  CHECK_TO_STRING("[4f::1]:2222");
  CHECK_TO_STRING("[4432:8d::33:1]:8732");
  CHECK_TO_STRING("[4e::d00:0:ed00:0:1]:5678");
  CHECK_TO_STRING("[::1]:2221");
  CHECK_TO_STRING("[::1]:2222");
  CHECK_TO_STRING("[4432::33:1]:872");
  CHECK_TO_STRING("[4432::33:1]:999");
}

CAF_TEST(comparison) {
  CHECK_COMPARISON("[::1]:8888", "[::2]:8888");
  CHECK_COMPARISON("[4e::d00:0:ed00:0:1]:1234", "[4f::d00:12:ed00:0:1]:1234");
  CHECK_COMPARISON("[::1]:1111", "[4f::1]:2222");
  CHECK_COMPARISON("[4432::33:1]:8732", "[4432:8d::33:1]:8732");
  CHECK_COMPARISON("[::1]:1111", "[::1]:8888");
  CHECK_COMPARISON("[4e::d00:0:ed00:0:1]:1234", "[4e::d00:0:ed00:0:1]:5678");
  CHECK_COMPARISON("[::1]:2221", "[::1]:2222");
  CHECK_COMPARISON("[4432::33:1]:872", "[4432::33:1]:999");
}

CAF_TEST(serialization) {
  CHECK_SERIALIZATION("[::1]:8888");
  CHECK_SERIALIZATION("[4e::d00:0:ed00:0:1]:1234");
  CHECK_SERIALIZATION("[::1]:1111");
  CHECK_SERIALIZATION("[4432::33:1]:8732");
  CHECK_SERIALIZATION("[::2]:8888");
  CHECK_SERIALIZATION("[4f::d00:12:ed00:0:1]:1234");
  CHECK_SERIALIZATION("[4f::1]:2222");
  CHECK_SERIALIZATION("[4432:8d::33:1]:8732");
  CHECK_SERIALIZATION("[4e::d00:0:ed00:0:1]:5678");
  CHECK_SERIALIZATION("[::1]:2221");
  CHECK_SERIALIZATION("[::1]:2222");
  CHECK_SERIALIZATION("[4432::33:1]:872");
  CHECK_SERIALIZATION("[4432::33:1]:999");
}

CAF_TEST_FIXTURE_SCOPE_END()
<|endoftext|>


<commit_before>/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 *
 *  Gearmand client and server library.
 *
 *  Copyright (C) 2011-2013 Data Differential, http://datadifferential.com/
 *  Copyright (C) 2008 Brian Aker, Eric Day
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *
 *      * Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *      * Redistributions in binary form must reproduce the above
 *  copyright notice, this list of conditions and the following disclaimer
 *  in the documentation and/or other materials provided with the
 *  distribution.
 *
 *      * The names of its contributors may not be used to endorse or
 *  promote products derived from this software without specific prior
 *  written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "libgearman/allocator.hpp"
#include "libgearman/server_options.hpp"
#include "libgearman/interface/packet.hpp"
#include "libgearman/interface/error.hpp"
#include "libgearman/vector.h"
#include "libgearman/assert.hpp"
#include "libgearman/ssl.h"

enum universal_options_t
{
  GEARMAN_UNIVERSAL_NON_BLOCKING,
  GEARMAN_UNIVERSAL_DONT_TRACK_PACKETS,
  GEARMAN_UNIVERSAL_IDENTIFY,
  GEARMAN_UNIVERSAL_MAX
};

/**
  @todo this is only used by the server and should be made private.
 */
typedef struct gearman_connection_st gearman_connection_st;
typedef gearman_return_t (gearman_event_watch_fn)(gearman_connection_st *con,
                                                  short events, void *context);

struct gearman_universal_st : public error_st
{
  struct Options {
    bool dont_track_packets;
    bool non_blocking;
    bool no_new_data;
    bool _ssl;

    Options() :
      dont_track_packets(false),
      non_blocking(false),
      no_new_data(false),
      _ssl(false)
    { }
  } options;
  gearman_verbose_t verbose;
  uint32_t con_count;
  uint32_t packet_count;
  uint32_t pfds_size;
  uint32_t sending;
  int timeout; // Connection timeout.
  gearman_connection_st *con_list;
  gearman_server_options_st *server_options_list;
  gearman_packet_st *packet_list;
  struct pollfd *pfds;
  gearman_log_fn *log_fn;
  void *log_context;
  gearman_allocator_t allocator;
  struct gearman_vector_st *_identifier;
  struct gearman_vector_st *_namespace;
  SSL_CTX* _ctx_ssl;
  struct error_st _error;
  int wakeup_fd[2];

  bool ssl() const
  {
    return options._ssl;
  }

  void ssl(bool ssl_)
  {
    options._ssl= ssl_;
  }

  private:
  void close_wakeup();

  public:
  bool wakeup(bool);
  bool has_wakeup() const
  {
    return wakeup_fd[0] != INVALID_SOCKET;
  }

  bool is_non_blocking() const
  {
    return options.non_blocking;
  }

  void non_blocking(bool arg_)
  {
    options.non_blocking= arg_;
  }

  const char *error() const
  {
    if (_error.error() == NULL)
    {
      if (_error.error_code() != GEARMAN_SUCCESS)
      {
        return gearman_strerror(_error.error_code());
      }

      return NULL;
    }

    return _error.error();
  }

  gearman_return_t error_code() const
  {
    return _error.error_code();
  }

  gearman_return_t error_code(const gearman_return_t rc)
  {
    return _error.error_code(rc);
  }

  int last_errno() const
  {
    return _error.system_error();
  }

  void last_errno(const int last_errno_)
  {
    _error.system_error(last_errno_);
  }

  bool has_connections() const
  {
    return con_count;
  }

  void reset_error()
  {
    _error.clear();
  }

  gearman_return_t option(const universal_options_t& option_, bool value);

  gearman_universal_st(const universal_options_t *options_= NULL) :
    verbose(GEARMAN_VERBOSE_NEVER),
    con_count(0),
    packet_count(0),
    pfds_size(0),
    sending(0),
    timeout(-1),
    con_list(NULL),
    server_options_list(NULL),
    packet_list(NULL),
    pfds(NULL),
    log_fn(NULL),
    log_context(NULL),
    allocator(gearman_default_allocator()),
    _identifier(NULL),
    _namespace(NULL),
    _ctx_ssl(NULL)
  {
    wakeup_fd[0]= INVALID_SOCKET;
    wakeup_fd[1]= INVALID_SOCKET;

    if (options_)
    {
      while (*options_ != GEARMAN_UNIVERSAL_MAX)
      {
        /**
          @note Check for bad value, refactor gearman_add_options().
        */
        (void)option(*options_, true);
        options_++;
      }
    }
  }

  const char* ssl_ca_file() const
  {
    if (getenv("GEARMAND_CA_CERTIFICATE"))
    {
      return getenv("GEARMAND_CA_CERTIFICATE");
    }

    return GEARMAND_CA_CERTIFICATE;
  }

  const char* ssl_certificate() const
  {
    if (getenv("GEARMAN_CLIENT_PEM"))
    {
      return getenv("GEARMAN_CLIENT_PEM");
    }

    return GEARMAN_CLIENT_PEM;
  }

  const char* ssl_key() const
  {
    if (getenv("GEARMAN_CLIENT_KEY"))
    {
      return getenv("GEARMAN_CLIENT_KEY");
    }

    return GEARMAN_CLIENT_KEY;
  }

private:
  bool init_ssl();

public:
  SSL_CTX* ctx_ssl()
  {
    if (ssl())
    {
      if (_ctx_ssl == NULL)
      {
        if (init_ssl() == false)
        {
          abort();
        }
      }
      assert(_ctx_ssl);

      return _ctx_ssl;
    }

    return NULL;
  }

  ~gearman_universal_st();

  void identifier(const char *identifier_, const size_t identifier_size_);
  bool has_identifier() const
  {
    return _identifier;
  }

  void flush();
  void reset();
};

static inline bool gearman_universal_is_non_blocking(gearman_universal_st &self)
{
  return self.is_non_blocking();
}

static inline const char *gearman_universal_error(const gearman_universal_st &self)
{
  return self.error();
}

static inline gearman_return_t gearman_universal_error_code(const gearman_universal_st &self)
{
  return self.error_code();
}

static inline int gearman_universal_errno(const gearman_universal_st &self)
{
  return self.last_errno();
}

static inline void universal_reset_error(gearman_universal_st &self)
{
  self.reset_error();
}
<commit_msg>gearman_universal_st refactoring<commit_after>/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 *
 *  Gearmand client and server library.
 *
 *  Copyright (C) 2011-2013 Data Differential, http://datadifferential.com/
 *  Copyright (C) 2008 Brian Aker, Eric Day
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *
 *      * Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *      * Redistributions in binary form must reproduce the above
 *  copyright notice, this list of conditions and the following disclaimer
 *  in the documentation and/or other materials provided with the
 *  distribution.
 *
 *      * The names of its contributors may not be used to endorse or
 *  promote products derived from this software without specific prior
 *  written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "libgearman/allocator.hpp"
#include "libgearman/server_options.hpp"
#include "libgearman/interface/packet.hpp"
#include "libgearman/interface/error.hpp"
#include "libgearman/vector.h"
#include "libgearman/assert.hpp"
#include "libgearman/ssl.h"

enum universal_options_t
{
  GEARMAN_UNIVERSAL_NON_BLOCKING,
  GEARMAN_UNIVERSAL_DONT_TRACK_PACKETS,
  GEARMAN_UNIVERSAL_IDENTIFY,
  GEARMAN_UNIVERSAL_MAX
};

/**
  @todo this is only used by the server and should be made private.
 */
typedef struct gearman_connection_st gearman_connection_st;
typedef gearman_return_t (gearman_event_watch_fn)(gearman_connection_st *con,
                                                  short events, void *context);

struct gearman_universal_st : public error_st
{
  struct Options {
    bool dont_track_packets;
    bool non_blocking;
    bool no_new_data;
    bool _ssl;

    Options() :
      dont_track_packets{false},
      non_blocking{false},
      no_new_data{false},
      _ssl{false}
    { }
  } options;
  gearman_verbose_t verbose;
  uint32_t con_count;
  uint32_t packet_count;
  uint32_t pfds_size;
  uint32_t sending;
  int timeout; // Connection timeout.
  gearman_connection_st *con_list;
  gearman_server_options_st *server_options_list;
  gearman_packet_st *packet_list;
  struct pollfd *pfds;
  gearman_log_fn *log_fn;
  void *log_context;
  gearman_allocator_t allocator;
  struct gearman_vector_st *_identifier;
  struct gearman_vector_st *_namespace;
  SSL_CTX* _ctx_ssl;
  struct error_st _error;
  int wakeup_fd[2];

  bool ssl() const
  {
    return options._ssl;
  }

  void ssl(bool ssl_)
  {
    options._ssl= ssl_;
  }

  private:
  void close_wakeup();

  public:
  bool wakeup(bool);
  bool has_wakeup() const
  {
    return wakeup_fd[0] != INVALID_SOCKET;
  }

  bool is_non_blocking() const
  {
    return options.non_blocking;
  }

  void non_blocking(bool arg_)
  {
    options.non_blocking= arg_;
  }

  const char *error() const
  {
    if (_error.error() == nullptr)
    {
      if (_error.error_code() != GEARMAN_SUCCESS)
      {
        return gearman_strerror(_error.error_code());
      }

      return nullptr;
    }

    return _error.error();
  }

  gearman_return_t error_code() const
  {
    return _error.error_code();
  }

  gearman_return_t error_code(const gearman_return_t rc)
  {
    return _error.error_code(rc);
  }

  int last_errno() const
  {
    return _error.system_error();
  }

  void last_errno(const int last_errno_)
  {
    _error.system_error(last_errno_);
  }

  bool has_connections() const
  {
    return con_count;
  }

  void reset_error()
  {
    _error.clear();
  }

  gearman_return_t option(const universal_options_t& option_, bool value);

  gearman_universal_st(const universal_options_t *options_= nullptr) :
    verbose(GEARMAN_VERBOSE_NEVER),
    con_count(0),
    packet_count(0),
    pfds_size(0),
    sending(0),
    timeout{-1},
    con_list(nullptr),
    server_options_list(nullptr),
    packet_list(nullptr),
    pfds(nullptr),
    log_fn(nullptr),
    log_context(nullptr),
    allocator(gearman_default_allocator()),
    _identifier(nullptr),
    _namespace(nullptr),
    _ctx_ssl(nullptr)
  {
    wakeup_fd[0]= INVALID_SOCKET;
    wakeup_fd[1]= INVALID_SOCKET;

    if (options_)
    {
      while (*options_ != GEARMAN_UNIVERSAL_MAX)
      {
        /**
          @note Check for bad value, refactor gearman_add_options().
        */
        (void)option(*options_, true);
        options_++;
      }
    }
  }

  const char* ssl_ca_file() const
  {
    if (getenv("GEARMAND_CA_CERTIFICATE"))
    {
      return getenv("GEARMAND_CA_CERTIFICATE");
    }

    return GEARMAND_CA_CERTIFICATE;
  }

  const char* ssl_certificate() const
  {
    if (getenv("GEARMAN_CLIENT_PEM"))
    {
      return getenv("GEARMAN_CLIENT_PEM");
    }

    return GEARMAN_CLIENT_PEM;
  }

  const char* ssl_key() const
  {
    if (getenv("GEARMAN_CLIENT_KEY"))
    {
      return getenv("GEARMAN_CLIENT_KEY");
    }

    return GEARMAN_CLIENT_KEY;
  }

private:
  bool init_ssl();

public:
  SSL_CTX* ctx_ssl()
  {
    if (ssl())
    {
      if (!_ctx_ssl)
      {
        if (init_ssl() == false)
        {
          abort();
        }
      }
      assert(_ctx_ssl);

      return _ctx_ssl;
    }

    return nullptr;
  }

  ~gearman_universal_st();

  void identifier(const char *identifier_, const size_t identifier_size_);
  bool has_identifier() const
  {
    return _identifier;
  }

  void flush();
  void reset();
};

static inline bool gearman_universal_is_non_blocking(gearman_universal_st &self)
{
  return self.is_non_blocking();
}

static inline const char *gearman_universal_error(const gearman_universal_st &self)
{
  return self.error();
}

static inline gearman_return_t gearman_universal_error_code(const gearman_universal_st &self)
{
  return self.error_code();
}

static inline int gearman_universal_errno(const gearman_universal_st &self)
{
  return self.last_errno();
}

static inline void universal_reset_error(gearman_universal_st &self)
{
  self.reset_error();
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010-2011 Dmitry Marakasov
 *
 * This file is part of glosm.
 *
 * glosm is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * glosm is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with glosm.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <glosm/TileManager.hh>

#include <glosm/Viewer.hh>
#include <glosm/Geometry.hh>
#include <glosm/GeometryDatasource.hh>
#include <glosm/Tile.hh>
#include <glosm/Exception.hh>

#include <glosm/util/gl.h>

#include <cassert>

TileManager::TileId::TileId(int lev, int xx, int yy) : level(lev), x(xx), y(yy) {
}

bool TileManager::TileId::operator<(const TileId& other) const {
	if (level < other.level) return true;
	if (level > other.level) return false;
	if (x < other.x) return true;
	if (x > other.x) return false;
	return y < other.y;
}

TileManager::TileManager(const Projection projection): projection_(projection) {
	int errn;

	if ((errn = pthread_mutex_init(&tiles_mutex_, 0)) != 0)
		throw SystemError(errn) << "pthread_mutex_init failed";

	if ((errn = pthread_mutex_init(&queue_mutex_, 0)) != 0) {
		pthread_mutex_destroy(&tiles_mutex_);
		throw SystemError(errn) << "pthread_mutex_init failed";
	}

	if ((errn = pthread_cond_init(&queue_cond_, 0)) != 0) {
		pthread_mutex_destroy(&tiles_mutex_);
		pthread_mutex_destroy(&queue_mutex_);
		throw SystemError(errn) << "pthread_cond_init failed";
	}

	if ((errn = pthread_create(&loading_thread_, NULL, LoadingThreadFuncWrapper, (void*)this)) != 0) {
		pthread_mutex_destroy(&tiles_mutex_);
		pthread_mutex_destroy(&queue_mutex_);
		pthread_cond_destroy(&queue_cond_);
		throw SystemError(errn) << "pthread_create failed";
	}

	target_level_ = 0;
	generation_ = 0;
	thread_die_flag_ = false;
}

TileManager::~TileManager() {
	thread_die_flag_ = true;
	pthread_cond_signal(&queue_cond_);

	pthread_join(loading_thread_, NULL);

	pthread_cond_destroy(&queue_cond_);
	pthread_mutex_destroy(&queue_mutex_);
	pthread_mutex_destroy(&tiles_mutex_);

	for (TilesMap::iterator i = tiles_.begin(); i != tiles_.end(); ++i)
		delete i->second.tile;
}

int TileManager::LoadTile(const TileId& id, const BBoxi& bbox, int flags) {
	int ret = 0;
	if (flags & SYNC) {
		Tile* tile = SpawnTile(bbox);
		/* TODO: we may call tile->BindBuffers here */
		tiles_.insert(std::make_pair(id, TileData(tile, generation_)));
	} else {
		bool added = false;
		pthread_mutex_lock(&queue_mutex_);
		/* don't needlessly enqueue more tiles that we can process in a frame time*/
		/* TODO: this should be user-settable, as we don't necessarily do GC/loading
		 * every frame */
		if (loading_.find(id) == loading_.end() && queue_.size() < 2) {
			added = true;
			queue_.push_back(TileTask(id, bbox));
		}
		ret = queue_.size();
		pthread_mutex_unlock(&queue_mutex_);
		if (added)
			pthread_cond_signal(&queue_cond_);
	}

	return ret;
}

/*
 * recursive quadtree processing
 */

bool TileManager::LoadTiles(const BBoxi& bbox, int flags, int level, int x, int y) {
	if (level == target_level_) {
		TilesMap::iterator thistile = tiles_.find(TileId(level, x, y));

		if (thistile != tiles_.end()) {
			thistile->second.generation = generation_;
			return true; /* tile already loaded */
		}

		BBoxi bbox = BBoxi::ForGeoTile(level, x, y);

		LoadTile(TileId(level, x, y), BBoxi::ForGeoTile(level, x, y), flags);

		/* no deeper recursion */
		return true;
	}

	/* children */
	for (int d = 0; d < 4; ++d) {
		int xx = x * 2 + d % 2;
		int yy = y * 2 + d / 2;
		if (BBoxi::ForGeoTile(level + 1, xx, yy).Intersects(bbox)) {
			if (!LoadTiles(bbox, flags, level + 1, xx, yy))
				return false;
		}
	}

	return true;
}

/*
 * loading queue - related
 */

void TileManager::LoadingThreadFunc() {
	pthread_mutex_lock(&queue_mutex_);
	while (!thread_die_flag_) {
		/* found nothing, sleep */
		if (queue_.empty()) {
			pthread_cond_wait(&queue_cond_, &queue_mutex_);
			continue;
		}

		/* take a task from the queue */
		TileTask task = queue_.front();
		queue_.pop_front();

		/* mark it as loading */
		std::pair<LoadingSet::iterator, bool> pair = loading_.insert(task.id);
		assert(pair.second);

		pthread_mutex_unlock(&queue_mutex_);

		/* load tile */
		Tile* tile = SpawnTile(task.bbox);

		pthread_mutex_lock(&tiles_mutex_);
		tiles_.insert(std::make_pair(task.id, TileData(tile, generation_)));
		pthread_mutex_unlock(&tiles_mutex_);

		pthread_mutex_lock(&queue_mutex_);
		loading_.erase(pair.first);
	}
	pthread_mutex_unlock(&queue_mutex_);
}

void* TileManager::LoadingThreadFuncWrapper(void* arg) {
	static_cast<TileManager*>(arg)->LoadingThreadFunc();
	return NULL;
}

/*
 * protected interface
 */

void TileManager::Render(const Viewer& viewer) {
	pthread_mutex_lock(&tiles_mutex_);

	/* and render them */
	for (TilesMap::iterator i = tiles_.begin(); i != tiles_.end(); ++i) {
		if (i->second.generation != generation_)
			continue;

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();

		/* prepare modelview matrix for the tile: position
		 * it in the right place given that viewer is always
		 * at (0, 0, 0) */
		Vector3f offset = projection_.Project(i->second.tile->GetReference(), Vector2i(viewer.GetPos(projection_))) +
				projection_.Project(Vector2i(viewer.GetPos(projection_)), viewer.GetPos(projection_));

		glTranslatef(offset.x, offset.y, offset.z);

		/* same for rotation */
		Vector3i ref = i->second.tile->GetReference();
		Vector3i pos = viewer.GetPos(projection_);

		/* normal at tile's reference point */
		Vector3d refnormal = (
				(Vector3d)projection_.Project(Vector3i(ref.x, ref.y, std::numeric_limits<osmint_t>::max()), pos) -
				(Vector3d)projection_.Project(Vector3i(ref.x, ref.y, 0), pos)
			).Normalized();

		/* normal at reference point projected to equator */
		Vector3d refeqnormal = (
				(Vector3d)projection_.Project(Vector3i(ref.x, 0, std::numeric_limits<osmint_t>::max()), pos) -
				(Vector3d)projection_.Project(Vector3i(ref.x, 0, 0), pos)
			).Normalized();

		/* normal at north pole */
		Vector3d polenormal = (
				(Vector3d)projection_.Project(Vector3i(ref.x, 900000000, std::numeric_limits<osmint_t>::max()), pos) -
				(Vector3d)projection_.Project(Vector3i(ref.x, 900000000, 0), pos)
			).Normalized();

		/* XXX: IsValid() check basically detects
		 * MercatorProjection and does no rotation for it.
		 * While is's ok for now, this may need more generic
		 * approach in future */
		if (polenormal.IsValid()) {
			Vector3d side = refnormal.CrossProduct(polenormal).Normalized();

			glRotatef((double)((osmlong_t)ref.y - (osmlong_t)pos.y) / 10000000.0, side.x, side.y, side.z);
			glRotatef((double)((osmlong_t)ref.x - (osmlong_t)pos.x) / 10000000.0, polenormal.x, polenormal.y, polenormal.z);
		}

		i->second.tile->Render();

		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
	}

	pthread_mutex_unlock(&tiles_mutex_);
}

/*
 * public interface
 */

void TileManager::SetTargetLevel(int level) {
	target_level_ = level;
}

void TileManager::RequestVisible(const BBoxi& bbox, int flags) {
	if (!flags & SYNC) {
		pthread_mutex_lock(&queue_mutex_);
		queue_.clear();
		pthread_mutex_unlock(&queue_mutex_);
	}

	pthread_mutex_lock(&tiles_mutex_);
	if (flags & BLOB)
		LoadTile(TileId(0, 0, 0), bbox, flags);
	else
		LoadTiles(bbox, flags);
	pthread_mutex_unlock(&tiles_mutex_);
}

void TileManager::GarbageCollect() {
	/* TODO: may put deletable tiles into list and delete after
	 * unlocking mutex for less contention */
	pthread_mutex_lock(&tiles_mutex_);
	for (TilesMap::iterator i = tiles_.begin(); i != tiles_.end(); ) {
		if (i->second.generation != generation_) {
			delete i->second.tile;
			TilesMap::iterator tmp = i++;
			tiles_.erase(tmp);
		} else {
			i++;
		}
	}
	generation_++;
	pthread_mutex_unlock(&tiles_mutex_);
}
<commit_msg>Fix uninitialized variable usage<commit_after>/*
 * Copyright (C) 2010-2011 Dmitry Marakasov
 *
 * This file is part of glosm.
 *
 * glosm is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * glosm is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with glosm.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <glosm/TileManager.hh>

#include <glosm/Viewer.hh>
#include <glosm/Geometry.hh>
#include <glosm/GeometryDatasource.hh>
#include <glosm/Tile.hh>
#include <glosm/Exception.hh>

#include <glosm/util/gl.h>

#include <cassert>

TileManager::TileId::TileId(int lev, int xx, int yy) : level(lev), x(xx), y(yy) {
}

bool TileManager::TileId::operator<(const TileId& other) const {
	if (level < other.level) return true;
	if (level > other.level) return false;
	if (x < other.x) return true;
	if (x > other.x) return false;
	return y < other.y;
}

TileManager::TileManager(const Projection projection): projection_(projection) {
	target_level_ = 0;
	generation_ = 0;
	thread_die_flag_ = false;

	int errn;

	if ((errn = pthread_mutex_init(&tiles_mutex_, 0)) != 0)
		throw SystemError(errn) << "pthread_mutex_init failed";

	if ((errn = pthread_mutex_init(&queue_mutex_, 0)) != 0) {
		pthread_mutex_destroy(&tiles_mutex_);
		throw SystemError(errn) << "pthread_mutex_init failed";
	}

	if ((errn = pthread_cond_init(&queue_cond_, 0)) != 0) {
		pthread_mutex_destroy(&tiles_mutex_);
		pthread_mutex_destroy(&queue_mutex_);
		throw SystemError(errn) << "pthread_cond_init failed";
	}

	if ((errn = pthread_create(&loading_thread_, NULL, LoadingThreadFuncWrapper, (void*)this)) != 0) {
		pthread_mutex_destroy(&tiles_mutex_);
		pthread_mutex_destroy(&queue_mutex_);
		pthread_cond_destroy(&queue_cond_);
		throw SystemError(errn) << "pthread_create failed";
	}
}

TileManager::~TileManager() {
	thread_die_flag_ = true;
	pthread_cond_signal(&queue_cond_);

	/* TODO: check exit code */
	pthread_join(loading_thread_, NULL);

	pthread_cond_destroy(&queue_cond_);
	pthread_mutex_destroy(&queue_mutex_);
	pthread_mutex_destroy(&tiles_mutex_);

	for (TilesMap::iterator i = tiles_.begin(); i != tiles_.end(); ++i)
		delete i->second.tile;
}

int TileManager::LoadTile(const TileId& id, const BBoxi& bbox, int flags) {
	int ret = 0;
	if (flags & SYNC) {
		Tile* tile = SpawnTile(bbox);
		/* TODO: we may call tile->BindBuffers here */
		tiles_.insert(std::make_pair(id, TileData(tile, generation_)));
	} else {
		bool added = false;
		pthread_mutex_lock(&queue_mutex_);
		/* don't needlessly enqueue more tiles that we can process in a frame time*/
		/* TODO: this should be user-settable, as we don't necessarily do GC/loading
		 * every frame */
		if (loading_.find(id) == loading_.end() && queue_.size() < 2) {
			added = true;
			queue_.push_back(TileTask(id, bbox));
		}
		ret = queue_.size();
		pthread_mutex_unlock(&queue_mutex_);
		if (added)
			pthread_cond_signal(&queue_cond_);
	}

	return ret;
}

/*
 * recursive quadtree processing
 */

bool TileManager::LoadTiles(const BBoxi& bbox, int flags, int level, int x, int y) {
	if (level == target_level_) {
		TilesMap::iterator thistile = tiles_.find(TileId(level, x, y));

		if (thistile != tiles_.end()) {
			thistile->second.generation = generation_;
			return true; /* tile already loaded */
		}

		BBoxi bbox = BBoxi::ForGeoTile(level, x, y);

		LoadTile(TileId(level, x, y), BBoxi::ForGeoTile(level, x, y), flags);

		/* no deeper recursion */
		return true;
	}

	/* children */
	for (int d = 0; d < 4; ++d) {
		int xx = x * 2 + d % 2;
		int yy = y * 2 + d / 2;
		if (BBoxi::ForGeoTile(level + 1, xx, yy).Intersects(bbox)) {
			if (!LoadTiles(bbox, flags, level + 1, xx, yy))
				return false;
		}
	}

	return true;
}

/*
 * loading queue - related
 */

void TileManager::LoadingThreadFunc() {
	pthread_mutex_lock(&queue_mutex_);
	while (!thread_die_flag_) {
		/* found nothing, sleep */
		if (queue_.empty()) {
			pthread_cond_wait(&queue_cond_, &queue_mutex_);
			continue;
		}

		/* take a task from the queue */
		TileTask task = queue_.front();
		queue_.pop_front();

		/* mark it as loading */
		std::pair<LoadingSet::iterator, bool> pair = loading_.insert(task.id);
		assert(pair.second);

		pthread_mutex_unlock(&queue_mutex_);

		/* load tile */
		Tile* tile = SpawnTile(task.bbox);

		pthread_mutex_lock(&tiles_mutex_);
		tiles_.insert(std::make_pair(task.id, TileData(tile, generation_)));
		pthread_mutex_unlock(&tiles_mutex_);

		pthread_mutex_lock(&queue_mutex_);
		loading_.erase(pair.first);
	}
	pthread_mutex_unlock(&queue_mutex_);
}

void* TileManager::LoadingThreadFuncWrapper(void* arg) {
	static_cast<TileManager*>(arg)->LoadingThreadFunc();
	return NULL;
}

/*
 * protected interface
 */

void TileManager::Render(const Viewer& viewer) {
	pthread_mutex_lock(&tiles_mutex_);

	/* and render them */
	for (TilesMap::iterator i = tiles_.begin(); i != tiles_.end(); ++i) {
		if (i->second.generation != generation_)
			continue;

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();

		/* prepare modelview matrix for the tile: position
		 * it in the right place given that viewer is always
		 * at (0, 0, 0) */
		Vector3f offset = projection_.Project(i->second.tile->GetReference(), Vector2i(viewer.GetPos(projection_))) +
				projection_.Project(Vector2i(viewer.GetPos(projection_)), viewer.GetPos(projection_));

		glTranslatef(offset.x, offset.y, offset.z);

		/* same for rotation */
		Vector3i ref = i->second.tile->GetReference();
		Vector3i pos = viewer.GetPos(projection_);

		/* normal at tile's reference point */
		Vector3d refnormal = (
				(Vector3d)projection_.Project(Vector3i(ref.x, ref.y, std::numeric_limits<osmint_t>::max()), pos) -
				(Vector3d)projection_.Project(Vector3i(ref.x, ref.y, 0), pos)
			).Normalized();

		/* normal at reference point projected to equator */
		Vector3d refeqnormal = (
				(Vector3d)projection_.Project(Vector3i(ref.x, 0, std::numeric_limits<osmint_t>::max()), pos) -
				(Vector3d)projection_.Project(Vector3i(ref.x, 0, 0), pos)
			).Normalized();

		/* normal at north pole */
		Vector3d polenormal = (
				(Vector3d)projection_.Project(Vector3i(ref.x, 900000000, std::numeric_limits<osmint_t>::max()), pos) -
				(Vector3d)projection_.Project(Vector3i(ref.x, 900000000, 0), pos)
			).Normalized();

		/* XXX: IsValid() check basically detects
		 * MercatorProjection and does no rotation for it.
		 * While is's ok for now, this may need more generic
		 * approach in future */
		if (polenormal.IsValid()) {
			Vector3d side = refnormal.CrossProduct(polenormal).Normalized();

			glRotatef((double)((osmlong_t)ref.y - (osmlong_t)pos.y) / 10000000.0, side.x, side.y, side.z);
			glRotatef((double)((osmlong_t)ref.x - (osmlong_t)pos.x) / 10000000.0, polenormal.x, polenormal.y, polenormal.z);
		}

		i->second.tile->Render();

		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
	}

	pthread_mutex_unlock(&tiles_mutex_);
}

/*
 * public interface
 */

void TileManager::SetTargetLevel(int level) {
	target_level_ = level;
}

void TileManager::RequestVisible(const BBoxi& bbox, int flags) {
	if (!flags & SYNC) {
		pthread_mutex_lock(&queue_mutex_);
		queue_.clear();
		pthread_mutex_unlock(&queue_mutex_);
	}

	pthread_mutex_lock(&tiles_mutex_);
	if (flags & BLOB)
		LoadTile(TileId(0, 0, 0), bbox, flags);
	else
		LoadTiles(bbox, flags);
	pthread_mutex_unlock(&tiles_mutex_);
}

void TileManager::GarbageCollect() {
	/* TODO: may put deletable tiles into list and delete after
	 * unlocking mutex for less contention */
	pthread_mutex_lock(&tiles_mutex_);
	for (TilesMap::iterator i = tiles_.begin(); i != tiles_.end(); ) {
		if (i->second.generation != generation_) {
			delete i->second.tile;
			TilesMap::iterator tmp = i++;
			tiles_.erase(tmp);
		} else {
			i++;
		}
	}
	generation_++;
	pthread_mutex_unlock(&tiles_mutex_);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 neurodata (http://neurodata.io/)
 * Written by Disa Mhembere (disa@jhu.edu)
 *
 * This file is part of knor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY CURRENT_KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "util.hpp"
#include <cassert>

using namespace knor::base;

void test_hclust_floor() {
    assert(get_hclust_floor(1) == 1);
    assert(get_hclust_floor(2) == 2);
    assert(get_hclust_floor(3) == 2);
    assert(get_hclust_floor(4) == 4);
    assert(get_hclust_floor(5) == 4);
    assert(get_hclust_floor(6) == 4);
    assert(get_hclust_floor(7) == 4);
    assert(get_hclust_floor(8) == 8);
    assert(get_hclust_floor(9) == 8);
    assert(get_hclust_floor(10) == 8);
    assert(get_hclust_floor(15) == 8);
    assert(get_hclust_floor(16) == 16);
    assert(get_hclust_floor(20) == 16);
    assert(get_hclust_floor(27) == 16);
    assert(get_hclust_floor(31) == 16);
    assert(get_hclust_floor(32) == 32);
    printf("hclust_floor test OK ...\n");
}

void test_hclust_ceil() {
    assert(get_hclust_ceil(1) == 1);
    assert(get_hclust_ceil(2) == 2);
    assert(get_hclust_ceil(3) == 4);
    assert(get_hclust_ceil(4) == 4);
    assert(get_hclust_ceil(5) == 8);
    assert(get_hclust_ceil(6) == 8);
    assert(get_hclust_ceil(7) == 8);
    assert(get_hclust_ceil(8) == 8);
    assert(get_hclust_ceil(9) == 16);
    assert(get_hclust_ceil(10) == 16);
    assert(get_hclust_ceil(15) == 16);
    assert(get_hclust_ceil(16) == 16);
    assert(get_hclust_ceil(20) == 32);
    assert(get_hclust_ceil(27) == 32);
    assert(get_hclust_ceil(31) == 32);
    assert(get_hclust_ceil(32) == 32);
    assert(get_hclust_ceil(33) == 64);
    assert(get_hclust_ceil(63) == 64);

    printf("hclust_ceil test OK ...\n");
}

int main() {
    test_hclust_floor();
    test_hclust_ceil();
    printf("Successful util test!\n");
}
<commit_msg>test get_max_hnodes<commit_after>/*
 * Copyright 2016 neurodata (http://neurodata.io/)
 * Written by Disa Mhembere (disa@jhu.edu)
 *
 * This file is part of knor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY CURRENT_KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "util.hpp"
#include <cassert>

using namespace knor::base;

void test_hclust_floor() {
    assert(get_hclust_floor(1) == 1);
    assert(get_hclust_floor(2) == 2);
    assert(get_hclust_floor(3) == 2);
    assert(get_hclust_floor(4) == 4);
    assert(get_hclust_floor(5) == 4);
    assert(get_hclust_floor(6) == 4);
    assert(get_hclust_floor(7) == 4);
    assert(get_hclust_floor(8) == 8);
    assert(get_hclust_floor(9) == 8);
    assert(get_hclust_floor(10) == 8);
    assert(get_hclust_floor(15) == 8);
    assert(get_hclust_floor(16) == 16);
    assert(get_hclust_floor(20) == 16);
    assert(get_hclust_floor(27) == 16);
    assert(get_hclust_floor(31) == 16);
    assert(get_hclust_floor(32) == 32);
    printf("hclust_floor test OK ...\n");
}

void test_get_max_hnodes() {
    assert(get_max_hnodes(4) == 7);
    assert(get_max_hnodes(8) == 15);
    assert(get_max_hnodes(16) == 31);
    assert(get_max_hnodes(32) == 63);
    assert(get_max_hnodes(64) == 127);
    assert(get_max_hnodes(128) == 255);
    printf("test_get_max_hnodes test OK ...\n");
}

void test_hclust_ceil() {
    assert(get_hclust_ceil(1) == 1);
    assert(get_hclust_ceil(2) == 2);
    assert(get_hclust_ceil(3) == 4);
    assert(get_hclust_ceil(4) == 4);
    assert(get_hclust_ceil(5) == 8);
    assert(get_hclust_ceil(6) == 8);
    assert(get_hclust_ceil(7) == 8);
    assert(get_hclust_ceil(8) == 8);
    assert(get_hclust_ceil(9) == 16);
    assert(get_hclust_ceil(10) == 16);
    assert(get_hclust_ceil(15) == 16);
    assert(get_hclust_ceil(16) == 16);
    assert(get_hclust_ceil(20) == 32);
    assert(get_hclust_ceil(27) == 32);
    assert(get_hclust_ceil(31) == 32);
    assert(get_hclust_ceil(32) == 32);
    assert(get_hclust_ceil(33) == 64);
    assert(get_hclust_ceil(63) == 64);

    printf("hclust_ceil test OK ...\n");
}

int main() {
    test_hclust_floor();
    test_hclust_ceil();
    test_get_max_hnodes();
    printf("Successful util test!\n");
}
<|endoftext|>


<commit_before>/*
    kopetemessagemanager.cpp - Manages all chats

    Copyright (c) 2002      by Duncan Mac-Vicar Prett <duncan@kde.org>
    Copyright (c) 2002      by Daniel Stone <dstone@kde.org>
    Copyright (c) 2002      by Martijn Klingens <klingens@kde.org>
    Copyright (c) 2002-2003 by Olivier Goffart <ogoffart@tiscalinet.be>
    Copyright (c) 2003      by Jason Keirstead   <jason@keirstead.org>

    Kopete    (c) 2002-2003 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This library is free software; you can redistribute it and/or         *
    * modify it under the terms of the GNU Lesser General Public            *
    * License as published by the Free Software Foundation; either          *
    * version 2 of the License, or (at your option) any later version.      *
    *                                                                       *
    *************************************************************************
*/

#include <kdebug.h>
#include <klocale.h>
#include <knotifyclient.h>
#include <qapplication.h>
#include <kglobal.h>
#include <qregexp.h>

#include "kopeteaccount.h"
#include "kopetemessagemanager.h"
#include "kopetemessagemanagerfactory.h"
#include "kopeteprefs.h"
#include "kopetemetacontact.h"
#include "kopetecommandhandler.h"
#include "kopeteview.h"

struct KMMPrivate
{
	KopeteContactPtrList mContactList;
	const KopeteContact *mUser;
	QMap<const KopeteContact *, KopeteOnlineStatus> contactStatus;
	KopeteProtocol *mProtocol;
	int mId;
	bool isEmpty;
	bool mCanBeDeleted;
	QDateTime awayTime;
	QString displayName;
	KopeteView *view;
};

KopeteMessageManager::KopeteMessageManager( const KopeteContact *user,
	KopeteContactPtrList others, KopeteProtocol *protocol, int id, const char *name )
: QObject( user->account(), name )
{
	d = new KMMPrivate;
	d->mUser = user;
	d->mProtocol = protocol;
	d->mId = id;
	d->isEmpty= others.isEmpty();
	d->mCanBeDeleted = true;
	d->view=0L;

	for( KopeteContact *c = others.first(); c; c = others.next() )
	{
		addContact(c,true);
	}

	connect( user, SIGNAL( onlineStatusChanged( KopeteContact *, const KopeteOnlineStatus &, const KopeteOnlineStatus & ) ), this,
		SLOT( slotStatusChanged( KopeteContact *, const KopeteOnlineStatus &, const KopeteOnlineStatus & ) ) );

//	kdDebug(14010) << k_funcinfo << endl;
}

KopeteMessageManager::~KopeteMessageManager()
{
//	kdDebug(14010) << k_funcinfo << endl;

	for( KopeteContact *c = d->mContactList.first(); c; c = d->mContactList.next() )
		c->setConversations( c->conversations() - 1 );

	if (!d) return;
	d->mCanBeDeleted = false; //prevent double deletion
	KopeteMessageManagerFactory::factory()->removeSession( this );
	emit(closing( this ) );
	delete d;
}

void KopeteMessageManager::slotStatusChanged( KopeteContact *c, const KopeteOnlineStatus &status, const KopeteOnlineStatus &oldStatus )
{
	if(!KopetePrefs::prefs()->notifyAway())
		return;
	if( status.status() == KopeteOnlineStatus::Away )
	{
		d->awayTime = QDateTime::currentDateTime();
		KopeteMessage msg(c, d->mContactList, i18n("%1 has been marked as away.")
			.arg( QString::fromLatin1("/me") ), KopeteMessage::Outbound, KopeteMessage::PlainText);
		sendMessage( msg );
	}
	else if( oldStatus.status() == KopeteOnlineStatus::Away && status.status() == KopeteOnlineStatus::Online )
	{
		KopeteMessage msg(c, d->mContactList, i18n("%1 is no longer marked as away. Gone since %1")
			.arg( QString::fromLatin1("/me") ).arg(KGlobal::locale()->formatDateTime(d->awayTime, true)), KopeteMessage::Outbound, KopeteMessage::PlainText);
		sendMessage( msg );
	}
}

void KopeteMessageManager::setContactOnlineStatus( const KopeteContact *contact, const KopeteOnlineStatus &status )
{
	d->contactStatus[ contact ] = status;
}

const KopeteOnlineStatus KopeteMessageManager::contactOnlineStatus( const KopeteContact *contact ) const
{
	if( d->contactStatus.contains( contact ) )
		return d->contactStatus[ contact ];

	return contact->onlineStatus();
}

const QString KopeteMessageManager::displayName()
{
	if( d->displayName.isNull() )
	{
		connect( this, SIGNAL( contactChanged() ), this, SLOT( slotUpdateDisplayName() ) );
		slotUpdateDisplayName();
	}

	return d->displayName;
}

void KopeteMessageManager::setDisplayName( const QString &newName )
{
//	kdDebug(14010) << k_funcinfo << endl;
	disconnect( this, SIGNAL( contactChanged() ), this, SLOT( slotUpdateDisplayName() ) );

	d->displayName = newName;

	emit( displayNameChanged() );
}

void KopeteMessageManager::slotUpdateDisplayName()
{
//	kdDebug(14010) << k_funcinfo << endl;

	QString nextDisplayName;

	KopeteContact *c = d->mContactList.first();
	if( c->metaContact() )
		d->displayName = c->metaContact()->displayName();
	else
		d->displayName = c->displayName();

	//If we have only 1 contact, add the status of him
	if( d->mContactList.count() == 1 )
	{
		d->displayName.append( QString::fromLatin1( " (%1)").arg( c->onlineStatus().description() ) );
	}
	else
	{
		while( ( c = d->mContactList.next() ) )
		{
			if( c->metaContact() )
				nextDisplayName = c->metaContact()->displayName();
			else
				nextDisplayName = c->displayName();
			d->displayName.append( QString::fromLatin1( ", " ) ).append( nextDisplayName );
		}
	}

	emit( displayNameChanged() );
}

const KopeteContactPtrList& KopeteMessageManager::members() const
{
	return d->mContactList;
}

const KopeteContact* KopeteMessageManager::user() const
{
	return d->mUser;
}

KopeteProtocol* KopeteMessageManager::protocol() const
{
	return d->mProtocol;
}

int KopeteMessageManager::mmId() const
{
	return d->mId;
}

void KopeteMessageManager::setMMId( int id )
{
	d->mId = id;
}

void KopeteMessageManager::sendMessage(KopeteMessage &message)
{
	message.setManager(this);
	KopeteMessage sentMessage = message;
	if( !KopeteCommandHandler::commandHandler()->processMessage( message, this ) )
	{
		emit messageSent(sentMessage, this);
		if ( !account()->isAway() || KopetePrefs::prefs()->soundIfAway() )
			KNotifyClient::event( QString::fromLatin1( "kopete_outgoing"), i18n("Outgoing Message Sent") );
	}
	else
		emit( messageSuccess() );
}

void KopeteMessageManager::messageSucceeded()
{
	emit( messageSuccess() );
}

void KopeteMessageManager::appendMessage( KopeteMessage &msg )
{
//	kdDebug(14010) << k_funcinfo << endl;
	msg.setManager(this);

	if( msg.direction() == KopeteMessage::Inbound )
	{
		if( KopetePrefs::prefs()->highlightEnabled() && msg.plainBody().contains( QRegExp(QString::fromLatin1("\\b(%1)\\b").arg(user()->displayName()),false) ) )
			msg.setImportance( KopeteMessage::Highlight );

		emit( messageReceived( msg, this ) );
	}

	emit messageAppended( msg, this );
}

void KopeteMessageManager::addContact( const KopeteContact *c, bool supress )
{
	if ( d->mContactList.contains(c) )
	{
		kdDebug(14010) << k_funcinfo << "Contact already exists" <<endl;
		emit contactAdded(c, supress);
	}
	else
	{
		if(d->mContactList.count()==1 && d->isEmpty)
		{
			KopeteContact *old=d->mContactList.first();
			d->mContactList.remove(old);
			d->mContactList.append(c);
			//disconnect (old, SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
			disconnect (old, SIGNAL(onlineStatusChanged( KopeteContact*, const KopeteOnlineStatus&, const KopeteOnlineStatus&)), this, SIGNAL(contactChanged()));
			if(old->metaContact())
				disconnect (old->metaContact(), SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
			emit contactAdded(c, supress);
			emit contactRemoved(old, QString::null);
		}
		else
		{
			d->mContactList.append(c);
			emit contactAdded(c, supress);
		}
		c->setConversations( c->conversations() + 1 );
		//connect (c, SIGNAL(displayNameChanged(const QString &,const QString &)), this, SIGNAL(contactChanged()));
		connect (c, SIGNAL(onlineStatusChanged( KopeteContact*, const KopeteOnlineStatus&, const KopeteOnlineStatus&)), this, SIGNAL(contactChanged()));
		if(c->metaContact())
			connect (c->metaContact(), SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
		connect (c, SIGNAL(contactDestroyed(KopeteContact*)) , this , SLOT(slotContactDestroyed(KopeteContact*)));
	}
	d->isEmpty=false;
	slotUpdateDisplayName();
}

void KopeteMessageManager::removeContact( const KopeteContact *c, const QString& raison )
{
	if(!c || !d->mContactList.contains(c))
		return;

	if(d->mContactList.count()==1)
	{
		kdDebug(14010) << k_funcinfo << "Contact not removed. Keep always one contact" <<endl;
		d->isEmpty=true;
	}
	else
	{
		d->mContactList.remove( c );
		//disconnect (c, SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
		disconnect (c, SIGNAL(onlineStatusChanged( KopeteContact*, const KopeteOnlineStatus&, const KopeteOnlineStatus&)), this, SIGNAL(contactChanged()));
		if(c->metaContact())
			disconnect (c->metaContact(), SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
		c->setConversations( c->conversations() - 1 );
	}
	emit contactRemoved(c, raison);
	slotUpdateDisplayName();
}

void KopeteMessageManager::receivedTypingMsg( const KopeteContact *c , bool t )
{
	emit(remoteTyping( c, t ));
}

void KopeteMessageManager::receivedTypingMsg( const QString &contactId , bool t )
{
	for( KopeteContact *it = d->mContactList.first(); it; it = d->mContactList.next() )
	{
		if( it->contactId() == contactId )
		{
			receivedTypingMsg( it, t );
			return;
		}
	}
}

void KopeteMessageManager::typing ( bool t )
{
	emit typingMsg(t);
}

void KopeteMessageManager::setCanBeDeleted ( bool b )
{
	d->mCanBeDeleted = b;
	if(b && !d->view)
		deleteLater();
}

KopeteView* KopeteMessageManager::view(bool canCreate  , KopeteMessage::MessageType type )
{
	if(!d->view && canCreate)
	{
		d->view=KopeteMessageManagerFactory::factory()->createView( this , type );
		connect( d->view->mainWidget(), SIGNAL( closing( KopeteView * ) ), this, SLOT( slotViewDestroyed( ) ) );
	}
	return d->view;
}

void KopeteMessageManager::slotViewDestroyed()
{
	d->view=0L;
	if(d->mCanBeDeleted)
		deleteLater();
}

KopeteAccount *KopeteMessageManager::account() const
{
	return user()->account();
}

void KopeteMessageManager::slotContactDestroyed(KopeteContact* c)
{
	if(!c || !d->mContactList.contains(c))
		return;

	if(d->mContactList.count()==1)
	{
		deleteLater(); //the contact has been deleted, it is better to delete this
	}
	else
	{
		d->mContactList.remove( c );
		emit contactRemoved(c , QString::null);
	}
}

#include "kopetemessagemanager.moc"

// vim: set noet ts=4 sts=4 sw=4:
<commit_msg>wow, forget a connection<commit_after>/*
    kopetemessagemanager.cpp - Manages all chats

    Copyright (c) 2002      by Duncan Mac-Vicar Prett <duncan@kde.org>
    Copyright (c) 2002      by Daniel Stone <dstone@kde.org>
    Copyright (c) 2002      by Martijn Klingens <klingens@kde.org>
    Copyright (c) 2002-2003 by Olivier Goffart <ogoffart@tiscalinet.be>
    Copyright (c) 2003      by Jason Keirstead   <jason@keirstead.org>

    Kopete    (c) 2002-2003 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This library is free software; you can redistribute it and/or         *
    * modify it under the terms of the GNU Lesser General Public            *
    * License as published by the Free Software Foundation; either          *
    * version 2 of the License, or (at your option) any later version.      *
    *                                                                       *
    *************************************************************************
*/

#include <kdebug.h>
#include <klocale.h>
#include <knotifyclient.h>
#include <qapplication.h>
#include <kglobal.h>
#include <qregexp.h>

#include "kopeteaccount.h"
#include "kopetemessagemanager.h"
#include "kopetemessagemanagerfactory.h"
#include "kopeteprefs.h"
#include "kopetemetacontact.h"
#include "kopetecommandhandler.h"
#include "kopeteview.h"

struct KMMPrivate
{
	KopeteContactPtrList mContactList;
	const KopeteContact *mUser;
	QMap<const KopeteContact *, KopeteOnlineStatus> contactStatus;
	KopeteProtocol *mProtocol;
	int mId;
	bool isEmpty;
	bool mCanBeDeleted;
	QDateTime awayTime;
	QString displayName;
	KopeteView *view;
};

KopeteMessageManager::KopeteMessageManager( const KopeteContact *user,
	KopeteContactPtrList others, KopeteProtocol *protocol, int id, const char *name )
: QObject( user->account(), name )
{
	d = new KMMPrivate;
	d->mUser = user;
	d->mProtocol = protocol;
	d->mId = id;
	d->isEmpty= others.isEmpty();
	d->mCanBeDeleted = true;
	d->view=0L;

	for( KopeteContact *c = others.first(); c; c = others.next() )
	{
		addContact(c,true);
	}

	connect( user, SIGNAL( onlineStatusChanged( KopeteContact *, const KopeteOnlineStatus &, const KopeteOnlineStatus & ) ), this,
		SLOT( slotStatusChanged( KopeteContact *, const KopeteOnlineStatus &, const KopeteOnlineStatus & ) ) );

	connect( this, SIGNAL( contactChanged() ), this, SLOT( slotUpdateDisplayName() ) );
}

KopeteMessageManager::~KopeteMessageManager()
{
	for( KopeteContact *c = d->mContactList.first(); c; c = d->mContactList.next() )
		c->setConversations( c->conversations() - 1 );

	if (!d) return;
	d->mCanBeDeleted = false; //prevent double deletion
	KopeteMessageManagerFactory::factory()->removeSession( this );
	emit(closing( this ) );
	delete d;
}

void KopeteMessageManager::slotStatusChanged( KopeteContact *c, const KopeteOnlineStatus &status, const KopeteOnlineStatus &oldStatus )
{
	if(!KopetePrefs::prefs()->notifyAway())
		return;
	if( status.status() == KopeteOnlineStatus::Away )
	{
		d->awayTime = QDateTime::currentDateTime();
		KopeteMessage msg(c, d->mContactList, i18n("%1 has been marked as away.")
			.arg( QString::fromLatin1("/me") ), KopeteMessage::Outbound, KopeteMessage::PlainText);
		sendMessage( msg );
	}
	else if( oldStatus.status() == KopeteOnlineStatus::Away && status.status() == KopeteOnlineStatus::Online )
	{
		KopeteMessage msg(c, d->mContactList, i18n("%1 is no longer marked as away. Gone since %1")
			.arg( QString::fromLatin1("/me") ).arg(KGlobal::locale()->formatDateTime(d->awayTime, true)), KopeteMessage::Outbound, KopeteMessage::PlainText);
		sendMessage( msg );
	}
}

void KopeteMessageManager::setContactOnlineStatus( const KopeteContact *contact, const KopeteOnlineStatus &status )
{
	d->contactStatus[ contact ] = status;
}

const KopeteOnlineStatus KopeteMessageManager::contactOnlineStatus( const KopeteContact *contact ) const
{
	if( d->contactStatus.contains( contact ) )
		return d->contactStatus[ contact ];

	return contact->onlineStatus();
}

const QString KopeteMessageManager::displayName()
{
	if( d->displayName.isNull() )
	{
		connect( this, SIGNAL( contactChanged() ), this, SLOT( slotUpdateDisplayName() ) );
		slotUpdateDisplayName();
	}

	return d->displayName;
}

void KopeteMessageManager::setDisplayName( const QString &newName )
{
	disconnect( this, SIGNAL( contactChanged() ), this, SLOT( slotUpdateDisplayName() ) );

	d->displayName = newName;

	emit( displayNameChanged() );
}

void KopeteMessageManager::slotUpdateDisplayName()
{
	QString nextDisplayName;

	KopeteContact *c = d->mContactList.first();
	if( c->metaContact() )
		d->displayName = c->metaContact()->displayName();
	else
		d->displayName = c->displayName();

	//If we have only 1 contact, add the status of him
	if( d->mContactList.count() == 1 )
	{
		d->displayName.append( QString::fromLatin1( " (%1)").arg( c->onlineStatus().description() ) );
	}
	else
	{
		while( ( c = d->mContactList.next() ) )
		{
			if( c->metaContact() )
				nextDisplayName = c->metaContact()->displayName();
			else
				nextDisplayName = c->displayName();
			d->displayName.append( QString::fromLatin1( ", " ) ).append( nextDisplayName );
		}
	}

	emit( displayNameChanged() );
}

const KopeteContactPtrList& KopeteMessageManager::members() const
{
	return d->mContactList;
}

const KopeteContact* KopeteMessageManager::user() const
{
	return d->mUser;
}

KopeteProtocol* KopeteMessageManager::protocol() const
{
	return d->mProtocol;
}

int KopeteMessageManager::mmId() const
{
	return d->mId;
}

void KopeteMessageManager::setMMId( int id )
{
	d->mId = id;
}

void KopeteMessageManager::sendMessage(KopeteMessage &message)
{
	message.setManager(this);
	KopeteMessage sentMessage = message;
	if( !KopeteCommandHandler::commandHandler()->processMessage( message, this ) )
	{
		emit messageSent(sentMessage, this);
		if ( !account()->isAway() || KopetePrefs::prefs()->soundIfAway() )
			KNotifyClient::event( QString::fromLatin1( "kopete_outgoing"), i18n("Outgoing Message Sent") );
	}
	else
		emit( messageSuccess() );
}

void KopeteMessageManager::messageSucceeded()
{
	emit( messageSuccess() );
}

void KopeteMessageManager::appendMessage( KopeteMessage &msg )
{
	msg.setManager(this);

	if( msg.direction() == KopeteMessage::Inbound )
	{
		if( KopetePrefs::prefs()->highlightEnabled() && msg.plainBody().contains( QRegExp(QString::fromLatin1("\\b(%1)\\b").arg(user()->displayName()),false) ) )
			msg.setImportance( KopeteMessage::Highlight );

		emit( messageReceived( msg, this ) );
	}

	emit messageAppended( msg, this );
}

void KopeteMessageManager::addContact( const KopeteContact *c, bool supress )
{
	if ( d->mContactList.contains(c) )
	{
		kdDebug(14010) << k_funcinfo << "Contact already exists" <<endl;
		emit contactAdded(c, supress);
	}
	else
	{
		if(d->mContactList.count()==1 && d->isEmpty)
		{
			KopeteContact *old=d->mContactList.first();
			d->mContactList.remove(old);
			d->mContactList.append(c);
			//disconnect (old, SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
			disconnect (old, SIGNAL(onlineStatusChanged( KopeteContact*, const KopeteOnlineStatus&, const KopeteOnlineStatus&)), this, SIGNAL(contactChanged()));
			if(old->metaContact())
				disconnect (old->metaContact(), SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
			emit contactAdded(c, supress);
			emit contactRemoved(old, QString::null);
		}
		else
		{
			d->mContactList.append(c);
			emit contactAdded(c, supress);
		}
		c->setConversations( c->conversations() + 1 );
		//connect (c, SIGNAL(displayNameChanged(const QString &,const QString &)), this, SIGNAL(contactChanged()));
		connect (c, SIGNAL(onlineStatusChanged( KopeteContact*, const KopeteOnlineStatus&, const KopeteOnlineStatus&)), this, SIGNAL(contactChanged()));
		if(c->metaContact())
			connect (c->metaContact(), SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
		connect (c, SIGNAL(contactDestroyed(KopeteContact*)) , this , SLOT(slotContactDestroyed(KopeteContact*)));
	}
	d->isEmpty=false;
	slotUpdateDisplayName();
}

void KopeteMessageManager::removeContact( const KopeteContact *c, const QString& raison )
{
	if(!c || !d->mContactList.contains(c))
		return;

	if(d->mContactList.count()==1)
	{
		kdDebug(14010) << k_funcinfo << "Contact not removed. Keep always one contact" <<endl;
		d->isEmpty=true;
	}
	else
	{
		d->mContactList.remove( c );
		//disconnect (c, SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
		disconnect (c, SIGNAL(onlineStatusChanged( KopeteContact*, const KopeteOnlineStatus&, const KopeteOnlineStatus&)), this, SIGNAL(contactChanged()));
		if(c->metaContact())
			disconnect (c->metaContact(), SIGNAL(displayNameChanged(const QString &, const QString &)), this, SIGNAL(contactChanged()));
		disconnect (c, SIGNAL(contactDestroyed(KopeteContact*)) , this , SLOT(slotContactDestroyed(KopeteContact*)));
		c->setConversations( c->conversations() - 1 );
	}
	emit contactRemoved(c, raison);
	slotUpdateDisplayName();
}

void KopeteMessageManager::receivedTypingMsg( const KopeteContact *c , bool t )
{
	emit(remoteTyping( c, t ));
}

void KopeteMessageManager::receivedTypingMsg( const QString &contactId , bool t )
{
	for( KopeteContact *it = d->mContactList.first(); it; it = d->mContactList.next() )
	{
		if( it->contactId() == contactId )
		{
			receivedTypingMsg( it, t );
			return;
		}
	}
}

void KopeteMessageManager::typing ( bool t )
{
	emit typingMsg(t);
}

void KopeteMessageManager::setCanBeDeleted ( bool b )
{
	d->mCanBeDeleted = b;
	if(b && !d->view)
		deleteLater();
}

KopeteView* KopeteMessageManager::view(bool canCreate  , KopeteMessage::MessageType type )
{
	if(!d->view && canCreate)
	{
		d->view=KopeteMessageManagerFactory::factory()->createView( this , type );
		connect( d->view->mainWidget(), SIGNAL( closing( KopeteView * ) ), this, SLOT( slotViewDestroyed( ) ) );
	}
	return d->view;
}

void KopeteMessageManager::slotViewDestroyed()
{
	d->view=0L;
	if(d->mCanBeDeleted)
		deleteLater();
}

KopeteAccount *KopeteMessageManager::account() const
{
	return user()->account();
}

void KopeteMessageManager::slotContactDestroyed(KopeteContact* c)
{
	if(!c || !d->mContactList.contains(c))
		return;

	if(d->mContactList.count()==1)
	{
		deleteLater(); //the contact has been deleted, it is better to delete this
	}
	else
	{
		d->mContactList.remove( c );
		emit contactRemoved(c , QString::null);
	}
}

#include "kopetemessagemanager.moc"

// vim: set noet ts=4 sts=4 sw=4:
<|endoftext|>


<commit_before>// -*-mode: c++; indent-tabs-mode: nil; -*-

#include <iostream>
#include <fstream>

#include <sys/types.h> // opendir
#include <dirent.h>    // opendir, readdir

#include <sys/types.h> // stat
#include <sys/stat.h>  // stat
#include <unistd.h>    // stat

#include <limits.h>    // realpath
#include <stdlib.h>    // realpath

#include <string.h>    // strerror_r
#include <errno.h>     // errno

#include <set>
#include <vector>
#include <map>
#include <limits.h> // PATH_MAX http://stackoverflow.com/a/9449307/257924
#include <regex>

bool realPathToString(const std::string & inPath, std::string & realPath, bool verbose)
{
  realPath.resize(PATH_MAX);
  // Get the realPath for duplicate detection.
  const char * tmp = realpath(inPath.c_str(), &(realPath[0]));
  // Warn about bad links:
  if (!tmp) {
    char strbuf[1024];
    const char * errstring = strerror_r(errno, strbuf, sizeof(strbuf));
    if (verbose) {
      std::cerr << "WARNING: Failed resolve: " << inPath << " : " << errstring << std::endl;
    }
    realPath = "";
    return false;
  }
  realPath.resize(strlen(&(realPath[0])));
  return true;
}

namespace VerboseNS {

  enum VerbosityLevel {
    E_NONE      = 0
    , E_VERBOSE = 1 << 0
    , E_DEBUG   = 1 << 1
  };

  class VerbosityTracker
  {
  public:

    VerbosityTracker() : _verbosity(E_NONE) {}

    bool isAtVerbosity(int level) { return (_verbosity & level) != 0; }

    void setVerbosity(int value)
    {
      _verbosity = value;
    }

#define DEFINE_SET_VERBOSITY_METHOD(TYPE)       \
  TYPE & setVerbosity(int value) \
  { \
    _verbosity.setVerbosity(value); \
    return *this; \
  }

  private:
    int _verbosity;
  };

};

class UniqueFileVisitor
{
public:
  virtual bool visit(const std::string & absPath) = 0;
};

class UniqueFileScanner
{
public:
  UniqueFileScanner()
    : _visitor(NULL)
  {}

  UniqueFileScanner & setVisitor(UniqueFileVisitor * visitor)
  {
    _visitor = visitor;
    return *this;
  }

  DEFINE_SET_VERBOSITY_METHOD(UniqueFileScanner);

  bool scan(const std::string & inDir)
  {
    DIR * dirfp = opendir(inDir.c_str());

    if (!dirfp) {
      std::cerr << "ERROR: Failed to open directory: " << inDir << std::endl;
      return false;
    }

    struct dirent *result = nullptr;
    struct stat statbuf;
    std::string absPath;
    std::string realPath;
    errno = 0;
    while ((result = readdir(dirfp)) != nullptr) {
      std::string basename = result->d_name;

      // Skip directories we obviously should not traverse into:
      if (basename == "." || basename == ".." || basename == ".git")
        continue;

      if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
        std::cout << std::endl;
      }

      // Fully-qualify the directory entry in preparation for calling stat:
      absPath = inDir;
      absPath += "/";
      absPath += basename;
      if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
        std::cout << "absPath  " << absPath << std::endl;
      }

      // Identify the type of file it is:
      int status = stat(absPath.c_str(), &statbuf);
      if (status == 0) { // stat succeeded

        // Get the realPath for duplicate detection.
        if (!realPathToString(absPath, realPath, _verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)))
          continue;

        if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
          std::cout << "realPath " << realPath << std::endl;
        }

        // Avoid parsing the same file or directory twice (can happen via symbolic links):
        SetT::iterator iter = _seen.find(realPath);
        if(iter != _seen.end()) {
          if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
            std::cout << "Skipping previously processed file or directory: " << realPath << std::endl;
          }
          continue;
        }
        _seen.insert(realPath);

        if ( S_ISDIR(statbuf.st_mode) ) {
          if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
            std::cout << "directory absPath " << absPath << std::endl;
          }
          // recurse:
          if (!scan(absPath)) {
            return false;
          }
        } else if ( S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode) ) {
          if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
            std::cout << "parsable absPath " << absPath << std::endl;
          }
          if (!_visitor->visit(absPath))
            return false;
        } else {
          if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
            std::cout << "Skipping non-regular file: " << absPath << std::endl;
          }
          continue;
        }
      } else { // stat failed
        if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
          std::cerr << "WARNING: Failed to stat: " << absPath << std::endl;
        }
        continue;
      }

    }

    if (errno) {
      std::cerr << "ERROR: Failed to read from directory: " << inDir << " : " << strerror(errno) << std::endl;
      return false;
    }

    closedir(dirfp);
    return true;
  }

private:
  typedef std::set<std::string> SetT;
  SetT _seen;
  UniqueFileVisitor * _visitor; // not owned by this object.
  VerboseNS::VerbosityTracker _verbosity;
};

class OrgIDParser : public UniqueFileVisitor
{
  std::regex _id_regexp{
    "\n"
    "\\s*:PROPERTIES:\\s*\n"
    "\\s*:ID:\\s*([a-z0-9-]+)",
    std::regex_constants::icase};

public:
 OrgIDParser() {}

  virtual bool visit(const std::string & absPath)
  {

    // Only work on Org-mode files:
    static const char * orgExtension = ".org";
    std::size_t pos;
    if ( (pos = absPath.rfind(orgExtension)) != std::string::npos ) {
      if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
        std::cout << "Processing " << absPath << std::endl;
      }

      // Parse the file:
      return parse(absPath);

    }
    return true;
  }

  bool parse(const std::string & absPath)
  {
    std::ifstream stream(absPath.c_str(), std::ifstream::binary);
    if (!stream) {
      std::cerr << "ERROR: Failed to open: " << absPath << std::endl;
      return false;
    }

    // Get length of file:
    stream.seekg (0, stream.end);
    std::streampos length = stream.tellg();
    stream.seekg (0, stream.beg);

    // Allocate memory:
    std::string buffer(length, 0);

    // Read data as a block:
    stream.read(&(buffer[0]), length);

    stream.close();

    auto ids_begin = std::sregex_iterator(buffer.begin(), buffer.end(), _id_regexp);
    auto ids_end = std::sregex_iterator();

    for (std::sregex_iterator i = ids_begin; i != ids_end; ++i) {
      std::smatch match = *i;
      std::string id = match.str(1);

      IdSetT & ids = _map[absPath]; // Create a IdSetT if one does not exist
      if (ids.find(id) != ids.end()) {
        std::cerr << "ERROR: Duplicate ID value " << id << " at position " << match.position(1) << " in file " << absPath << std::endl;
        return false;
      }
      ids.insert(id);

    }

    return true;
  }

  bool writeIdAlistFile(const std::string & alistPath)
  {
    std::ofstream stream(alistPath.c_str());
    if (!stream) {
      std::cerr << "ERROR: Cannot open " << alistPath << " for writing." << std::endl;
      return false;
    }

    bool first = true;
    stream << "(";
    for (FileToIdSetT::iterator cur = _map.begin(); cur != _map.end(); ++cur)
    {
      const std::string & absPath = cur->first;

      if (!first) {
        stream << std::endl << " ";
      }

      stream << "(\"" << absPath << "\"";
      IdSetT & ids = cur->second;

      for (IdSetT::iterator cur = ids.begin(); cur != ids.end(); ++cur)
      {
        stream << " \"" << *cur << "\"";
      }

      stream << ")";

      first = false;
    }
    stream << ")" << std::endl;

    return true;
  }

  DEFINE_SET_VERBOSITY_METHOD(OrgIDParser);

private:
  VerboseNS::VerbosityTracker _verbosity;
  typedef std::set<std::string> IdSetT;
  typedef std::map<std::string, IdSetT > FileToIdSetT;
  FileToIdSetT _map;
};

int main(int argc, char *argv[], char *const envp[])
{
  int verbosity = VerboseNS::E_NONE;
  bool readingDirectories = false;
  typedef std::vector<std::string> DirectoryListT;
  DirectoryListT directories;
  std::string idAlistPath;
  for (int i = 1; i < argc; i++)
  {
    std::string arg = argv[i];
    if (arg.empty()) break;
    if (readingDirectories) {
      // Get the realPath for things like ".":
      std::string realPath;
      if (!realPathToString(arg, realPath, true))
        continue;
      directories.push_back(realPath);
    } else {
      if (arg == "--") {
        readingDirectories = true;
      } else if (arg == "-verbose") {
        verbosity |= VerboseNS::E_VERBOSE;
      } else if (arg == "-debug") {
        verbosity |= VerboseNS::E_DEBUG;
      } else if (arg == "-o") {
        if (i + 1 >= argc) {
          std::cerr << "ERROR: -o requires a value." << std::endl;
          return 1;
        }
        idAlistPath = argv[++i];
      } else if (arg == "-h" || arg == "--help") {
        std::cerr << "USAGE: " << argv[0] << " -o OUTPUT-ALIST-FILE [ -verbose ] [ -debug ] [ -h ] -- DIR-1 DIR-2 ... DIR-N" << std::endl;
        return 0;
      } else {
        std::cerr << "ERROR: Unrecognized option " << arg << std::endl;
        return 1;
      }
    }
  }

  if (directories.empty()) {
    std::cerr << "ERROR: No directories specified." << std::endl;
    return 1;
  }

  if (idAlistPath.empty()) {
    std::cerr << "ERROR: No alistfile specified." << std::endl;
    return 1;
  }

  // OrgIDParser will house the results:
  OrgIDParser parser;
  parser.setVerbosity(verbosity);

  // UniqueFileScanner scans for files and removes duplicates, visiting each
  // with the parser:
  UniqueFileScanner scanner;
  scanner
    .setVerbosity(verbosity)
    .setVisitor(&parser);
  for (DirectoryListT::iterator cur = directories.begin(); cur != directories.end(); ++cur)
  {
    int success = scanner.scan(*cur);
    if (!success) {
      return 1;
    }
  }

  // Write out the alist file:
  parser.writeIdAlistFile(idAlistPath);

  return 0;
} // end main

<commit_msg>org-id-scanner.cpp: Reset errno after a failed stat<commit_after>// -*-mode: c++; indent-tabs-mode: nil; -*-

#include <iostream>
#include <fstream>

#include <sys/types.h> // opendir
#include <dirent.h>    // opendir, readdir

#include <sys/types.h> // stat
#include <sys/stat.h>  // stat
#include <unistd.h>    // stat

#include <limits.h>    // realpath
#include <stdlib.h>    // realpath

#include <string.h>    // strerror_r
#include <errno.h>     // errno

#include <set>
#include <vector>
#include <map>
#include <limits.h> // PATH_MAX http://stackoverflow.com/a/9449307/257924
#include <regex>

bool realPathToString(const std::string & inPath, std::string & realPath, bool verbose)
{
  realPath.resize(PATH_MAX);
  // Get the realPath for duplicate detection.
  const char * tmp = realpath(inPath.c_str(), &(realPath[0]));
  // Warn about bad links:
  if (!tmp) {
    char strbuf[1024];
    const char * errstring = strerror_r(errno, strbuf, sizeof(strbuf));
    if (verbose) {
      std::cerr << "WARNING: Failed resolve: " << inPath << " : " << errstring << std::endl;
    }
    realPath = "";
    return false;
  }
  realPath.resize(strlen(&(realPath[0])));
  return true;
}

namespace VerboseNS {

  enum VerbosityLevel {
    E_NONE      = 0
    , E_VERBOSE = 1 << 0
    , E_DEBUG   = 1 << 1
  };

  class VerbosityTracker
  {
  public:

    VerbosityTracker() : _verbosity(E_NONE) {}

    bool isAtVerbosity(int level) { return (_verbosity & level) != 0; }

    void setVerbosity(int value)
    {
      _verbosity = value;
    }

#define DEFINE_SET_VERBOSITY_METHOD(TYPE)       \
  TYPE & setVerbosity(int value) \
  { \
    _verbosity.setVerbosity(value); \
    return *this; \
  }

  private:
    int _verbosity;
  };

};

class UniqueFileVisitor
{
public:
  virtual bool visit(const std::string & absPath) = 0;
};

class UniqueFileScanner
{
public:
  UniqueFileScanner()
    : _visitor(NULL)
  {}

  UniqueFileScanner & setVisitor(UniqueFileVisitor * visitor)
  {
    _visitor = visitor;
    return *this;
  }

  DEFINE_SET_VERBOSITY_METHOD(UniqueFileScanner);

  bool scan(const std::string & inDir)
  {
    DIR * dirfp = opendir(inDir.c_str());

    if (!dirfp) {
      std::cerr << "ERROR: Failed to open directory: " << inDir << std::endl;
      return false;
    }

    struct dirent *result = nullptr;
    struct stat statbuf;
    std::string absPath;
    std::string realPath;
    errno = 0;
    while ((result = readdir(dirfp)) != nullptr) {
      std::string basename = result->d_name;

      // Skip directories we obviously should not traverse into:
      if (basename == "." || basename == ".." || basename == ".git")
        continue;

      if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
        std::cout << std::endl;
      }

      // Fully-qualify the directory entry in preparation for calling stat:
      absPath = inDir;
      absPath += "/";
      absPath += basename;
      if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
        std::cout << "absPath  " << absPath << std::endl;
      }

      // Identify the type of file it is:
      int status = stat(absPath.c_str(), &statbuf);
      if (status == 0) { // stat succeeded

        // Get the realPath for duplicate detection.
        if (!realPathToString(absPath, realPath, _verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)))
          continue;

        if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
          std::cout << "realPath " << realPath << std::endl;
        }

        // Avoid parsing the same file or directory twice (can happen via symbolic links):
        SetT::iterator iter = _seen.find(realPath);
        if(iter != _seen.end()) {
          if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
            std::cout << "Skipping previously processed file or directory: " << realPath << std::endl;
          }
          continue;
        }
        _seen.insert(realPath);

        if ( S_ISDIR(statbuf.st_mode) ) {
          if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
            std::cout << "directory absPath " << absPath << std::endl;
          }
          // recurse:
          if (!scan(absPath)) {
            return false;
          }
        } else if ( S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode) ) {
          if (_verbosity.isAtVerbosity(VerboseNS::E_DEBUG)) {
            std::cout << "parsable absPath " << absPath << std::endl;
          }
          if (!_visitor->visit(absPath))
            return false;
        } else {
          if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
            std::cout << "Skipping non-regular file: " << absPath << std::endl;
          }
          continue;
        }
      } else { // stat failed
        if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
          std::cerr << "WARNING: Failed to stat: " << absPath << std::endl;
          errno = 0; // reset errno so we do not fail after the loop
        }
        continue;
      }

    }

    if (errno) {
      std::cerr << "ERROR: Failed to read from directory: " << inDir << " : " << strerror(errno) << std::endl;
      return false;
    }

    closedir(dirfp);
    return true;
  }

private:
  typedef std::set<std::string> SetT;
  SetT _seen;
  UniqueFileVisitor * _visitor; // not owned by this object.
  VerboseNS::VerbosityTracker _verbosity;
};

class OrgIDParser : public UniqueFileVisitor
{
  std::regex _id_regexp{
    "\n"
    "\\s*:PROPERTIES:\\s*\n"
    "\\s*:ID:\\s*([a-z0-9-]+)",
    std::regex_constants::icase};

public:
 OrgIDParser() {}

  virtual bool visit(const std::string & absPath)
  {

    // Only work on Org-mode files:
    static const char * orgExtension = ".org";
    std::size_t pos;
    if ( (pos = absPath.rfind(orgExtension)) != std::string::npos ) {
      if (_verbosity.isAtVerbosity(VerboseNS::E_VERBOSE)) {
        std::cout << "Processing " << absPath << std::endl;
      }

      // Parse the file:
      return parse(absPath);

    }
    return true;
  }

  bool parse(const std::string & absPath)
  {
    std::ifstream stream(absPath.c_str(), std::ifstream::binary);
    if (!stream) {
      std::cerr << "ERROR: Failed to open: " << absPath << std::endl;
      return false;
    }

    // Get length of file:
    stream.seekg (0, stream.end);
    std::streampos length = stream.tellg();
    stream.seekg (0, stream.beg);

    // Allocate memory:
    std::string buffer(length, 0);

    // Read data as a block:
    stream.read(&(buffer[0]), length);

    stream.close();

    auto ids_begin = std::sregex_iterator(buffer.begin(), buffer.end(), _id_regexp);
    auto ids_end = std::sregex_iterator();

    for (std::sregex_iterator i = ids_begin; i != ids_end; ++i) {
      std::smatch match = *i;
      std::string id = match.str(1);

      IdSetT & ids = _map[absPath]; // Create a IdSetT if one does not exist
      if (ids.find(id) != ids.end()) {
        std::cerr << "ERROR: Duplicate ID value " << id << " at position " << match.position(1) << " in file " << absPath << std::endl;
        return false;
      }
      ids.insert(id);

    }

    return true;
  }

  bool writeIdAlistFile(const std::string & alistPath)
  {
    std::ofstream stream(alistPath.c_str());
    if (!stream) {
      std::cerr << "ERROR: Cannot open " << alistPath << " for writing." << std::endl;
      return false;
    }

    bool first = true;
    stream << "(";
    for (FileToIdSetT::iterator cur = _map.begin(); cur != _map.end(); ++cur)
    {
      const std::string & absPath = cur->first;

      if (!first) {
        stream << std::endl << " ";
      }

      stream << "(\"" << absPath << "\"";
      IdSetT & ids = cur->second;

      for (IdSetT::iterator cur = ids.begin(); cur != ids.end(); ++cur)
      {
        stream << " \"" << *cur << "\"";
      }

      stream << ")";

      first = false;
    }
    stream << ")" << std::endl;

    return true;
  }

  DEFINE_SET_VERBOSITY_METHOD(OrgIDParser);

private:
  VerboseNS::VerbosityTracker _verbosity;
  typedef std::set<std::string> IdSetT;
  typedef std::map<std::string, IdSetT > FileToIdSetT;
  FileToIdSetT _map;
};

int main(int argc, char *argv[], char *const envp[])
{
  int verbosity = VerboseNS::E_NONE;
  bool readingDirectories = false;
  typedef std::vector<std::string> DirectoryListT;
  DirectoryListT directories;
  std::string idAlistPath;
  for (int i = 1; i < argc; i++)
  {
    std::string arg = argv[i];
    if (arg.empty()) break;
    if (readingDirectories) {
      // Get the realPath for things like ".":
      std::string realPath;
      if (!realPathToString(arg, realPath, true))
        continue;
      directories.push_back(realPath);
    } else {
      if (arg == "--") {
        readingDirectories = true;
      } else if (arg == "-verbose") {
        verbosity |= VerboseNS::E_VERBOSE;
      } else if (arg == "-debug") {
        verbosity |= VerboseNS::E_DEBUG;
      } else if (arg == "-o") {
        if (i + 1 >= argc) {
          std::cerr << "ERROR: -o requires a value." << std::endl;
          return 1;
        }
        idAlistPath = argv[++i];
      } else if (arg == "-h" || arg == "--help") {
        std::cerr << "USAGE: " << argv[0] << " -o OUTPUT-ALIST-FILE [ -verbose ] [ -debug ] [ -h ] -- DIR-1 DIR-2 ... DIR-N" << std::endl;
        return 0;
      } else {
        std::cerr << "ERROR: Unrecognized option " << arg << std::endl;
        return 1;
      }
    }
  }

  if (directories.empty()) {
    std::cerr << "ERROR: No directories specified." << std::endl;
    return 1;
  }

  if (idAlistPath.empty()) {
    std::cerr << "ERROR: No alistfile specified." << std::endl;
    return 1;
  }

  // OrgIDParser will house the results:
  OrgIDParser parser;
  parser.setVerbosity(verbosity);

  // UniqueFileScanner scans for files and removes duplicates, visiting each
  // with the parser:
  UniqueFileScanner scanner;
  scanner
    .setVerbosity(verbosity)
    .setVisitor(&parser);
  for (DirectoryListT::iterator cur = directories.begin(); cur != directories.end(); ++cur)
  {
    int success = scanner.scan(*cur);
    if (!success) {
      return 1;
    }
  }

  // Write out the alist file:
  parser.writeIdAlistFile(idAlistPath);

  return 0;
} // end main

<|endoftext|>