<commit_before>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#if defined(_MSC_VER)
#pragma warning(4:4503)
#endif

// BZFlag common header
#include "common.h"

// interface header
#include "KeyManager.h"

// system headers
#include <assert.h>
#include <ctype.h>
#include <string.h> // strstr, etc
#include <string>   // std::string
#include <vector>

// local implementation headers
#include "BzfEvent.h"

// initialize the singleton
template <>
KeyManager* Singleton<KeyManager>::_instance = (KeyManager*)0;

const char*		KeyManager::buttonNames[] = {
  "???",
  "Pause",
  "Home",
  "End",
  "Left Arrow",
  "Right Arrow",
  "Up Arrow",
  "Down Arrow",
  "Page Up",
  "Page Down",
  "Insert",
  "Backspace",
  "Delete",
  "Kp0",
  "Kp1",
  "Kp2",
  "Kp3",
  "Kp4",
  "Kp5",
  "Kp6",
  "Kp7",
  "Kp8",
  "Kp9",
  "Kp_Period",
  "Kp_Divide",
  "Kp_Multiply",
  "Kp_Minus",
  "Kp_Plus",
  "Kp_Enter",
  "Kp_Equals",
  "F1",
  "F2",
  "F3",
  "F4",
  "F5",
  "F6",
  "F7",
  "F8",
  "F9",
  "F10",
  "F11",
  "F12",
  "Help",
  "Print",
  "Sysreq",
  "Break",
  "Menu",
  "Power",
  "Euro",
  "Undo",
  "Left Mouse",
  "Middle Mouse",
  "Right Mouse",
  "Wheel Up",
  "Wheel Down",
  "Mouse Button 6",
  "Mouse Button 7",
  "Mouse Button 8",
  "Mouse Button 9",
  "Mouse Button 10",
  "Joystick Button 1",
  "Joystick Button 2",
  "Joystick Button 3",
  "Joystick Button 4",
  "Joystick Button 5",
  "Joystick Button 6",
  "Joystick Button 7",
  "Joystick Button 8",
  "Joystick Button 9",
  "Joystick Button 10",
  "LastButton"  // should always be last item listed
};
const char*		KeyManager::asciiNames[][2] = {
  { "Tab",		"\t" },
  { "Backspace",	"\b" },
  { "Enter",		"\r" },
  { "Space",		" "  }
};

KeyManager::KeyManager()
{
  unsigned int i;

  // prep string to key map
  BzfKeyEvent key;
  key.ascii  = 0;
  key.shift  = 0;
  for (i = BzfKeyEvent::Pause; i < BzfKeyEvent::LastButton; ++i) {
    key.button = static_cast<BzfKeyEvent::Button>(i);
    stringToEvent.insert(std::make_pair(std::string(buttonNames[i]), key));
  }
  key.button = BzfKeyEvent::NoButton;
  for (i = 0; i < countof(asciiNames); ++i) {
    key.ascii = asciiNames[i][1][0];
    stringToEvent.insert(std::make_pair(std::string(asciiNames[i][0]), key));
  }
  char buffer[2];
  buffer[1] = 0;
  for (i = 0x21; i < 0x7f; ++i) {
    buffer[0] = key.ascii = static_cast<char>(i);
    stringToEvent.insert(std::make_pair(std::string(buffer), key));
  }
}

KeyManager::~KeyManager()
{
}

void			KeyManager::bind(const BzfKeyEvent& key,
					 bool press, const std::string& cmd)
{
  if (press) {
    pressEventToCommand.erase(key);
    pressEventToCommand.insert(std::make_pair(key, cmd));
  } else {
    releaseEventToCommand.erase(key);
    releaseEventToCommand.insert(std::make_pair(key, cmd));
  }
  notify(key, press, cmd);
}

void			KeyManager::unbind(const BzfKeyEvent& key,
					   bool press)
{
  if (press)
    pressEventToCommand.erase(key);
  else
    releaseEventToCommand.erase(key);
  notify(key, press, "");
}

void			KeyManager::unbindCommand(const char* command)
{
  EventToCommandMap::iterator index;
  EventToCommandMap::iterator deleteme;

  index = pressEventToCommand.begin();
  while (index != pressEventToCommand.end()) {
    if (index->second == command) {
      deleteme = index;
      index++;
      unbind(deleteme->first, true);
    } else {
      index++;
    }
  }

  index = releaseEventToCommand.begin();
  while (index != releaseEventToCommand.end()) {
    if (index->second == command) {
      deleteme = index;
      index++;
      unbind(deleteme->first, false);
    } else {
      index++;
    }
  }
}

std::string		KeyManager::get(const BzfKeyEvent& key,
					bool press) const
{
  const EventToCommandMap* map = press ? &pressEventToCommand :
    &releaseEventToCommand;
  EventToCommandMap::const_iterator index = map->find(key);
  if (index == map->end())
    return "";
  else
    return index->second;
}

std::vector<std::string> KeyManager::getKeysFromCommand(std::string command, bool press) const
{
  std::vector<std::string> keys;
  EventToCommandMap::const_iterator index;
  if (press) {
    for (index = pressEventToCommand.begin(); index != pressEventToCommand.end(); ++index) {
      if (index->second == command) {
	keys.push_back(this->keyEventToString(index->first));
      }
    }
  } else {
    for (index = releaseEventToCommand.begin(); index != releaseEventToCommand.end(); ++index) {
      if (index->second == command) {
	keys.push_back(this->keyEventToString(index->first));
      }
    }
  }
  return keys;
}

std::string		KeyManager::keyEventToString(
					const BzfKeyEvent& key) const
{
  std::string name;
  if (key.shift & BzfKeyEvent::ShiftKey)
    name += "Shift+";
  if (key.shift & BzfKeyEvent::ControlKey)
    name += "Ctrl+";
  if (key.shift & BzfKeyEvent::AltKey)
    name += "Alt+";
  switch (key.ascii) {
    case 0:
      return name + buttonNames[key.button];
    case '\b':
      return name + "Backspace";
    case '\t':
      return name + "Tab";
    case '\r':
      return name + "Enter";
    case ' ':
      return name + "Space";
    default:
      if (!isspace((unsigned char)key.ascii))
	return name + std::string(&key.ascii, 1);
      return name + "???";
  }
}

bool			KeyManager::stringToKeyEvent(
				const std::string& name, BzfKeyEvent& key) const
{
  // find last + in name
  const char* shiftDelimiter = strrchr(name.c_str(), '+');

  // split name into shift part and key name part
  std::string shiftPart, keyPart;
  if (shiftDelimiter == NULL) {
    keyPart = name;
  } else {
    shiftPart  = "+";
    shiftPart += name.substr(0, shiftDelimiter - name.c_str() + 1);
    keyPart    = shiftDelimiter + 1;
  }

  // find key name
  StringToEventMap::const_iterator index = stringToEvent.find(keyPart);
  if (index == stringToEvent.end())
    return false;

  // get key event sans shift state
  key = index->second;

  // apply shift state
  if (strstr(shiftPart.c_str(), "+Shift+") != NULL ||
      strstr(shiftPart.c_str(), "+shift+") != NULL)
    key.shift |= BzfKeyEvent::ShiftKey;
  if (strstr(shiftPart.c_str(), "+Ctrl+") != NULL ||
      strstr(shiftPart.c_str(), "+ctrl+") != NULL)
    key.shift |= BzfKeyEvent::ControlKey;
  if (strstr(shiftPart.c_str(), "+Alt+") != NULL ||
      strstr(shiftPart.c_str(), "+alt+") != NULL)
    key.shift |= BzfKeyEvent::AltKey;

  // success
  return true;
}

void			KeyManager::iterate(
				IterateCallback callback, void* userData)
{
  assert(callback != NULL);

  EventToCommandMap::const_iterator index;
  for (index = pressEventToCommand.begin(); index != pressEventToCommand.end(); ++index)
    (*callback)(keyEventToString(index->first), true, index->second, userData);
  for (index = releaseEventToCommand.begin(); index != releaseEventToCommand.end(); ++index)
    (*callback)(keyEventToString(index->first), false, index->second, userData);
}

void			KeyManager::addCallback(
				ChangeCallback callback, void* userData)
{
  callbacks.add(callback, userData);
}

void			KeyManager::removeCallback(
				ChangeCallback callback, void* userData)
{
  callbacks.remove(callback, userData);
}

void			KeyManager::notify(
				const BzfKeyEvent& key,
				bool press, const std::string& cmd)
{
  CallbackInfo info;
  info.name  = keyEventToString(key);
  info.press = press;
  info.cmd   = cmd;
  callbacks.iterate(&onCallback, &info);
}

bool			KeyManager::onCallback(
				ChangeCallback callback,
				void* userData,
				void* vinfo)
{
  CallbackInfo* info = reinterpret_cast<CallbackInfo*>(vinfo);
  callback(info->name, info->press, info->cmd, userData);
  return true;
}

bool			KeyManager::KeyEventLess::operator()(
				const BzfKeyEvent& a,
				const BzfKeyEvent& b) const
{
  if (a.ascii == 0 && b.ascii == 0) {
    if (a.button < b.button)
      return true;
    if (a.button > b.button)
      return false;

    // check shift
    if (a.shift < b.shift)
      return true;
  } else if (a.ascii == 0 && b.ascii != 0) {
    return true;
  } else if (a.ascii != 0 && b.ascii == 0) {
    return false;
  } else {
    if (toupper(a.ascii) < toupper(b.ascii))
      return true;
    if (toupper(a.ascii) > toupper(b.ascii))
      return false;

    // check shift state without shift key
    if ((a.shift & ~BzfKeyEvent::ShiftKey) < (b.shift & ~BzfKeyEvent::ShiftKey))
      return true;
  }

  return false;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8

<commit_msg>my bad, back to what it was since interface includes it<commit_after>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#if defined(_MSC_VER)
#pragma warning(4:4503)
#endif

// interface header
#include "KeyManager.h"

// system headers
#include <assert.h>
#include <ctype.h>
#include <string.h> // strstr, etc
#include <string>   // std::string
#include <vector>

// local implementation headers
#include "BzfEvent.h"

// initialize the singleton
template <>
KeyManager* Singleton<KeyManager>::_instance = (KeyManager*)0;

const char*		KeyManager::buttonNames[] = {
  "???",
  "Pause",
  "Home",
  "End",
  "Left Arrow",
  "Right Arrow",
  "Up Arrow",
  "Down Arrow",
  "Page Up",
  "Page Down",
  "Insert",
  "Backspace",
  "Delete",
  "Kp0",
  "Kp1",
  "Kp2",
  "Kp3",
  "Kp4",
  "Kp5",
  "Kp6",
  "Kp7",
  "Kp8",
  "Kp9",
  "Kp_Period",
  "Kp_Divide",
  "Kp_Multiply",
  "Kp_Minus",
  "Kp_Plus",
  "Kp_Enter",
  "Kp_Equals",
  "F1",
  "F2",
  "F3",
  "F4",
  "F5",
  "F6",
  "F7",
  "F8",
  "F9",
  "F10",
  "F11",
  "F12",
  "Help",
  "Print",
  "Sysreq",
  "Break",
  "Menu",
  "Power",
  "Euro",
  "Undo",
  "Left Mouse",
  "Middle Mouse",
  "Right Mouse",
  "Wheel Up",
  "Wheel Down",
  "Mouse Button 6",
  "Mouse Button 7",
  "Mouse Button 8",
  "Mouse Button 9",
  "Mouse Button 10",
  "Joystick Button 1",
  "Joystick Button 2",
  "Joystick Button 3",
  "Joystick Button 4",
  "Joystick Button 5",
  "Joystick Button 6",
  "Joystick Button 7",
  "Joystick Button 8",
  "Joystick Button 9",
  "Joystick Button 10",
  "LastButton"  // should always be last item listed
};
const char*		KeyManager::asciiNames[][2] = {
  { "Tab",		"\t" },
  { "Backspace",	"\b" },
  { "Enter",		"\r" },
  { "Space",		" "  }
};

KeyManager::KeyManager()
{
  unsigned int i;

  // prep string to key map
  BzfKeyEvent key;
  key.ascii  = 0;
  key.shift  = 0;
  for (i = BzfKeyEvent::Pause; i < BzfKeyEvent::LastButton; ++i) {
    key.button = static_cast<BzfKeyEvent::Button>(i);
    stringToEvent.insert(std::make_pair(std::string(buttonNames[i]), key));
  }
  key.button = BzfKeyEvent::NoButton;
  for (i = 0; i < countof(asciiNames); ++i) {
    key.ascii = asciiNames[i][1][0];
    stringToEvent.insert(std::make_pair(std::string(asciiNames[i][0]), key));
  }
  char buffer[2];
  buffer[1] = 0;
  for (i = 0x21; i < 0x7f; ++i) {
    buffer[0] = key.ascii = static_cast<char>(i);
    stringToEvent.insert(std::make_pair(std::string(buffer), key));
  }
}

KeyManager::~KeyManager()
{
}

void			KeyManager::bind(const BzfKeyEvent& key,
					 bool press, const std::string& cmd)
{
  if (press) {
    pressEventToCommand.erase(key);
    pressEventToCommand.insert(std::make_pair(key, cmd));
  } else {
    releaseEventToCommand.erase(key);
    releaseEventToCommand.insert(std::make_pair(key, cmd));
  }
  notify(key, press, cmd);
}

void			KeyManager::unbind(const BzfKeyEvent& key,
					   bool press)
{
  if (press)
    pressEventToCommand.erase(key);
  else
    releaseEventToCommand.erase(key);
  notify(key, press, "");
}

void			KeyManager::unbindCommand(const char* command)
{
  EventToCommandMap::iterator index;
  EventToCommandMap::iterator deleteme;

  index = pressEventToCommand.begin();
  while (index != pressEventToCommand.end()) {
    if (index->second == command) {
      deleteme = index;
      index++;
      unbind(deleteme->first, true);
    } else {
      index++;
    }
  }

  index = releaseEventToCommand.begin();
  while (index != releaseEventToCommand.end()) {
    if (index->second == command) {
      deleteme = index;
      index++;
      unbind(deleteme->first, false);
    } else {
      index++;
    }
  }
}

std::string		KeyManager::get(const BzfKeyEvent& key,
					bool press) const
{
  const EventToCommandMap* map = press ? &pressEventToCommand :
    &releaseEventToCommand;
  EventToCommandMap::const_iterator index = map->find(key);
  if (index == map->end())
    return "";
  else
    return index->second;
}

std::vector<std::string> KeyManager::getKeysFromCommand(std::string command, bool press) const
{
  std::vector<std::string> keys;
  EventToCommandMap::const_iterator index;
  if (press) {
    for (index = pressEventToCommand.begin(); index != pressEventToCommand.end(); ++index) {
      if (index->second == command) {
	keys.push_back(this->keyEventToString(index->first));
      }
    }
  } else {
    for (index = releaseEventToCommand.begin(); index != releaseEventToCommand.end(); ++index) {
      if (index->second == command) {
	keys.push_back(this->keyEventToString(index->first));
      }
    }
  }
  return keys;
}

std::string		KeyManager::keyEventToString(
					const BzfKeyEvent& key) const
{
  std::string name;
  if (key.shift & BzfKeyEvent::ShiftKey)
    name += "Shift+";
  if (key.shift & BzfKeyEvent::ControlKey)
    name += "Ctrl+";
  if (key.shift & BzfKeyEvent::AltKey)
    name += "Alt+";
  switch (key.ascii) {
    case 0:
      return name + buttonNames[key.button];
    case '\b':
      return name + "Backspace";
    case '\t':
      return name + "Tab";
    case '\r':
      return name + "Enter";
    case ' ':
      return name + "Space";
    default:
      if (!isspace((unsigned char)key.ascii))
	return name + std::string(&key.ascii, 1);
      return name + "???";
  }
}

bool			KeyManager::stringToKeyEvent(
				const std::string& name, BzfKeyEvent& key) const
{
  // find last + in name
  const char* shiftDelimiter = strrchr(name.c_str(), '+');

  // split name into shift part and key name part
  std::string shiftPart, keyPart;
  if (shiftDelimiter == NULL) {
    keyPart = name;
  } else {
    shiftPart  = "+";
    shiftPart += name.substr(0, shiftDelimiter - name.c_str() + 1);
    keyPart    = shiftDelimiter + 1;
  }

  // find key name
  StringToEventMap::const_iterator index = stringToEvent.find(keyPart);
  if (index == stringToEvent.end())
    return false;

  // get key event sans shift state
  key = index->second;

  // apply shift state
  if (strstr(shiftPart.c_str(), "+Shift+") != NULL ||
      strstr(shiftPart.c_str(), "+shift+") != NULL)
    key.shift |= BzfKeyEvent::ShiftKey;
  if (strstr(shiftPart.c_str(), "+Ctrl+") != NULL ||
      strstr(shiftPart.c_str(), "+ctrl+") != NULL)
    key.shift |= BzfKeyEvent::ControlKey;
  if (strstr(shiftPart.c_str(), "+Alt+") != NULL ||
      strstr(shiftPart.c_str(), "+alt+") != NULL)
    key.shift |= BzfKeyEvent::AltKey;

  // success
  return true;
}

void			KeyManager::iterate(
				IterateCallback callback, void* userData)
{
  assert(callback != NULL);

  EventToCommandMap::const_iterator index;
  for (index = pressEventToCommand.begin(); index != pressEventToCommand.end(); ++index)
    (*callback)(keyEventToString(index->first), true, index->second, userData);
  for (index = releaseEventToCommand.begin(); index != releaseEventToCommand.end(); ++index)
    (*callback)(keyEventToString(index->first), false, index->second, userData);
}

void			KeyManager::addCallback(
				ChangeCallback callback, void* userData)
{
  callbacks.add(callback, userData);
}

void			KeyManager::removeCallback(
				ChangeCallback callback, void* userData)
{
  callbacks.remove(callback, userData);
}

void			KeyManager::notify(
				const BzfKeyEvent& key,
				bool press, const std::string& cmd)
{
  CallbackInfo info;
  info.name  = keyEventToString(key);
  info.press = press;
  info.cmd   = cmd;
  callbacks.iterate(&onCallback, &info);
}

bool			KeyManager::onCallback(
				ChangeCallback callback,
				void* userData,
				void* vinfo)
{
  CallbackInfo* info = reinterpret_cast<CallbackInfo*>(vinfo);
  callback(info->name, info->press, info->cmd, userData);
  return true;
}

bool			KeyManager::KeyEventLess::operator()(
				const BzfKeyEvent& a,
				const BzfKeyEvent& b) const
{
  if (a.ascii == 0 && b.ascii == 0) {
    if (a.button < b.button)
      return true;
    if (a.button > b.button)
      return false;

    // check shift
    if (a.shift < b.shift)
      return true;
  } else if (a.ascii == 0 && b.ascii != 0) {
    return true;
  } else if (a.ascii != 0 && b.ascii == 0) {
    return false;
  } else {
    if (toupper(a.ascii) < toupper(b.ascii))
      return true;
    if (toupper(a.ascii) > toupper(b.ascii))
      return false;

    // check shift state without shift key
    if ((a.shift & ~BzfKeyEvent::ShiftKey) < (b.shift & ~BzfKeyEvent::ShiftKey))
      return true;
  }

  return false;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8

<|endoftext|>


<commit_before>#pragma once

#include <mutex>
#include <deque>
#include <functional>
#include <memory>

#include <boost/signals2.hpp>
#include <boost/asio.hpp>

#include "linebuffer.hpp"

namespace util
{
	//! Specifies that util::connection should use a single read handler
	struct connection_single_handler_tag {};

	//! Specifies that util::connection should use a read signaller.
	struct connection_multiple_handler_tag {};

	template <class T>
	struct connection_traits;

	template <>
	struct connection_traits<connection_single_handler_tag>
	{
		typedef connection_single_handler_tag tag;
		typedef connection_traits<tag> type;

		typedef std::function<void (const std::string&)> handler_type;

		typedef void connection_return;

		template <class Callable>
		static connection_return connect(handler_type& handler, Callable&& f)
		{
			handler = handler_type(std::forward<Callable>(f));
		}
	};

	template <>
	struct connection_traits<connection_multiple_handler_tag>
	{
		typedef connection_multiple_handler_tag tag;
		typedef connection_traits<tag> type;

		typedef boost::signals2::signal<void (const std::string&)> handler_type;

		typedef boost::signals2::connection connection_return;

		template <class Callable>
		static connection_return connect(handler_type& handler, Callable&& f)
		{
			return handler.connect(f);
		}
	};

	//! Connection handler
	/*!
	 *  This class handles a connection to a client from the server. It can
	 *  only be created as a std::shared_ptr -- it's a shared object. When it
	 *  destructs, it closes the socket it owns, cleaning up the connection
	 *  automatically.
	 */
	template <class Socket, class HandlerTag, class HandlerTraits = connection_traits<HandlerTag>>
	class connection : public std::enable_shared_from_this<connection<Socket, HandlerTag>>
	{
	public:
		//! The socket type used by this clas
		typedef Socket socket_type;

		//! Handler tag
		typedef HandlerTag handler_tag;

		//! Handler traits
		typedef HandlerTraits handler_traits;

		//! Convenience
		typedef connection<socket_type, handler_tag> type;

		//! The pointer type for this class.
		typedef std::shared_ptr<type> pointer;

	private:
		//! Construct the connection.
		/*!
		 *  This function is only accessable within the class, so we cannot
		 *  make instances of this class outside its members. See create() for
		 *  how instances can be publicly created.
		 *  \param socket A reference to the socket for which this class is
		 *  built around. This socket is moved into the class.
		 */
		connection(socket_type socket)
			:socket_(std::move(socket)),
			writing_(false)
		{

		}
	public:

		//! Create a new instance of the class from an io_service.
		/*!
		 *  This is one of two overloads of this function, the only function
		 *  able to create new instances of this class.
		 *
		 *  It creates a new class (with a new socket) from the given
		 *  io_service, wrapping it in a shared_ptr and returning it.
		 *  \param io_service The io_service with which the connection's socket
		 *  should be registered.
		 *  \returns A new instance of the class, wrapped in a std::shared_ptr
		 */
		static pointer create(boost::asio::io_service& io_service)
		{
			return create(socket_type(io_service));
		}

		//! Create a new instance of the class from a socket
		/*!
		 *  This is one of the two overloads of this function, the only
		 *  function able to create new instances of this class.
		 *
		 *  It creates a new class to manage the given socket.
		 *
		 *  \param socket The socket to manage.
		 *  \returns A new instance of the class, wrapped in a std::shared_ptr
		 */
		template <class T>
		static pointer create(T&& socket)
		{
			auto ptr = pointer(new type(std::move(socket)));
			ptr->setup_read();
			return ptr;
		}

		//! Return the socket managed by this class.
		//! \return The socket managed by this class.
		socket_type& socket()
		{
			return socket_;
		}

		//! \overload
		const socket_type& socket() const
		{
			return socket_;
		}

		//! Add a message to the write queue.
		/*!
		 *  This function adds a message to the queue to be asynchronously sent
		 *  when the socket is available for writing. Thread safe.
		 *
		 *  \param msg The message to add to the message queue.
		 */
		void queue_write(const std::string& msg)
		{
			{
				std::lock_guard<std::mutex> guard(wq_mutex_);
				write_queue_.push_back(msg);
			}//cleanup lock

			setup_write();
		}

		//! Register a callback for a read event.
		/*!
		 *  This function registers a callback to receive the strings read
		 *  asynchronously from the managed socket.
		 *  \param f The callable object to call when a string is received --
		 *  must be able to take a string.
		 *
		 *  \returns An object that can be used for deregistering the callback.
		 */
		template <class Callable>
		typename handler_traits::connection_return connect_read(Callable&& f)
		{
			return handler_traits::connect(read_handler_, std::forward<Callable>(f));
		}

	protected:
		//! The socket managed by this class.
		socket_type socket_;

		//! The message queue to be placed on the socket.
		std::deque<std::string> write_queue_;

		//! Mutex protecting write_queue_ and writing_
		std::mutex wq_mutex_;

		//! For handling the write queue
		bool writing_;

		//! Handles the read callbacks.
		typename handler_traits::handler_type read_handler_;

		typedef util::line_buffer<std::array<char, 512>> line_buffer_type;
		//! Forms complete lines from buffers.
		line_buffer_type lb;

		//! Setup a read
		void setup_read()
		{
			auto buf = std::make_shared<line_buffer_type::buffer_type>();

			//Compiler won't look for shared_from_this in this class unless we tell it to
			auto shared(this->shared_from_this());

			socket_.async_receive(boost::asio::buffer(*buf),
					[this, shared, buf](const boost::system::error_code& ec, std::size_t bytes_tx)
					{
						if(ec)
							throw boost::system::system_error(ec);

						auto lines = lb(*buf, bytes_tx);
						for(const std::string& line : lines)
							read_handler_(line);

						setup_read();
					});
		}

		//! Setup a write
		void setup_write()
		{
			std::lock_guard<std::mutex> guard(wq_mutex_);

			if(!writing_ && !write_queue_.empty())
			{
				writing_ = true;

				auto msg = std::make_shared<std::string>(write_queue_.front());
				write_queue_.pop_front();

				//Compiler won't look for shared_from_this in this class unless we tell it to
				auto shared(this->shared_from_this());

				boost::asio::async_write(socket_, boost::asio::buffer(*msg),
						//Capture shared & msg to ensure lifetime
						[this, shared, msg](const boost::system::error_code& ec, std::size_t bytes_tx)
						{
							if(ec)
								throw boost::system::system_error(ec);

							wq_mutex_.lock();
							writing_ = false;
							wq_mutex_.unlock();

							setup_write();
						});
			}
		}

	};
}
<commit_msg>Extended connection to have close notification handlers.<commit_after>#pragma once

#include <mutex>
#include <deque>
#include <functional>
#include <memory>

#include <boost/signals2.hpp>
#include <boost/asio.hpp>

#include "linebuffer.hpp"

namespace util
{
	//! Specifies that util::connection should use a single read handler
	struct connection_single_handler_tag {};

	//! Specifies that util::connection should use a read signaller.
	struct connection_multiple_handler_tag {};

	template <class T>
	struct connection_traits;

	template <>
	struct connection_traits<connection_single_handler_tag>
	{
		typedef connection_single_handler_tag tag;
		typedef connection_traits<tag> type;

		template <typename function_type>
		struct handler_type
		{
			typedef std::function<function_type> type;
		};

		typedef void connection_return;

		template <class F>
		struct build_connect;

		template <class R, class... Args>
		struct build_connect<R(Args...)>
		{
			typedef typename handler_type<R(Args...)>::type object_type;

			template <class Callable>
			static connection_return connect(object_type& handler, Callable&& f)
			{
				handler = object_type(std::forward<Callable>(f));
			}
		};
	};

	template <>
	struct connection_traits<connection_multiple_handler_tag>
	{
		typedef connection_multiple_handler_tag tag;
		typedef connection_traits<tag> type;

		template <typename function_type>
		struct handler_type
		{
			typedef boost::signals2::signal<function_type> type;
		};

		typedef boost::signals2::connection connection_return;

		template <class F>
		struct build_connect;

		template <class R, class... Args>
		struct build_connect<R(Args...)>
		{
			typedef typename handler_type<R(Args...)>::type object_type;

			template <class Callable>
			static connection_return connect(object_type& handler, Callable&& f)
			{
				return handler.connect(std::forward<Callable>(f));
			}
		};
	};

	//! Connection handler
	/*!
	 *  This class handles a connection to a client from the server. It can
	 *  only be created as a std::shared_ptr -- it's a shared object. When it
	 *  destructs, it closes the socket it owns, cleaning up the connection
	 *  automatically.
	 */
	template <class Socket, class HandlerTag, class HandlerTraits = connection_traits<HandlerTag>>
	class connection : public std::enable_shared_from_this<connection<Socket, HandlerTag>>
	{
	public:
		//! The socket type used by this clas
		typedef Socket socket_type;

		//! Handler tag
		typedef HandlerTag handler_tag;

		//! Handler traits
		typedef HandlerTraits handler_traits;

		//! Convenience
		typedef connection<socket_type, handler_tag> type;

		//! The pointer type for this class.
		typedef std::shared_ptr<type> pointer;

	private:
		//! Construct the connection.
		/*!
		 *  This function is only accessable within the class, so we cannot
		 *  make instances of this class outside its members. See create() for
		 *  how instances can be publicly created.
		 *  \param socket A reference to the socket for which this class is
		 *  built around. This socket is moved into the class.
		 */
		connection(socket_type socket)
			:socket_(std::move(socket)),
			writing_(false)
		{

		}
	public:

		//! Create a new instance of the class from an io_service.
		/*!
		 *  This is one of two overloads of this function, the only function
		 *  able to create new instances of this class.
		 *
		 *  It creates a new class (with a new socket) from the given
		 *  io_service, wrapping it in a shared_ptr and returning it.
		 *  \param io_service The io_service with which the connection's socket
		 *  should be registered.
		 *  \returns A new instance of the class, wrapped in a std::shared_ptr
		 */
		static pointer create(boost::asio::io_service& io_service)
		{
			return create(socket_type(io_service));
		}

		//! Create a new instance of the class from a socket
		/*!
		 *  This is one of the two overloads of this function, the only
		 *  function able to create new instances of this class.
		 *
		 *  It creates a new class to manage the given socket.
		 *
		 *  \param socket The socket to manage.
		 *  \returns A new instance of the class, wrapped in a std::shared_ptr
		 */
		template <class T>
		static pointer create(T&& socket)
		{
			auto ptr = pointer(new type(std::move(socket)));
			ptr->setup_read();
			return ptr;
		}

		//! Return the socket managed by this class.
		//! \return The socket managed by this class.
		socket_type& socket()
		{
			return socket_;
		}

		//! \overload
		const socket_type& socket() const
		{
			return socket_;
		}

		//! Add a message to the write queue.
		/*!
		 *  This function adds a message to the queue to be asynchronously sent
		 *  when the socket is available for writing. Thread safe.
		 *
		 *  \param msg The message to add to the message queue.
		 */
		void queue_write(const std::string& msg)
		{
			{
				std::lock_guard<std::mutex> guard(wq_mutex_);
				write_queue_.push_back(msg);
			}//cleanup lock

			setup_write();
		}

		//! Register a callback for a read event.
		/*!
		 *  This function registers a callback to receive the strings read
		 *  asynchronously from the managed socket.
		 *  \param f The callable object to call when a string is received --
		 *  must be able to take a string.
		 *
		 *  \returns An object that can be used for deregistering the callback.
		 */
		template <class Callable>
		typename handler_traits::connection_return connect_read(Callable&& f)
		{
			typedef typename handler_traits::template build_connect<void(const std::string&)> build_connect_type;
			return build_connect_type::connect(read_handler_, std::forward<Callable>(f));
		}

		//! Register a callback for a close event
		template <class Callable>
		typename handler_traits::connection_return connect_close(Callable&& f)
		{
			typedef typename handler_traits::template build_connect<void(void)> build_connect_type;
			return build_connect_type::connect(close_handler_, std::forward<Callable>(f));
		}

		//! Reports the open/closed status of the internal socket
		bool is_open() const
		{
			return socket_.is_open();
		}

	protected:
		//! The socket managed by this class.
		socket_type socket_;

		//! The message queue to be placed on the socket.
		std::deque<std::string> write_queue_;

		//! Mutex protecting write_queue_ and writing_
		std::mutex wq_mutex_;

		//! For handling the write queue
		bool writing_;

		//! Handles the read callbacks.
		typename handler_traits::template handler_type<void (const std::string&)>::type read_handler_;

		//! Handles the close callbacks.
		typename handler_traits::template handler_type<void (void)>::type close_handler_;

		typedef util::line_buffer<std::array<char, 512>> line_buffer_type;
		//! Forms complete lines from buffers.
		line_buffer_type lb;

		//! Handle error codes given by the asio library
		/*!
		 *  This function is responsible for throwing true errors & cleanly
		 *  shutting the socket down when it received an end of file
		 *
		 *  \returns True if the calling handler is allowed to continue; false o/w
		 */
		bool handle_error(const boost::system::error_code& ec)
		{
			if(ec)
			{
				if(ec != boost::asio::error::eof)
					throw boost::system::system_error(ec);
				else
				{
					auto shared(this->shared_from_this());

					//Can't execute close in any of the handlers that call this function
					socket_.get_io_service().post(
							[this, shared]()
							{
								socket_.close();
								close_handler_();
							});

					return false;
				}
			}

			return true;
		}

		//! Setup a read
		void setup_read()
		{
			auto buf = std::make_shared<line_buffer_type::buffer_type>();

			//Compiler won't look for shared_from_this in this class unless we tell it to
			auto shared(this->shared_from_this());

			socket_.async_receive(boost::asio::buffer(*buf),
					[this, shared, buf](const boost::system::error_code& ec, std::size_t bytes_tx)
					{
						bool go = handle_error(ec);

						if(go)
						{
							auto lines = lb(*buf, bytes_tx);
							for(const std::string& line : lines)
								read_handler_(line);

							if(is_open())
								setup_read();
						}
					});
		}

		//! Setup a write
		void setup_write()
		{
			std::lock_guard<std::mutex> guard(wq_mutex_);

			if(!writing_ && !write_queue_.empty())
			{
				writing_ = true;

				auto msg = std::make_shared<std::string>(write_queue_.front());
				write_queue_.pop_front();

				//Compiler won't look for shared_from_this in this class unless we tell it to
				auto shared(this->shared_from_this());

				boost::asio::async_write(socket_, boost::asio::buffer(*msg),
						//Capture shared & msg to ensure lifetime
						[this, shared, msg](const boost::system::error_code& ec, std::size_t bytes_tx)
						{
							bool go = handle_error(ec);

							if(go)
							{
								wq_mutex_.lock();
								writing_ = false;
								wq_mutex_.unlock();

								if(is_open())
									setup_write();
							}
						});
			}
		}

	};
}
<|endoftext|>


<commit_before>/* description.cc
   Mathieu Stefani, 24 février 2016

   Implementation of the description system
*/

#include <sstream>
#include <algorithm>

#include <pistache/description.h>
#include <pistache/http_header.h>

namespace Pistache {
namespace Rest {

const char* schemeString(Scheme scheme) {
    switch (scheme) {
#define SCHEME(e, str) \
        case Scheme::e: \
            return str;
        SCHEMES
#undef SCHEME
    }

    return nullptr;
}

namespace Schema {

Contact::Contact(
        std::string name, std::string url, std::string email)
    : name(std::move(name))
    , url(std::move(url))
    , email(std::move(email))
{ }

License::License(
        std::string name, std::string url)
    : name(std::move(name))
    , url(std::move(url))
{ }

Info::Info(
        std::string title, std::string version, std::string description)
    : title(std::move(title))
    , version(std::move(version))
    , description(std::move(description))
{ }

PathDecl::PathDecl(
        std::string value, Http::Method method)
    : value(std::move(value))
    , method(method)
{ }

Path::Path(
        std::string value, Http::Method method, std::string description)
    : value(std::move(value))
    , method(method)
    , description(std::move(description))
    , hidden(false)
{ }

std::string
Path::swaggerFormat(const std::string& path) {
    if (path.empty()) return "";
    if (path[0] != '/') throw std::invalid_argument("Invalid path, should start with a '/'");

    /* @CodeDup: re-use the private Fragment class of Router */
    auto isPositional = [](const std::string& fragment) {
        if (fragment[0] == ':')
            return std::make_pair(true, fragment.substr(1));
        return std::make_pair(false, std::string());
    };

    auto isOptional = [](const std::string& fragment) {
        auto pos = fragment.find('?');
        // @Validation the '?' should be the last character
        return std::make_pair(pos != std::string::npos, pos);
    };

    std::ostringstream oss;

    auto processFragment = [&](std::string fragment) {
        auto optional = isOptional(fragment);
        if (optional.first)
            fragment.erase(optional.second, 1);

        auto positional = isPositional(fragment);
        if (positional.first) {
            oss << '{' << positional.second << '}';
        } else {
            oss << fragment;
        }
    };

    std::istringstream iss(path);
    std::string fragment;

    std::vector<std::string> fragments;

    while (std::getline(iss, fragment, '/')) {
        fragments.push_back(std::move(fragment));
    }

    for (size_t i = 0; i < fragments.size() - 1; ++i) {
        const auto& frag = fragments[i];

        processFragment(frag);
        oss << '/';
    }

    const auto& last = fragments.back();
    if (last.empty()) oss << '/';
    else {
        processFragment(last);
    }

    return oss.str();
}

bool
PathGroup::hasPath(const std::string& name, Http::Method method) const {
    auto group = paths(name);
    auto it = std::find_if(std::begin(group), std::end(group), [&](const Path& p) {
        return p.method == method;
    });
    return it != std::end(group);
}

bool
PathGroup::hasPath(const Path& path) const {
    return hasPath(path.value, path.method);
}

PathGroup::Group
PathGroup::paths(const std::string& name) const {
    auto it = groups_.find(name);
    if (it == std::end(groups_))
        return PathGroup::Group { };

    return it->second;
}

Optional<Path>
PathGroup::path(const std::string& name, Http::Method method) const {
    auto group = paths(name);
    auto it = std::find_if(std::begin(group), std::end(group), [&](const Path& p) {
        return p.method == method;
    });

    if (it != std::end(group)) {
        return Some(*it);
    }
    return None();
}

PathGroup::group_iterator
PathGroup::add(Path path) {
    if (hasPath(path))
        return PathGroup::group_iterator { };

    auto &group = groups_[path.value];
    return group.insert(group.end(), std::move(path));
}

PathGroup::const_iterator
PathGroup::begin() const {
    return groups_.begin();
}

PathGroup::const_iterator
PathGroup::end() const {
    return groups_.end();
}

PathGroup::flat_iterator
PathGroup::flatBegin() const {
    return makeFlatMapIterator(groups_, begin());
}

PathGroup::flat_iterator
PathGroup::flatEnd()  const {
    return makeFlatMapIterator(groups_, end());
}

PathBuilder::PathBuilder(Path* path)
    : path_(path)
{ }

SubPath::SubPath(
        std::string prefix, PathGroup* paths)
    : prefix(std::move(prefix))
   , paths(paths)
{ }

PathBuilder
SubPath::route(std::string name, Http::Method method, std::string description) {
    auto fullPath = prefix + name;
    Path path(std::move(fullPath), method, std::move(description));
    std::copy(std::begin(parameters), std::end(parameters), std::back_inserter(path.parameters));

    auto it = paths->add(std::move(path));

    return PathBuilder(&*it);
}

PathBuilder
SubPath::route(PathDecl fragment, std::string description) {
    return route(std::move(fragment.value), fragment.method, std::move(description));
}

SubPath
SubPath::path(std::string prefix) {
    return SubPath(this->prefix + prefix, paths);
}

Parameter::Parameter(
        std::string name, std::string description)
    : name(std::move(name))
    , description(std::move(description))
    , required(true)
{ }

Response::Response(
        Http::Code statusCode, std::string description)
    : statusCode(statusCode)
    , description(std::move(description))
{ }

ResponseBuilder::ResponseBuilder(
        Http::Code statusCode, std::string description)
    : response_(statusCode, std::move(description))
{ }

InfoBuilder::InfoBuilder(Info* info)
    : info_(info)
{ }

InfoBuilder&
InfoBuilder::termsOfService(std::string value) {
    info_->termsOfService = std::move(value);
    return *this;
}

InfoBuilder&
InfoBuilder::contact(std::string name, std::string url, std::string email) {
    info_->contact = Some(Contact(std::move(name), std::move(url), std::move(email)));
    return *this;
}

InfoBuilder&
InfoBuilder::license(std::string name, std::string url) {
    info_->license = Some(License(std::move(name), std::move(url)));
    return *this;
}

} // namespace Schema

Description::Description(
        std::string title, std::string version, std::string description)
    : info_(std::move(title), std::move(version), std::move(description))
{
}

Schema::InfoBuilder
Description::info() {
    Schema::InfoBuilder builder(&info_);
    return builder;
}

Description&
Description::host(std::string value) {
    host_ = std::move(value);
    return *this;
}

Description&
Description::basePath(std::string value) {
    basePath_ = std::move(value);
    return *this;
}

Schema::PathDecl
Description::get(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Get);
}

Schema::PathDecl
Description::post(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Post);
}

Schema::PathDecl
Description::put(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Put);
}

Schema::PathDecl
Description::del(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Delete);
}

Schema::SubPath
Description::path(std::string name) {
    return Schema::SubPath(std::move(name), &paths_);
}

Schema::PathBuilder
Description::route(std::string name, Http::Method method, std::string description) {
    auto it = paths_.emplace(std::move(name), method, std::move(description));
    return Schema::PathBuilder(&*it);
}

Schema::PathBuilder
Description::route(Schema::PathDecl fragment, std::string description) {
    return route(std::move(fragment.value), fragment.method, std::move(description));
}

Schema::ResponseBuilder
Description::response(Http::Code statusCode, std::string description) {
    Schema::ResponseBuilder builder(statusCode, std::move(description));
    return builder;
}

Swagger&
Swagger::uiPath(std::string path) {
    uiPath_ = std::move(path);
    return *this;
}

Swagger&
Swagger::uiDirectory(std::string dir) {
    uiDirectory_ = std::move(dir);
    return *this;
}

Swagger&
Swagger::apiPath(std::string path) {
    apiPath_ = std::move(path);
    return *this;
}

Swagger&
Swagger::serializer(Swagger::Serializer serialize) {
    serializer_ = std::move(serialize);
    return *this;
}

void
Swagger::install(Rest::Router& router) {

    Route::Handler uiHandler = [=](const Rest::Request& req, Http::ResponseWriter response) {
        auto res = req.resource();

        /*
         * @Export might be useful for routing also. Make it public or merge it with the Fragment class
         */

        struct Path {
            Path(const std::string& value)
                : value(value)
                , trailingSlashValue(value)
            {
                if (trailingSlashValue.back() != '/')
                    trailingSlashValue += '/';
            }

            static bool hasTrailingSlash(const Rest::Request& req) {
                auto res = req.resource();
                return res.back() == '/';
            }

            std::string stripPrefix(const Rest::Request& req) {
                auto res = req.resource();
                if (!res.compare(0, value.size(), value)) {
                    return res.substr(value.size());
                }
                return res;
            }

            bool matches(const Rest::Request& req) {
                auto res = req.resource();
                if (value == res)
                    return true;

                if (res == trailingSlashValue)
                    return true;

                return false;
            }

            bool isPrefix(const Rest::Request& req) {
                auto res = req.resource();
                return !res.compare(0, value.size(), value);
            }

            const std::string& withTrailingSlash() const {
                return trailingSlashValue;
            }

            std::string join(const std::string& value) const {
                std::string val;
                if (value[0] == '/') val = value.substr(1);
                else val = value;
                return trailingSlashValue + val;
            }

        private:
            std::string value;
            std::string trailingSlashValue;
        };

        Path ui(uiPath_);
        Path uiDir(uiDirectory_);

        if (ui.matches(req)) {
            if (!Path::hasTrailingSlash(req)) {
                response
                    .headers()
                    .add<Http::Header::Location>(uiPath_ + '/');

                response.send(Http::Code::Moved_Permanently);
            } else {
                auto index = uiDir.join("index.html");
                Http::serveFile(response, index.c_str());
            }
            return Route::Result::Ok;
        }
        else if (ui.isPrefix(req)) {
            auto file = ui.stripPrefix(req);
            auto path = uiDir.join(file);
            Http::serveFile(response, path.c_str());
            return Route::Result::Ok;
        }

        else if (res == apiPath_) {
            response.send(Http::Code::Ok, serializer_(description_), MIME(Application, Json));
            return Route::Result::Ok;
        }

        return Route::Result::Failure;
    };

    router.addCustomHandler(uiHandler);
}

} // namespace Rest
} // namespace Pistache
<commit_msg>Fixed potential empty path in Swagger description<commit_after>/* description.cc
   Mathieu Stefani, 24 février 2016

   Implementation of the description system
*/

#include <sstream>
#include <algorithm>

#include <pistache/description.h>
#include <pistache/http_header.h>

namespace Pistache {
namespace Rest {

const char* schemeString(Scheme scheme) {
    switch (scheme) {
#define SCHEME(e, str) \
        case Scheme::e: \
            return str;
        SCHEMES
#undef SCHEME
    }

    return nullptr;
}

namespace Schema {

Contact::Contact(
        std::string name, std::string url, std::string email)
    : name(std::move(name))
    , url(std::move(url))
    , email(std::move(email))
{ }

License::License(
        std::string name, std::string url)
    : name(std::move(name))
    , url(std::move(url))
{ }

Info::Info(
        std::string title, std::string version, std::string description)
    : title(std::move(title))
    , version(std::move(version))
    , description(std::move(description))
{ }

PathDecl::PathDecl(
        std::string value, Http::Method method)
    : value(std::move(value))
    , method(method)
{ }

Path::Path(
        std::string value, Http::Method method, std::string description)
    : value(std::move(value))
    , method(method)
    , description(std::move(description))
    , hidden(false)
{ }

std::string
Path::swaggerFormat(const std::string& path) {
    if (path.empty()) return "";
    if (path[0] != '/') throw std::invalid_argument("Invalid path, should start with a '/'");

    /* @CodeDup: re-use the private Fragment class of Router */
    auto isPositional = [](const std::string& fragment) {
        if (fragment[0] == ':')
            return std::make_pair(true, fragment.substr(1));
        return std::make_pair(false, std::string());
    };

    auto isOptional = [](const std::string& fragment) {
        auto pos = fragment.find('?');
        // @Validation the '?' should be the last character
        return std::make_pair(pos != std::string::npos, pos);
    };

    std::ostringstream oss;

    auto processFragment = [&](std::string fragment) {
        auto optional = isOptional(fragment);
        if (optional.first)
            fragment.erase(optional.second, 1);

        auto positional = isPositional(fragment);
        if (positional.first) {
            oss << '{' << positional.second << '}';
        } else {
            oss << fragment;
        }
    };

    std::istringstream iss(path);
    std::string fragment;

    std::vector<std::string> fragments;

    while (std::getline(iss, fragment, '/')) {
        fragments.push_back(std::move(fragment));
    }

    for (size_t i = 0; i < fragments.size() - 1; ++i) {
        const auto& frag = fragments[i];

        processFragment(frag);
        oss << '/';
    }

    const auto& last = fragments.back();
    if (last.empty()) oss << '/';
    else {
        processFragment(last);
    }

    return oss.str();
}

bool
PathGroup::hasPath(const std::string& name, Http::Method method) const {
    auto group = paths(name);
    auto it = std::find_if(std::begin(group), std::end(group), [&](const Path& p) {
        return p.method == method;
    });
    return it != std::end(group);
}

bool
PathGroup::hasPath(const Path& path) const {
    return hasPath(path.value, path.method);
}

PathGroup::Group
PathGroup::paths(const std::string& name) const {
    auto it = groups_.find(name);
    if (it == std::end(groups_))
        return PathGroup::Group { };

    return it->second;
}

Optional<Path>
PathGroup::path(const std::string& name, Http::Method method) const {
    auto group = paths(name);
    auto it = std::find_if(std::begin(group), std::end(group), [&](const Path& p) {
        return p.method == method;
    });

    if (it != std::end(group)) {
        return Some(*it);
    }
    return None();
}

PathGroup::group_iterator
PathGroup::add(Path path) {
    if (hasPath(path))
        return PathGroup::group_iterator { };

    auto &group = groups_[path.value];
    return group.insert(group.end(), std::move(path));
}

PathGroup::const_iterator
PathGroup::begin() const {
    return groups_.begin();
}

PathGroup::const_iterator
PathGroup::end() const {
    return groups_.end();
}

PathGroup::flat_iterator
PathGroup::flatBegin() const {
    return makeFlatMapIterator(groups_, begin());
}

PathGroup::flat_iterator
PathGroup::flatEnd()  const {
    return makeFlatMapIterator(groups_, end());
}

PathBuilder::PathBuilder(Path* path)
    : path_(path)
{ }

SubPath::SubPath(
        std::string prefix, PathGroup* paths)
    : prefix(std::move(prefix))
   , paths(paths)
{ }

PathBuilder
SubPath::route(std::string name, Http::Method method, std::string description) {
    auto fullPath = prefix + name;
    Path path(std::move(fullPath), method, std::move(description));
    std::copy(std::begin(parameters), std::end(parameters), std::back_inserter(path.parameters));

    auto it = paths->add(std::move(path));

    return PathBuilder(&*it);
}

PathBuilder
SubPath::route(PathDecl fragment, std::string description) {
    return route(std::move(fragment.value), fragment.method, std::move(description));
}

SubPath
SubPath::path(std::string prefix) {
    return SubPath(this->prefix + prefix, paths);
}

Parameter::Parameter(
        std::string name, std::string description)
    : name(std::move(name))
    , description(std::move(description))
    , required(true)
{ }

Response::Response(
        Http::Code statusCode, std::string description)
    : statusCode(statusCode)
    , description(std::move(description))
{ }

ResponseBuilder::ResponseBuilder(
        Http::Code statusCode, std::string description)
    : response_(statusCode, std::move(description))
{ }

InfoBuilder::InfoBuilder(Info* info)
    : info_(info)
{ }

InfoBuilder&
InfoBuilder::termsOfService(std::string value) {
    info_->termsOfService = std::move(value);
    return *this;
}

InfoBuilder&
InfoBuilder::contact(std::string name, std::string url, std::string email) {
    info_->contact = Some(Contact(std::move(name), std::move(url), std::move(email)));
    return *this;
}

InfoBuilder&
InfoBuilder::license(std::string name, std::string url) {
    info_->license = Some(License(std::move(name), std::move(url)));
    return *this;
}

} // namespace Schema

Description::Description(
        std::string title, std::string version, std::string description)
    : info_(std::move(title), std::move(version), std::move(description))
{
}

Schema::InfoBuilder
Description::info() {
    Schema::InfoBuilder builder(&info_);
    return builder;
}

Description&
Description::host(std::string value) {
    host_ = std::move(value);
    return *this;
}

Description&
Description::basePath(std::string value) {
    basePath_ = std::move(value);
    return *this;
}

Schema::PathDecl
Description::get(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Get);
}

Schema::PathDecl
Description::post(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Post);
}

Schema::PathDecl
Description::put(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Put);
}

Schema::PathDecl
Description::del(std::string name) {
    return Schema::PathDecl(std::move(name), Http::Method::Delete);
}

Schema::SubPath
Description::path(std::string name) {
    return Schema::SubPath(std::move(name), &paths_);
}

Schema::PathBuilder
Description::route(std::string name, Http::Method method, std::string description) {
    auto it = paths_.emplace(std::move(name), method, std::move(description));
    return Schema::PathBuilder(&*it);
}

Schema::PathBuilder
Description::route(Schema::PathDecl fragment, std::string description) {
    return route(std::move(fragment.value), fragment.method, std::move(description));
}

Schema::ResponseBuilder
Description::response(Http::Code statusCode, std::string description) {
    Schema::ResponseBuilder builder(statusCode, std::move(description));
    return builder;
}

Swagger&
Swagger::uiPath(std::string path) {
    uiPath_ = std::move(path);
    return *this;
}

Swagger&
Swagger::uiDirectory(std::string dir) {
    uiDirectory_ = std::move(dir);
    return *this;
}

Swagger&
Swagger::apiPath(std::string path) {
    apiPath_ = std::move(path);
    return *this;
}

Swagger&
Swagger::serializer(Swagger::Serializer serialize) {
    serializer_ = std::move(serialize);
    return *this;
}

void
Swagger::install(Rest::Router& router) {

    Route::Handler uiHandler = [=](const Rest::Request& req, Http::ResponseWriter response) {
        auto res = req.resource();

        /*
         * @Export might be useful for routing also. Make it public or merge it with the Fragment class
         */

        struct Path {
            Path(const std::string& value)
                : value(value)
                , trailingSlashValue(value)
            {
                if (trailingSlashValue.empty() || trailingSlashValue.back() != '/')
                    trailingSlashValue += '/';
            }

            static bool hasTrailingSlash(const Rest::Request& req) {
                auto res = req.resource();
                return res.back() == '/';
            }

            std::string stripPrefix(const Rest::Request& req) {
                auto res = req.resource();
                if (!res.compare(0, value.size(), value)) {
                    return res.substr(value.size());
                }
                return res;
            }

            bool matches(const Rest::Request& req) {
                auto res = req.resource();
                if (value == res)
                    return true;

                if (res == trailingSlashValue)
                    return true;

                return false;
            }

            bool isPrefix(const Rest::Request& req) {
                auto res = req.resource();
                return !res.compare(0, value.size(), value);
            }

            const std::string& withTrailingSlash() const {
                return trailingSlashValue;
            }

            std::string join(const std::string& value) const {
                std::string val;
                if (value[0] == '/') val = value.substr(1);
                else val = value;
                return trailingSlashValue + val;
            }

        private:
            std::string value;
            std::string trailingSlashValue;
        };

        Path ui(uiPath_);
        Path uiDir(uiDirectory_);

        if (ui.matches(req)) {
            if (!Path::hasTrailingSlash(req)) {
                response
                    .headers()
                    .add<Http::Header::Location>(uiPath_ + '/');

                response.send(Http::Code::Moved_Permanently);
            } else {
                auto index = uiDir.join("index.html");
                Http::serveFile(response, index.c_str());
            }
            return Route::Result::Ok;
        }
        else if (ui.isPrefix(req)) {
            auto file = ui.stripPrefix(req);
            auto path = uiDir.join(file);
            Http::serveFile(response, path.c_str());
            return Route::Result::Ok;
        }

        else if (res == apiPath_) {
            response.send(Http::Code::Ok, serializer_(description_), MIME(Application, Json));
            return Route::Result::Ok;
        }

        return Route::Result::Failure;
    };

    router.addCustomHandler(uiHandler);
}

} // namespace Rest
} // namespace Pistache
<|endoftext|>


<commit_before>/*!
 *  \file File componentMovement.cpp
 *  \brief Implementation of the class of component movements present in the game
 *
 *  The class implemented here provides to the game the speed and type
 *  of movement that a given element can perform
 *
 *  \sa componentMovement.hpp
 */

#include <componentMovement.hpp>
#include <gameObject.hpp>
#include <game.hpp>

//#include <camera.hpp>
//#include <inputManager.hpp>

//! A constructor.
    /*!
    This is a constructor method of componentMovement class
    */
CompMovement::CompMovement(const Vec2& s,moveType t):mType{t},speed{s}{}

//! A destructor.
    /*!
      This is a destructor method of componentMovement class
    */

CompMovement::~CompMovement() {}

/*!
	@fn void CompMovement::Update(float time)
	@brief Method that update element movement
	@param time
	@return The execution of this method returns no value
	@warning Method that requires review of comment
*/

void CompMovement::Update(float time) {
	UNUSED(time);
	GO(entity)->pos+=move;
	if (mType==moveType::t_bullet)GO(entity)->rotation=speed.angle();
}

/*!
	@fn void CompMovement::Render()
	@brief Method that render the new element movement
	@return The execution of this method returns no value
	@warning Method that requires review of comment
*/

void CompMovement::Render() {}

/*!
	@fn Component::type CompMovement::GetType()const
	@brief Method that sets the type of movement
	@return Component class object
	@warning Method that requires review of comment
*/

Component::type CompMovement::GetType()const{
	return Component::type::t_movement;
}
<commit_msg>Second significant names application<commit_after>/*!
 *  \file File componentMovement.cpp
 *  \brief Implementation of the class of component movements present in the game
 *
 *  The class implemented here provides to the game the speed and type
 *  of movement that a given element can perform
 *
 *  \sa componentMovement.hpp
 */

#include <componentMovement.hpp>
#include <gameObject.hpp>
#include <game.hpp>

//#include <camera.hpp>
//#include <inputManager.hpp>

//! A constructor.
    /*!
    This is a constructor method of componentMovement class
    */
CompMovement::CompMovement(const Vec2& sprite,moveType movetype):mType{movetype},speed{s}{}

//! A destructor.
    /*!
      This is a destructor method of componentMovement class
    */

CompMovement::~CompMovement() {}

/*!
	@fn void CompMovement::Update(float time)
	@brief Method that update element movement
	@param time
	@return The execution of this method returns no value
	@warning Method that requires review of comment
*/

void CompMovement::Update(float time) {
	UNUSED(time);
	GO(entity)->position+=move;
	if (mType==moveType::type_bullet)GO(entity)->rotation=speed.angle();
}

/*!
	@fn void CompMovement::Render()
	@brief Method that render the new element movement
	@return The execution of this method returns no value
	@warning Method that requires review of comment
*/

void CompMovement::Render() {}

/*!
	@fn Component::type CompMovement::GetType()const
	@brief Method that sets the type of movement
	@return Component class object
	@warning Method that requires review of comment
*/

Component::type CompMovement::GetType()const{
	return Component::type::type_movement;
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * 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 <bitcoin/bitcoin/config/checkpoint.hpp>

#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <boost/regex.hpp>
#include <bitcoin/bitcoin/define.hpp>
#include <bitcoin/bitcoin/formats/base16.hpp>

namespace libbitcoin {
namespace config {

using namespace boost;
using namespace boost::program_options;

checkpoint::checkpoint()
  : height_(0), hash_(bc::null_hash)
{
}

checkpoint::checkpoint(const std::string& value)
  : checkpoint()
{
    std::stringstream(value) >> *this;
}

checkpoint::checkpoint(const checkpoint& other)
  : hash_(other.hash()), height_(other.height())
{
}

// This is intended for static initialization (i.e. of the internal defaults).
checkpoint::checkpoint(const std::string& hash, size_t height)
  : height_(height)
{
    if (!decode_hash(hash_, hash))
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(hash));
    }
}

checkpoint::checkpoint(const hash_digest& hash, size_t height)
    : hash_(hash), height_(height)
{
}

const hash_digest& checkpoint::hash() const
{
    return hash_;
}

const size_t checkpoint::height() const
{
    return height_;
}

std::string checkpoint::to_string() const
{
    std::stringstream value;
    value << *this;
    return value.str();
}

std::istream& operator>>(std::istream& input, checkpoint& argument)
{
    std::string value;
    input >> value;

    // std::regex requires gcc 4.9, so we are using boost::regex for now.
    // We allow 1-10 digits, which is sufficient for 2^32 blocks.
    static const regex regular("^([0-9a-f]{64})(:([0-9]{1,10}))?$");

    sregex_iterator it(value.begin(), value.end(), regular), end;
    if (it == end)
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(value));
    }

    const auto& match = *it;
    if (!decode_hash(argument.hash_, match[1]))
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(value));
    }

    try
    {
        argument.height_ = lexical_cast<size_t>(match[3]);
    }
    catch (const boost::exception&)
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(value));
    }

    return input;
}

std::ostream& operator<<(std::ostream& output, const checkpoint& argument)
{
    output << encode_hash(argument.hash()) << ":" << argument.height();
    return output;
}

} // namespace config
} // namespace libbitcoin
<commit_msg>Expand checkpoint height deserialization to size_t, style.<commit_after>/**
 * Copyright (c) 2011-2015 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * 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 <bitcoin/bitcoin/config/checkpoint.hpp>

#include <cstddef>
#include <iostream>
#include <regex>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <boost/regex.hpp>
#include <bitcoin/bitcoin/define.hpp>
#include <bitcoin/bitcoin/formats/base16.hpp>

namespace libbitcoin {
namespace config {

using namespace boost;
using namespace boost::program_options;

checkpoint::checkpoint()
  : height_(0), hash_(bc::null_hash)
{
}

checkpoint::checkpoint(const std::string& value)
  : checkpoint()
{
    std::stringstream(value) >> *this;
}

checkpoint::checkpoint(const checkpoint& other)
  : hash_(other.hash()), height_(other.height())
{
}

// This is intended for static initialization (i.e. of the internal defaults).
checkpoint::checkpoint(const std::string& hash, size_t height)
  : height_(height)
{
    if (!decode_hash(hash_, hash))
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(hash));
    }
}

checkpoint::checkpoint(const hash_digest& hash, size_t height)
  : hash_(hash), height_(height)
{
}

const hash_digest& checkpoint::hash() const
{
    return hash_;
}

const size_t checkpoint::height() const
{
    return height_;
}

std::string checkpoint::to_string() const
{
    std::stringstream value;
    value << *this;
    return value.str();
}

std::istream& operator>>(std::istream& input, checkpoint& argument)
{
    std::string value;
    input >> value;

    // std::regex requires gcc 4.9, so we are using boost::regex for now.
    static const regex regular("^([0-9a-f]{64})(:([0-9]{1,20}))?$");

    sregex_iterator it(value.begin(), value.end(), regular), end;
    if (it == end)
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(value));
    }

    const auto& match = *it;
    if (!decode_hash(argument.hash_, match[1]))
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(value));
    }

    try
    {
        argument.height_ = lexical_cast<size_t>(match[3]);
    }
    catch (const boost::exception&)
    {
        BOOST_THROW_EXCEPTION(invalid_option_value(value));
    }

    return input;
}

std::ostream& operator<<(std::ostream& output, const checkpoint& argument)
{
    output << encode_hash(argument.hash()) << ":" << argument.height();
    return output;
}

} // namespace config
} // namespace libbitcoin
<|endoftext|>


<commit_before>#include "Process.hpp"
#include "log.hpp"
#include "Filesystem.hpp"
#include "quote.hpp"

#include <boost/config.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>

#include <cassert>
#include <stdexcept>

#if defined(BOOST_POSIX_API)
# include <sys/wait.h>
# include <unistd.h>
# if defined(__APPLE__) && defined(__DYNAMIC__)
#  include <crt_externs.h> // For _NSGetEnviron()
# endif
#elif defined(BOOST_WINDOWS_API)
# include <Windows.h>
#else
# error "Unsupported platform"
#endif

namespace io = boost::iostreams;

namespace configure {

	namespace {

		char** get_environ()
		{
#if defined(__APPLE__) && defined(__DYNAMIC__)
			return *_NSGetEnviron();
#else
			return environ;
#endif
		}

#ifdef BOOST_WINDOWS_API
		typedef HANDLE file_descriptor_t;
#else
		typedef int file_descriptor_t;
#endif

		struct Pipe
		{
		private:
			io::file_descriptor_source _source;
			io::file_descriptor_sink _sink;

		public:
			Pipe()
			{
				file_descriptor_t fds[2];
#ifdef BOOST_WINDOWS_API
				if (!::CreatePipe(&fds[0], &fds[1], NULL, 0))
#else
				if (::pipe(fds) == -1)
#endif
					throw std::runtime_error("pipe error");
				_source = io::file_descriptor_source(
				  fds[0], io::file_descriptor_flags::close_handle);
				_sink = io::file_descriptor_sink(
				  fds[1], io::file_descriptor_flags::close_handle);
			}

		public:
			io::file_descriptor_sink& sink() { return _sink; }
			io::file_descriptor_source& source() { return _source; }

		public:
			void close()
			{
				_sink.close();
				_source.close();
			}
		};

#ifdef BOOST_WINDOWS_API
		struct Child
		{
		private:
			PROCESS_INFORMATION _proc_info;

		public:
			explicit Child(PROCESS_INFORMATION const& proc_info)
			    : _proc_info(proc_info)
			{}

			Child(Child&& other)
				: _proc_info(other._proc_info)
			{
				other._proc_info.hProcess = INVALID_HANDLE_VALUE;
				other._proc_info.hThread = INVALID_HANDLE_VALUE;
			}

			~Child()
			{
				::CloseHandle(_proc_info.hProcess);
				::CloseHandle(_proc_info.hThread);
			}

			HANDLE process_handle() const { return _proc_info.hProcess; }
		};
#else
		struct Child
		{
		private:
			pid_t _pid;

		public:
			explicit Child(pid_t pid) : _pid(pid) {}
			pid_t process_handle() const { return _pid; }
		};
#endif

		std::ostream& operator <<(std::ostream& out, Child const& child)
		{
			return out << "<Process " << child.process_handle() << ">";
		}

	} // !anonymous

	struct Process::Impl
	{
		Command const command;
		Options const options;
		boost::optional<ExitCode> exit_code;
		io::file_descriptor_sink stdin_sink;
		io::file_descriptor_source stdout_source;
		io::file_descriptor_source stderr_source;
		Child child;

		Impl(Command cmd, Options options)
			: command(_prepare_command(std::move(cmd)))
			, options(std::move(options))
			, exit_code(boost::none)
			, child(_create_child())
		{}

#ifdef BOOST_POSIX_API
		pid_t _create_child()
		{
			char** env = nullptr;
			// Working directory
			if (this->options.working_directory)
			{
				throw "not there";
			}

			if (this->options.inherit_env)
			{
				env = get_environ();
			}

			std::unique_ptr<Pipe> stdin_pipe;
			std::unique_ptr<Pipe> stdout_pipe;
			std::unique_ptr<Pipe> stderr_pipe;
			std::tuple<bool, Stream, std::unique_ptr<Pipe>&, int> channels[] = {
				std::make_tuple(false,
				                this->options.stdin_,
				                std::ref(stdin_pipe),
				                STDIN_FILENO),
				std::make_tuple(true,
				                this->options.stdout_,
				                std::ref(stdout_pipe),
				                STDOUT_FILENO),
				std::make_tuple(true,
				                this->options.stderr_,
				                std::ref(stderr_pipe),
				                STDERR_FILENO),
			};

			for (auto& channel: channels)
				if (std::get<1>(channel) == Stream::PIPE)
					std::get<2>(channel).reset(new Pipe);

			log::debug("Spawning process:", boost::join(this->command, " "));
			pid_t child = ::fork();
			if (child < 0)
			{
				throw std::runtime_error("fork()");
			}
			else if (child == 0) // Child
			{
				for (auto& channel: channels)
				{
					Stream kind = std::get<1>(channel);
					bool is_sink = std::get<0>(channel);
					int old_fd = std::get<3>(channel);
					if (kind == Stream::PIPE)
					{
						int new_fd = (is_sink ?
						              std::get<2>(channel)->sink().handle() :
						              std::get<2>(channel)->source().handle());
					retry_dup2:
						int ret = ::dup2(new_fd, old_fd);
						if (ret == -1) {
							if (errno == EINTR) goto retry_dup2;
							::exit(EXIT_FAILURE);
						}
					}
					else if (kind == Stream::DEVNULL)
					{
						::close(old_fd);
					}
				}
				stdin_pipe.reset();
				stdout_pipe.reset();
				stderr_pipe.reset();
				std::vector<char const*> args;
				for (auto& arg: this->command)
					args.push_back(arg.c_str());
				args.push_back(nullptr);
				::execve(args[0], (char**) &args[0], env);
				::exit(EXIT_FAILURE);
			}
			else // Parent
			{
				if (this->options.stdin_ == Stream::PIPE)
					this->stdin_sink = stdin_pipe->sink();
				if (this->options.stdout_ == Stream::PIPE)
					this->stdout_source = stdout_pipe->source();
				if (this->options.stderr_ == Stream::PIPE)
					this->stderr_source = stderr_pipe->source();
			}
			return child;
		}
#elif defined(BOOST_WINDOWS_API)
		Child _create_child()
		{
			LPSECURITY_ATTRIBUTES proc_attrs = 0
			LPSECURITY_ATTRIBUTES thread_attrs = 0;
			BOOL inherit_handles = false;
			LPVOID env = nullptr;
			LPCTSTR work_dir = nullptr;
#if (_WIN32_WINNT >= 0x0600)
			DWORD creation_flags = EXTENDED_STARTUPINFO_PRESENT;
			STARTUPINFOEX startup_info_ex;
			ZeroMemory(&startup_info_ex, sizeof(STARTUPINFOEX));
			STARTUPINFO& startup_info = startup_info_ex.StartupInfo;
			startup_info.cb = sizeof(STARTUPINFOEX);
#else
			DWORD creation_flags = 0;
			STARTUPINFO startup_info;
			ZeroMemory(&startup_info, sizeof(STARTUPINFO));
			startup_info.cb = sizeof(STARTUPINFO);
#endif

			std::unique_ptr<Pipe> stdin_pipe;
			if (this->options.stdin_ == Stream::PIPE)
			{
				stdin_pipe.reset(new Pipe);
				::SetHandleInformation(stdin_pipe->source().handle(),
				                       HANDLE_FLAG_INHERIT,
				                       HANDLE_FLAG_INHERIT);
				startup_info.hStdInput = stdin_pipe->source().handle();
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
				inherit_handles = true;
				this->stdin_sink = stdin_pipe->sink();
			}
			else if (this->options.stdin_ == Stream::DEVNULL)
			{
				startup_info.hStdInput = INVALID_HANDLE_VALUE;
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
			}

			std::unique_ptr<Pipe> stdout_pipe;
			if (this->options.stdout_ == Stream::PIPE)
			{
				stdout_pipe.reset(new Pipe);
				::SetHandleInformation(stdout_pipe->sink().handle(),
				                       HANDLE_FLAG_INHERIT,
				                       HANDLE_FLAG_INHERIT);
				startup_info.hStdOutput = stdout_pipe->sink().handle();
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
				inherit_handles = true;
				this->stdout_source = stdout_pipe->source();
			}
			else if (this->options.stdout_ == Stream::DEVNULL)
			{
				startup_info.hStdOutput = INVALID_HANDLE_VALUE;
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
			}

			std::unique_ptr<Pipe> stderr_pipe;
			if (this->options.stderr_ == Stream::PIPE)
			{
				stderr_pipe.reset(new Pipe);
				::SetHandleInformation(stderr_pipe->sink().handle(),
				                       HANDLE_FLAG_INHERIT,
				                       HANDLE_FLAG_INHERIT);
				startup_info.hStdError = stderr_pipe->sink().handle();
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
				inherit_handles = true;
				this->stderr_source = stderr_pipe->source();
			}
			else if (this->options.stderr_ == Stream::DEVNULL)
			{
				startup_info.hStdError = INVALID_HANDLE_VALUE;
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
			}

			PROCESS_INFORMATION proc_info;
			auto ret = ::CreateProcess(
			  this->command.at(0).c_str(),
			  quote<CommandParser::windows_shell>(this->command).c_str(),
			  proc_attrs, thread_attrs, inherit_handles, creation_flags, env,
			  work_dir, &startup_info, &proc_info);
			if (!ret)
				throw std::runtime_error("CreateProcess() error");
			return Child(proc_info);
		}
#endif // !BOOST_WINDOWS_API

		Command _prepare_command(Command cmd)
		{
			cmd[0] = Filesystem::which(cmd[0]).get().string();
			return std::move(cmd);
		}

	};

	Process::Process(Command cmd, Options options)
		: _this(new Impl(std::move(cmd), std::move(options)))
	{}

	Process::~Process()
	{ this->wait(); }

	Process::Options const& Process::options() const
	{ return _this->options; }

	boost::optional<Process::ExitCode> Process::exit_code()
	{
		if (!_this->exit_code)
		{
#ifdef BOOST_WINDOWS_API
			int ret = ::WaitForSingleObject(_this->child.process_handle(), 0);
			switch (ret)
			{
			case WAIT_FAILED:
				throw std::runtime_error("WaitForSingleObject() failed");
			case WAIT_ABANDONED:
				log::warning(
				  "Wait on child", _this->child, "has been abandoned");
				break;
			case WAIT_TIMEOUT:
				log::debug("The child", _this->child, "is still alive");
				break;
			case WAIT_OBJECT_0:
			{
				DWORD exit_code;
				if (!::GetExitCodeProcess(
				      _this->child.process_handle(), &exit_code))
					throw std::runtime_error("GetExitCodeProcess() failed");
				log::debug("The child", _this->child,
				           "exited with status code", exit_code);
				_this->exit_code = exit_code;
			}
			}
#else
			pid_t ret;
			int status;
			do
			{
				log::debug("Checking exit status of child", _this->child);
				ret =
				  ::waitpid(_this->child.process_handle(), &status, WNOHANG);
			} while (ret == -1 && errno == EINTR);
			if (ret == -1)
				throw std::runtime_error("Waitpid failed");
			if (ret != 0)
			{
				log::debug("The child", _this->child,
				           "exited with status code", WEXITSTATUS(status));
				_this->exit_code = WEXITSTATUS(status);
			}
			else
				log::debug("The child", _this->child, "is still alive");
#endif
		}
		return _this->exit_code;
	}

	Process::ExitCode Process::wait()
	{
		while (!this->exit_code())
			log::debug("Waiting for child", _this->child, "to terminate");
		return _this->exit_code.get();
	}

	Process::ExitCode Process::call(Command cmd, Options options)
	{
		Process p(std::move(cmd), std::move(options));
		return p.wait();
	}

	std::string Process::check_output(Command cmd, Options options)
	{
		options.stdout_ = Stream::PIPE;
		options.stderr_ = Stream::DEVNULL;
		Process p(std::move(cmd), std::move(options));

		char buf[4096];
		std::string res;
		ssize_t size;
		auto& src = p._this->stdout_source;
		while (true)
		{
			size = ::read(src.handle(), buf, sizeof(buf));
			log::debug("Read from", p._this->child, "returned", size);
			if (size < 0)
			{
				if (errno == EINTR)
				{
					log::debug("Read interrupted by a signal, let's retry");
					continue;
				}
				throw std::runtime_error("read(): " + std::string(strerror(errno)));
			}
			if (size > 0)
			{
				log::debug("read", size, "bytes from child", p._this->child, "stdout");
				res.append(buf, size);
			}
			if (size == 0)
				break;
		}
		if (p.wait() != 0)
			throw std::runtime_error("Program failed");
		return res;
	}
}
<commit_msg>Fix more typos.<commit_after>#include "Process.hpp"
#include "log.hpp"
#include "Filesystem.hpp"
#include "quote.hpp"

#include <boost/config.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>

#include <cassert>
#include <stdexcept>
#include <inttypes.h>

#if defined(BOOST_POSIX_API)
# include <sys/wait.h>
# include <unistd.h>
# if defined(__APPLE__) && defined(__DYNAMIC__)
#  include <crt_externs.h> // For _NSGetEnviron()
# endif
#elif defined(BOOST_WINDOWS_API)
# include <Windows.h>
#else
# error "Unsupported platform"
#endif

namespace io = boost::iostreams;

namespace configure {

	namespace {

		char** get_environ()
		{
#if defined(__APPLE__) && defined(__DYNAMIC__)
			return *_NSGetEnviron();
#else
			return environ;
#endif
		}

#ifdef BOOST_WINDOWS_API
		typedef HANDLE file_descriptor_t;
#else
		typedef int file_descriptor_t;
#endif

		struct Pipe
		{
		private:
			io::file_descriptor_source _source;
			io::file_descriptor_sink _sink;

		public:
			Pipe()
			{
				file_descriptor_t fds[2];
#ifdef BOOST_WINDOWS_API
				if (!::CreatePipe(&fds[0], &fds[1], NULL, 0))
#else
				if (::pipe(fds) == -1)
#endif
					throw std::runtime_error("pipe error");
				_source = io::file_descriptor_source(
				  fds[0], io::file_descriptor_flags::close_handle);
				_sink = io::file_descriptor_sink(
				  fds[1], io::file_descriptor_flags::close_handle);
			}

		public:
			io::file_descriptor_sink& sink() { return _sink; }
			io::file_descriptor_source& source() { return _source; }

		public:
			void close()
			{
				_sink.close();
				_source.close();
			}
		};

#ifdef BOOST_WINDOWS_API
		struct Child
		{
		private:
			PROCESS_INFORMATION _proc_info;

		public:
			explicit Child(PROCESS_INFORMATION const& proc_info)
			    : _proc_info(proc_info)
			{}

			Child(Child&& other)
				: _proc_info(other._proc_info)
			{
				other._proc_info.hProcess = INVALID_HANDLE_VALUE;
				other._proc_info.hThread = INVALID_HANDLE_VALUE;
			}

			~Child()
			{
				::CloseHandle(_proc_info.hProcess);
				::CloseHandle(_proc_info.hThread);
			}

			HANDLE process_handle() const { return _proc_info.hProcess; }
		};
#else
		struct Child
		{
		private:
			pid_t _pid;

		public:
			explicit Child(pid_t pid) : _pid(pid) {}
			pid_t process_handle() const { return _pid; }
		};
#endif

		std::ostream& operator <<(std::ostream& out, Child const& child)
		{
			return out << "<Process " << child.process_handle() << ">";
		}

	} // !anonymous

	struct Process::Impl
	{
		Command const command;
		Options const options;
		boost::optional<ExitCode> exit_code;
		io::file_descriptor_sink stdin_sink;
		io::file_descriptor_source stdout_source;
		io::file_descriptor_source stderr_source;
		Child child;

		Impl(Command cmd, Options options)
			: command(_prepare_command(std::move(cmd)))
			, options(std::move(options))
			, exit_code(boost::none)
			, child(_create_child())
		{}

#ifdef BOOST_POSIX_API
		pid_t _create_child()
		{
			char** env = nullptr;
			// Working directory
			if (this->options.working_directory)
			{
				throw "not there";
			}

			if (this->options.inherit_env)
			{
				env = get_environ();
			}

			std::unique_ptr<Pipe> stdin_pipe;
			std::unique_ptr<Pipe> stdout_pipe;
			std::unique_ptr<Pipe> stderr_pipe;
			std::tuple<bool, Stream, std::unique_ptr<Pipe>&, int> channels[] = {
				std::make_tuple(false,
				                this->options.stdin_,
				                std::ref(stdin_pipe),
				                STDIN_FILENO),
				std::make_tuple(true,
				                this->options.stdout_,
				                std::ref(stdout_pipe),
				                STDOUT_FILENO),
				std::make_tuple(true,
				                this->options.stderr_,
				                std::ref(stderr_pipe),
				                STDERR_FILENO),
			};

			for (auto& channel: channels)
				if (std::get<1>(channel) == Stream::PIPE)
					std::get<2>(channel).reset(new Pipe);

			log::debug("Spawning process:", boost::join(this->command, " "));
			pid_t child = ::fork();
			if (child < 0)
			{
				throw std::runtime_error("fork()");
			}
			else if (child == 0) // Child
			{
				for (auto& channel: channels)
				{
					Stream kind = std::get<1>(channel);
					bool is_sink = std::get<0>(channel);
					int old_fd = std::get<3>(channel);
					if (kind == Stream::PIPE)
					{
						int new_fd = (is_sink ?
						              std::get<2>(channel)->sink().handle() :
						              std::get<2>(channel)->source().handle());
					retry_dup2:
						int ret = ::dup2(new_fd, old_fd);
						if (ret == -1) {
							if (errno == EINTR) goto retry_dup2;
							::exit(EXIT_FAILURE);
						}
					}
					else if (kind == Stream::DEVNULL)
					{
						::close(old_fd);
					}
				}
				stdin_pipe.reset();
				stdout_pipe.reset();
				stderr_pipe.reset();
				std::vector<char const*> args;
				for (auto& arg: this->command)
					args.push_back(arg.c_str());
				args.push_back(nullptr);
				::execve(args[0], (char**) &args[0], env);
				::exit(EXIT_FAILURE);
			}
			else // Parent
			{
				if (this->options.stdin_ == Stream::PIPE)
					this->stdin_sink = stdin_pipe->sink();
				if (this->options.stdout_ == Stream::PIPE)
					this->stdout_source = stdout_pipe->source();
				if (this->options.stderr_ == Stream::PIPE)
					this->stderr_source = stderr_pipe->source();
			}
			return child;
		}
#elif defined(BOOST_WINDOWS_API)
		Child _create_child()
		{
			LPSECURITY_ATTRIBUTES proc_attrs = 0;
			LPSECURITY_ATTRIBUTES thread_attrs = 0;
			BOOL inherit_handles = false;
			LPVOID env = nullptr;
			LPCTSTR work_dir = nullptr;
#if (_WIN32_WINNT >= 0x0600)
			DWORD creation_flags = EXTENDED_STARTUPINFO_PRESENT;
			STARTUPINFOEX startup_info_ex;
			ZeroMemory(&startup_info_ex, sizeof(STARTUPINFOEX));
			STARTUPINFO& startup_info = startup_info_ex.StartupInfo;
			startup_info.cb = sizeof(STARTUPINFOEX);
#else
			DWORD creation_flags = 0;
			STARTUPINFO startup_info;
			ZeroMemory(&startup_info, sizeof(STARTUPINFO));
			startup_info.cb = sizeof(STARTUPINFO);
#endif

			std::unique_ptr<Pipe> stdin_pipe;
			if (this->options.stdin_ == Stream::PIPE)
			{
				stdin_pipe.reset(new Pipe);
				::SetHandleInformation(stdin_pipe->source().handle(),
				                       HANDLE_FLAG_INHERIT,
				                       HANDLE_FLAG_INHERIT);
				startup_info.hStdInput = stdin_pipe->source().handle();
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
				inherit_handles = true;
				this->stdin_sink = stdin_pipe->sink();
			}
			else if (this->options.stdin_ == Stream::DEVNULL)
			{
				startup_info.hStdInput = INVALID_HANDLE_VALUE;
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
			}

			std::unique_ptr<Pipe> stdout_pipe;
			if (this->options.stdout_ == Stream::PIPE)
			{
				stdout_pipe.reset(new Pipe);
				::SetHandleInformation(stdout_pipe->sink().handle(),
				                       HANDLE_FLAG_INHERIT,
				                       HANDLE_FLAG_INHERIT);
				startup_info.hStdOutput = stdout_pipe->sink().handle();
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
				inherit_handles = true;
				this->stdout_source = stdout_pipe->source();
			}
			else if (this->options.stdout_ == Stream::DEVNULL)
			{
				startup_info.hStdOutput = INVALID_HANDLE_VALUE;
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
			}

			std::unique_ptr<Pipe> stderr_pipe;
			if (this->options.stderr_ == Stream::PIPE)
			{
				stderr_pipe.reset(new Pipe);
				::SetHandleInformation(stderr_pipe->sink().handle(),
				                       HANDLE_FLAG_INHERIT,
				                       HANDLE_FLAG_INHERIT);
				startup_info.hStdError = stderr_pipe->sink().handle();
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
				inherit_handles = true;
				this->stderr_source = stderr_pipe->source();
			}
			else if (this->options.stderr_ == Stream::DEVNULL)
			{
				startup_info.hStdError = INVALID_HANDLE_VALUE;
				startup_info.dwFlags |= STARTF_USESTDHANDLES;
			}

			PROCESS_INFORMATION proc_info;
			auto ret = ::CreateProcess(
			  this->command.at(0).c_str(),
			  quote<CommandParser::windows_shell>(this->command).c_str(),
			  proc_attrs, thread_attrs, inherit_handles, creation_flags, env,
			  work_dir, &startup_info, &proc_info);
			if (!ret)
				throw std::runtime_error("CreateProcess() error");
			return Child(proc_info);
		}
#endif // !BOOST_WINDOWS_API

		Command _prepare_command(Command cmd)
		{
			cmd[0] = Filesystem::which(cmd[0]).get().string();
			return std::move(cmd);
		}

	};

	Process::Process(Command cmd, Options options)
		: _this(new Impl(std::move(cmd), std::move(options)))
	{}

	Process::~Process()
	{ this->wait(); }

	Process::Options const& Process::options() const
	{ return _this->options; }

	boost::optional<Process::ExitCode> Process::exit_code()
	{
		if (!_this->exit_code)
		{
#ifdef BOOST_WINDOWS_API
			int ret = ::WaitForSingleObject(_this->child.process_handle(), 0);
			switch (ret)
			{
			case WAIT_FAILED:
				throw std::runtime_error("WaitForSingleObject() failed");
			case WAIT_ABANDONED:
				log::warning(
				  "Wait on child", _this->child, "has been abandoned");
				break;
			case WAIT_TIMEOUT:
				log::debug("The child", _this->child, "is still alive");
				break;
			case WAIT_OBJECT_0:
			{
				DWORD exit_code;
				if (!::GetExitCodeProcess(
				      _this->child.process_handle(), &exit_code))
					throw std::runtime_error("GetExitCodeProcess() failed");
				log::debug("The child", _this->child,
				           "exited with status code", exit_code);
				_this->exit_code = exit_code;
			}
			}
#else
			pid_t ret;
			int status;
			do
			{
				log::debug("Checking exit status of child", _this->child);
				ret =
				  ::waitpid(_this->child.process_handle(), &status, WNOHANG);
			} while (ret == -1 && errno == EINTR);
			if (ret == -1)
				throw std::runtime_error("Waitpid failed");
			if (ret != 0)
			{
				log::debug("The child", _this->child,
				           "exited with status code", WEXITSTATUS(status));
				_this->exit_code = WEXITSTATUS(status);
			}
			else
				log::debug("The child", _this->child, "is still alive");
#endif
		}
		return _this->exit_code;
	}

	Process::ExitCode Process::wait()
	{
		while (!this->exit_code())
			log::debug("Waiting for child", _this->child, "to terminate");
		return _this->exit_code.get();
	}

	Process::ExitCode Process::call(Command cmd, Options options)
	{
		Process p(std::move(cmd), std::move(options));
		return p.wait();
	}

	std::string Process::check_output(Command cmd, Options options)
	{
		options.stdout_ = Stream::PIPE;
		options.stderr_ = Stream::DEVNULL;
		Process p(std::move(cmd), std::move(options));

		char buf[4096];
		std::string res;
#ifdef BOOST_WINDOWS_API
		int size;
#else
		ssize_t size;
#endif
		auto& src = p._this->stdout_source;
		while (true)
		{
			size = ::read(src.handle(), buf, sizeof(buf));
			log::debug("Read from", p._this->child, "returned", size);
			if (size < 0)
			{
				if (errno == EINTR)
				{
					log::debug("Read interrupted by a signal, let's retry");
					continue;
				}
				throw std::runtime_error("read(): " + std::string(strerror(errno)));
			}
			if (size > 0)
			{
				log::debug("read", size, "bytes from child", p._this->child, "stdout");
				res.append(buf, size);
			}
			if (size == 0)
				break;
		}
		if (p.wait() != 0)
			throw std::runtime_error("Program failed");
		return res;
	}
}
<|endoftext|>