<commit_before>#include "xchainer/scalar.h"

#include <gtest/gtest.h>

namespace xchainer {
namespace {

TEST(ScalarTest, Dtype) {
    ASSERT_EQ(Scalar(true).dtype(), Dtype::kBool);
    ASSERT_EQ(Scalar(false).dtype(), Dtype::kBool);
    ASSERT_EQ(Scalar(int8_t(1)).dtype(), Dtype::kInt8);
    ASSERT_EQ(Scalar(int16_t(2)).dtype(), Dtype::kInt16);
    ASSERT_EQ(Scalar(int32_t(3)).dtype(), Dtype::kInt32);
    ASSERT_EQ(Scalar(int64_t(4)).dtype(), Dtype::kInt64);
    ASSERT_EQ(Scalar(uint8_t(5)).dtype(), Dtype::kUInt8);
    ASSERT_EQ(Scalar(6.7f).dtype(), Dtype::kFloat32);
    ASSERT_EQ(Scalar(8.9).dtype(), Dtype::kFloat64);
}

TEST(ScalarTest, Cast) {
    ASSERT_TRUE(bool(Scalar(true)));
    ASSERT_TRUE(bool(Scalar(1)));
    ASSERT_FALSE(bool(Scalar(false)));
    ASSERT_FALSE(bool(Scalar(0)));
    ASSERT_EQ(int8_t(Scalar(1)), 1);
    ASSERT_EQ(int16_t(Scalar(2)), 2);
    ASSERT_EQ(int32_t(Scalar(3)), 3);
    ASSERT_EQ(int64_t(Scalar(4)), 4);
    ASSERT_EQ(uint8_t(Scalar(5)), 5);
    ASSERT_FLOAT_EQ(float(Scalar(6)), 6.0f);
    ASSERT_FLOAT_EQ(float(Scalar(6.7)), 6.7f);
    ASSERT_DOUBLE_EQ(double(Scalar(8)), 8.0);
    ASSERT_DOUBLE_EQ(double(Scalar(8.9)), 8.9);
}

TEST(DtypeTest, UnaryOps) {
    ASSERT_THROW(-Scalar(true), DtypeError);
    ASSERT_THROW(-Scalar(false), DtypeError);
    ASSERT_EQ(int8_t(-Scalar(1)), -1);
    ASSERT_EQ(int16_t(-Scalar(2)), -2);
    ASSERT_EQ(int32_t(-Scalar(3)), -3);
    ASSERT_EQ(int64_t(-Scalar(4)), -4);
    ASSERT_EQ(uint8_t(-Scalar(5)), uint8_t(-5));
    ASSERT_FLOAT_EQ(float(-Scalar(6)), -6.0f);
    ASSERT_FLOAT_EQ(float(-Scalar(6.7)), -6.7f);
    ASSERT_DOUBLE_EQ(double(-Scalar(8)), -8.0);
    ASSERT_DOUBLE_EQ(double(-Scalar(8.9)), -8.9);

    ASSERT_EQ(int8_t(+Scalar(1)), 1);
    ASSERT_EQ(int16_t(+Scalar(2)), 2);
    ASSERT_EQ(int32_t(+Scalar(3)), 3);
    ASSERT_EQ(int64_t(+Scalar(4)), 4);
    ASSERT_EQ(uint8_t(+Scalar(5)), 5);
    ASSERT_EQ(float(+Scalar(5)), 5);
    ASSERT_FLOAT_EQ(float(+Scalar(6)), 6.0f);
    ASSERT_FLOAT_EQ(float(+Scalar(6.7)), 6.7f);
    ASSERT_DOUBLE_EQ(double(+Scalar(8)), 8.0);
    ASSERT_DOUBLE_EQ(double(+Scalar(8.9)), 8.9);
}

TEST(ScalarTest, ToString) {
    ASSERT_STREQ(Scalar(true).ToString().c_str(), "True");
    ASSERT_STREQ(Scalar(false).ToString().c_str(), "False");
    ASSERT_STREQ(Scalar(int8_t(1)).ToString().c_str(), std::to_string(int8_t(1)).c_str());
    ASSERT_STREQ(Scalar(int16_t(2)).ToString().c_str(), std::to_string(int16_t(2)).c_str());
    ASSERT_STREQ(Scalar(int32_t(3)).ToString().c_str(), std::to_string(int32_t(3)).c_str());
    ASSERT_STREQ(Scalar(int64_t(4)).ToString().c_str(), std::to_string(int64_t(4)).c_str());
    ASSERT_STREQ(Scalar(uint8_t(5)).ToString().c_str(), std::to_string(uint8_t(5)).c_str());
}

}  // namespace
}  // namespace xchainer
<commit_msg>Compare strings instead of C-string in tests<commit_after>#include "xchainer/scalar.h"

#include <gtest/gtest.h>

namespace xchainer {
namespace {

TEST(ScalarTest, Dtype) {
    ASSERT_EQ(Scalar(true).dtype(), Dtype::kBool);
    ASSERT_EQ(Scalar(false).dtype(), Dtype::kBool);
    ASSERT_EQ(Scalar(int8_t(1)).dtype(), Dtype::kInt8);
    ASSERT_EQ(Scalar(int16_t(2)).dtype(), Dtype::kInt16);
    ASSERT_EQ(Scalar(int32_t(3)).dtype(), Dtype::kInt32);
    ASSERT_EQ(Scalar(int64_t(4)).dtype(), Dtype::kInt64);
    ASSERT_EQ(Scalar(uint8_t(5)).dtype(), Dtype::kUInt8);
    ASSERT_EQ(Scalar(6.7f).dtype(), Dtype::kFloat32);
    ASSERT_EQ(Scalar(8.9).dtype(), Dtype::kFloat64);
}

TEST(ScalarTest, Cast) {
    ASSERT_TRUE(bool(Scalar(true)));
    ASSERT_TRUE(bool(Scalar(1)));
    ASSERT_FALSE(bool(Scalar(false)));
    ASSERT_FALSE(bool(Scalar(0)));
    ASSERT_EQ(int8_t(Scalar(1)), 1);
    ASSERT_EQ(int16_t(Scalar(2)), 2);
    ASSERT_EQ(int32_t(Scalar(3)), 3);
    ASSERT_EQ(int64_t(Scalar(4)), 4);
    ASSERT_EQ(uint8_t(Scalar(5)), 5);
    ASSERT_FLOAT_EQ(float(Scalar(6)), 6.0f);
    ASSERT_FLOAT_EQ(float(Scalar(6.7)), 6.7f);
    ASSERT_DOUBLE_EQ(double(Scalar(8)), 8.0);
    ASSERT_DOUBLE_EQ(double(Scalar(8.9)), 8.9);
}

TEST(DtypeTest, UnaryOps) {
    ASSERT_THROW(-Scalar(true), DtypeError);
    ASSERT_THROW(-Scalar(false), DtypeError);
    ASSERT_EQ(int8_t(-Scalar(1)), -1);
    ASSERT_EQ(int16_t(-Scalar(2)), -2);
    ASSERT_EQ(int32_t(-Scalar(3)), -3);
    ASSERT_EQ(int64_t(-Scalar(4)), -4);
    ASSERT_EQ(uint8_t(-Scalar(5)), uint8_t(-5));
    ASSERT_FLOAT_EQ(float(-Scalar(6)), -6.0f);
    ASSERT_FLOAT_EQ(float(-Scalar(6.7)), -6.7f);
    ASSERT_DOUBLE_EQ(double(-Scalar(8)), -8.0);
    ASSERT_DOUBLE_EQ(double(-Scalar(8.9)), -8.9);

    ASSERT_EQ(int8_t(+Scalar(1)), 1);
    ASSERT_EQ(int16_t(+Scalar(2)), 2);
    ASSERT_EQ(int32_t(+Scalar(3)), 3);
    ASSERT_EQ(int64_t(+Scalar(4)), 4);
    ASSERT_EQ(uint8_t(+Scalar(5)), 5);
    ASSERT_EQ(float(+Scalar(5)), 5);
    ASSERT_FLOAT_EQ(float(+Scalar(6)), 6.0f);
    ASSERT_FLOAT_EQ(float(+Scalar(6.7)), 6.7f);
    ASSERT_DOUBLE_EQ(double(+Scalar(8)), 8.0);
    ASSERT_DOUBLE_EQ(double(+Scalar(8.9)), 8.9);
}

TEST(ScalarTest, ToString) {
    ASSERT_EQ(Scalar(true).ToString(), "True");
    ASSERT_EQ(Scalar(false).ToString(), "False");
    ASSERT_EQ(Scalar(int8_t(1)).ToString(), std::to_string(int8_t(1)));
    ASSERT_EQ(Scalar(int16_t(2)).ToString(), std::to_string(int16_t(2)));
    ASSERT_EQ(Scalar(int32_t(3)).ToString(), std::to_string(int32_t(3)));
    ASSERT_EQ(Scalar(int64_t(4)).ToString(), std::to_string(int64_t(4)));
    ASSERT_EQ(Scalar(uint8_t(5)).ToString(), std::to_string(uint8_t(5)));
}

}  // namespace
}  // namespace xchainer
<|endoftext|>


<commit_before>/*
 * Android File Transfer for Linux: MTP client for android devices
 * Copyright (C) 2015  Vladimir Menshakov

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

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#include <usb/Device.h>
#include <usb/Exception.h>
#include <mtp/ByteArray.h>

#include <unistd.h>
#include <sys/ioctl.h>
#include <time.h>

#include "linux/usbdevice_fs.h"

#define IOCTL(...) do { int r = ioctl(__VA_ARGS__); if (r < 0) throw Exception(#__VA_ARGS__); } while(false)

namespace mtp { namespace usb
{
	Device::InterfaceToken::InterfaceToken(int fd, unsigned interfaceNumber): _fd(fd), _interfaceNumber(interfaceNumber)
	{
		IOCTL(_fd, USBDEVFS_CLAIMINTERFACE, &interfaceNumber);
	}

	Device::InterfaceToken::~InterfaceToken()
	{
		ioctl(_fd, USBDEVFS_RELEASEINTERFACE, _interfaceNumber);
	}

	Device::Device(int fd): _fd(fd)
	{
		IOCTL(_fd, USBDEVFS_GET_CAPABILITIES, &_capabilities);
	}

	Device::~Device()
	{
		close(_fd);
	}

	int Device::GetConfiguration() const
	{
		return 0;
	}

	void Device::SetConfiguration(int idx)
	{
		fprintf(stderr, "SetConfiguration(%d): not implemented", idx);
	}

	void Device::Reap(void *urbPtr, int timeout)
	{
		usbdevfs_urb *urb = static_cast<usbdevfs_urb *>(urbPtr);
		timespec started = {};
		if (clock_gettime(CLOCK_MONOTONIC, &started) == -1)
			throw Exception("clock_gettime");

		while(true)
		{
			int r = ioctl(_fd, USBDEVFS_REAPURBNDELAY, &urb);
			if (r == 0)
			{
				break;
			}
			if (r == -1 && errno == EAGAIN)
			{
				timespec now = {};
				if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
					throw Exception("clock_gettime");
				int delta = (now.tv_sec - started.tv_sec) * 1000 + (now.tv_nsec - started.tv_nsec) / 1000000;
				if (timeout > 0 && delta >= timeout)
					throw std::runtime_error("timeout reaping usb urb");
				usleep(100); //0.1ms
				continue;
			}
		}
	}

	void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout)
	{
		size_t transferSize = ep->GetMaxPacketSize() * 1024;
		ByteArray data(transferSize);

		size_t r;
		bool continuation = false;
		do
		{
			r = inputStream->Read(data.data(), data.size());
			//HexDump("write", ByteArray(data.data(), data.data() + r));
			usbdevfs_urb urb = {};
			urb.usercontext = this;
			urb.type = USBDEVFS_URB_TYPE_BULK;
			urb.endpoint = ep->GetAddress();
			urb.buffer = const_cast<u8 *>(data.data());
			urb.buffer_length = r;
			if (continuation)
				urb.flags |= USBDEVFS_URB_BULK_CONTINUATION;
			else
				continuation = true;
			IOCTL(_fd, USBDEVFS_SUBMITURB, &urb);
			Reap(&urb, timeout);
		}
		while(r == transferSize);
	}

	void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout)
	{
		ByteArray data(ep->GetMaxPacketSize() * 1024);
		usbdevfs_urb urb = {};
		urb.usercontext = this;
		urb.type = USBDEVFS_URB_TYPE_BULK;
		urb.endpoint = ep->GetAddress();
		urb.buffer = data.data();
		urb.buffer_length = data.size();
		IOCTL(_fd, USBDEVFS_SUBMITURB, &urb);

		Reap(&urb, timeout);
		//HexDump("read", ByteArray(data.data(), data.data() + urb.actual_length));
		outputStream->Write(data.data(), urb.actual_length);
	}

}}<commit_msg>fixed invalid release interface ioctl<commit_after>/*
 * Android File Transfer for Linux: MTP client for android devices
 * Copyright (C) 2015  Vladimir Menshakov

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

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#include <usb/Device.h>
#include <usb/Exception.h>
#include <mtp/ByteArray.h>

#include <unistd.h>
#include <sys/ioctl.h>
#include <time.h>

#include "linux/usbdevice_fs.h"

#define IOCTL(...) do { int r = ioctl(__VA_ARGS__); if (r < 0) throw Exception(#__VA_ARGS__); } while(false)

namespace mtp { namespace usb
{
	Device::InterfaceToken::InterfaceToken(int fd, unsigned interfaceNumber): _fd(fd), _interfaceNumber(interfaceNumber)
	{
		IOCTL(_fd, USBDEVFS_CLAIMINTERFACE, &interfaceNumber);
	}

	Device::InterfaceToken::~InterfaceToken()
	{
		ioctl(_fd, USBDEVFS_RELEASEINTERFACE, &_interfaceNumber);
	}

	Device::Device(int fd): _fd(fd)
	{
		IOCTL(_fd, USBDEVFS_GET_CAPABILITIES, &_capabilities);
	}

	Device::~Device()
	{
		close(_fd);
	}

	int Device::GetConfiguration() const
	{
		return 0;
	}

	void Device::SetConfiguration(int idx)
	{
		fprintf(stderr, "SetConfiguration(%d): not implemented", idx);
	}

	void Device::Reap(void *urbPtr, int timeout)
	{
		usbdevfs_urb *urb = static_cast<usbdevfs_urb *>(urbPtr);
		timespec started = {};
		if (clock_gettime(CLOCK_MONOTONIC, &started) == -1)
			throw Exception("clock_gettime");

		while(true)
		{
			int r = ioctl(_fd, USBDEVFS_REAPURBNDELAY, &urb);
			if (r == 0)
			{
				break;
			}
			if (r == -1 && errno == EAGAIN)
			{
				timespec now = {};
				if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
					throw Exception("clock_gettime");
				int delta = (now.tv_sec - started.tv_sec) * 1000 + (now.tv_nsec - started.tv_nsec) / 1000000;
				if (timeout > 0 && delta >= timeout)
					throw std::runtime_error("timeout reaping usb urb");
				usleep(100); //0.1ms
				continue;
			}
		}
	}

	void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout)
	{
		size_t transferSize = ep->GetMaxPacketSize() * 1024;
		ByteArray data(transferSize);

		size_t r;
		bool continuation = false;
		do
		{
			r = inputStream->Read(data.data(), data.size());
			//HexDump("write", ByteArray(data.data(), data.data() + r));
			usbdevfs_urb urb = {};
			urb.usercontext = this;
			urb.type = USBDEVFS_URB_TYPE_BULK;
			urb.endpoint = ep->GetAddress();
			urb.buffer = const_cast<u8 *>(data.data());
			urb.buffer_length = r;
			if (continuation)
				urb.flags |= USBDEVFS_URB_BULK_CONTINUATION;
			else
				continuation = true;
			IOCTL(_fd, USBDEVFS_SUBMITURB, &urb);
			Reap(&urb, timeout);
		}
		while(r == transferSize);
	}

	void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout)
	{
		ByteArray data(ep->GetMaxPacketSize() * 1024);
		usbdevfs_urb urb = {};
		urb.usercontext = this;
		urb.type = USBDEVFS_URB_TYPE_BULK;
		urb.endpoint = ep->GetAddress();
		urb.buffer = data.data();
		urb.buffer_length = data.size();
		IOCTL(_fd, USBDEVFS_SUBMITURB, &urb);

		Reap(&urb, timeout);
		//HexDump("read", ByteArray(data.data(), data.data() + urb.actual_length));
		outputStream->Write(data.data(), urb.actual_length);
	}

}}<|endoftext|>


<commit_before>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015  Vladimir Menshakov

    Android File Transfer For Linux 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.

    Android File Transfer For Linux 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 Android File Transfer For Linux.
    If not, see <http://www.gnu.org/licenses/>.
 */

#include <usb/Device.h>
#include <usb/Exception.h>
#include <mtp/usb/TimeoutException.h>
#include <mtp/usb/DeviceBusyException.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <mtp/ByteArray.h>

#include <unistd.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <sys/time.h>

#include "linux/usbdevice_fs.h"

#define IOCTL(...) do \
{ \
	int r = ioctl(__VA_ARGS__); \
	if (r < 0) \
	{ \
		if (errno == EBUSY) \
			throw DeviceBusyException(); \
		else if (errno == ENODEV) \
			throw DeviceNotFoundException(); \
		else \
			throw Exception("ioctl(" #__VA_ARGS__ ")"); \
	} \
} while(false)

namespace mtp { namespace usb
{

	FileHandler::~FileHandler()
	{ close(_fd); }

	InterfaceToken::InterfaceToken(int fd, unsigned interfaceNumber): _fd(fd), _interfaceNumber(interfaceNumber)
	{
		IOCTL(_fd, USBDEVFS_CLAIMINTERFACE, &interfaceNumber);
	}

	InterfaceToken::~InterfaceToken()
	{
		ioctl(_fd, USBDEVFS_RELEASEINTERFACE, &_interfaceNumber);
	}

#define PRINT_CAP(CAP, NAME) \
	if (capabilities & (CAP)) \
	{ \
		printf(NAME " "); \
		capabilities &= ~(CAP); \
	}

	Device::Device(int fd, const EndpointPtr &controlEp): _fd(fd), _capabilities(0), _controlEp(controlEp)
	{
		try { IOCTL(_fd.Get(), USBDEVFS_GET_CAPABILITIES, &_capabilities); }
		catch(const std::exception &ex)
		{ fprintf(stderr, "get usbfs capabilities failed: %s\n", ex.what()); }
		printf("capabilities = 0x%02x: ", (unsigned)_capabilities);
		if (_capabilities)
		{
			u32 capabilities = _capabilities;
			PRINT_CAP(USBDEVFS_CAP_ZERO_PACKET, "<zero>");
			PRINT_CAP(USBDEVFS_CAP_BULK_CONTINUATION, "<bulk-continuation>");
			PRINT_CAP(USBDEVFS_CAP_NO_PACKET_SIZE_LIM, "<no-packet-size-limit>");
			PRINT_CAP(USBDEVFS_CAP_BULK_SCATTER_GATHER, "<bulk-scatter-gather>");
			PRINT_CAP(USBDEVFS_CAP_REAP_AFTER_DISCONNECT, "<reap-after-disconnect>");
			if (capabilities)
				printf("<unknown capability 0x%02x>", capabilities);
			printf("\n");
		}
		else
			printf("[none]\n");
	}

	Device::~Device()
	{ }

	int Device::GetConfiguration() const
	{
		return 0;
	}

	void Device::SetConfiguration(int idx)
	{
		fprintf(stderr, "SetConfiguration(%d): not implemented", idx);
	}

	struct Device::Urb : Noncopyable
	{
		int						Fd;
		ByteArray				Buffer;
		usbdevfs_urb			KernelUrb;

		Urb(int fd, u8 type, const EndpointPtr & ep): Fd(fd), Buffer(4096), KernelUrb()
		{
			KernelUrb.type			= type;
			KernelUrb.endpoint		= ep->GetAddress();
			KernelUrb.buffer		= Buffer.data();
			KernelUrb.buffer_length = Buffer.size();
		}

		size_t GetTransferSize() const
		{ return Buffer.size(); }

		void Submit()
		{
			IOCTL(Fd, USBDEVFS_SUBMITURB, &KernelUrb);
		}

		void Discard()
		{
			int r = ioctl(Fd, USBDEVFS_DISCARDURB, &KernelUrb);
			if (r != 0)
			{
				perror("ioctl(USBDEVFS_DISCARDURB)");
			}
		}

		size_t Send(const IObjectInputStreamPtr &inputStream)
		{
			size_t r = inputStream->Read(Buffer.data(), Buffer.size());
			//HexDump("write", ByteArray(Buffer.data(), Buffer.data() + r));
			KernelUrb.buffer_length = r;
			return r;
		}

		size_t Send(const ByteArray &inputData)
		{
			size_t r = std::min(Buffer.size(), inputData.size());
			std::copy(inputData.data(), inputData.data() + r, Buffer.data());
			KernelUrb.buffer_length = r;
			return r;
		}

		size_t Recv(const IObjectOutputStreamPtr &outputStream)
		{
			//HexDump("read", ByteArray(Buffer.data(), Buffer.data() + KernelUrb.actual_length));
			return outputStream->Write(Buffer.data(), KernelUrb.actual_length);
		}

		ByteArray Recv()
		{ return ByteArray(Buffer.begin(), Buffer.begin() + KernelUrb.actual_length); }

		template<unsigned Flag>
		void SetFlag(bool value)
		{
			if (value)
				KernelUrb.flags |= Flag;
			else
				KernelUrb.flags &= ~Flag;
		}

		void SetContinuationFlag(bool continuation)
		{ SetFlag<USBDEVFS_URB_BULK_CONTINUATION>(continuation); }

		void SetZeroPacketFlag(bool zero)
		{ SetFlag<USBDEVFS_URB_ZERO_PACKET>(zero); }
	};

	void * Device::Reap(int timeout)
	{
		timeval started = {};
		if (gettimeofday(&started, NULL) == -1)
			throw usb::Exception("gettimeofday");

		pollfd fd = {};
		fd.fd		= _fd.Get();
		fd.events	= POLLOUT;
		int r = poll(&fd, 1, timeout);

		timeval now = {};
		if (gettimeofday(&now, NULL) == -1)
			throw usb::Exception("gettimeofday");

		if (r < 0)
			throw Exception("poll");

		if (r == 0 && timeout > 0)
		{
			int ms = (now.tv_sec - started.tv_sec) * 1000 + (now.tv_usec - started.tv_usec) / 1000;
			fprintf(stderr, "%d ms since the last poll call\n", ms);
		}

		usbdevfs_urb *urb;
		r = ioctl(_fd.Get(), USBDEVFS_REAPURBNDELAY, &urb);
		if (r == 0)
			return urb;
		else if (errno == EAGAIN)
			throw TimeoutException("timeout reaping usb urb");
		else
			throw Exception("ioctl");
	}

	void Device::ClearHalt(const EndpointPtr & ep)
	{
		try
		{ unsigned index = ep->GetAddress(); IOCTL(_fd.Get(), USBDEVFS_CLEAR_HALT, &index); }
		catch(const std::exception &ex)
		{ fprintf(stderr, "clearing halt status for ep %02x: %s\n", (unsigned)ep->GetAddress(), ex.what()); }
	}

	void Device::Submit(const UrbPtr &urb, int timeout)
	{
		urb->Submit();
		{
			scoped_mutex_lock l(_mutex);
			_urbs.insert(std::make_pair(&urb->KernelUrb, urb));
		}
		try
		{
			while(true)
			{
				UrbPtr completedUrb;
				{
					void *completedKernelUrb = Reap(timeout);
					scoped_mutex_lock l(_mutex);
					auto urbIt = _urbs.find(completedKernelUrb);
					if (urbIt == _urbs.end())
					{
						fprintf(stderr, "got unknown urb: %p\n", completedKernelUrb);
						continue;
					}
					completedUrb = urbIt->second;
					_urbs.erase(urbIt);
				}
				if (completedUrb == urb)
					break;
			}
		}
		catch(const TimeoutException &ex)
		{
			urb->Discard();
			throw;
		}
		catch(const std::exception &ex)
		{
			fprintf(stderr, "error while submitting urb: %s\n", ex.what());
			urb->Discard();
			throw;
		}
	}

	void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout)
	{
		UrbPtr urb = std::make_shared<Urb>(_fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb->GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			r = urb->Send(inputStream);
			urb->SetZeroPacketFlag(r != transferSize);
			urb->SetContinuationFlag(continuation);
			continuation = true;
			Submit(urb, timeout);

			ProcessControl();
		}
		while(r == transferSize);
	}

	void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout)
	{
		UrbPtr urb = std::make_shared<Urb>(_fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb->GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			urb->SetContinuationFlag(continuation);
			continuation = true;
			Submit(urb, timeout);

			ProcessControl();
			r = urb->Recv(outputStream);
		}
		while(r == transferSize);
	}

	u8 Device::TransactionType(const EndpointPtr &ep)
	{
		EndpointType type = ep->GetType();
		switch(type)
		{
		case EndpointType::Control:
			return USBDEVFS_URB_TYPE_CONTROL;
		case EndpointType::Isochronous:
			return USBDEVFS_URB_TYPE_ISO;
		case EndpointType::Bulk:
			return USBDEVFS_URB_TYPE_BULK;
		case EndpointType::Interrupt:
			return USBDEVFS_URB_TYPE_INTERRUPT;
		default:
			throw std::runtime_error("invalid endpoint type");
		}
	}

	void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout)
	{
		printf("read control %02x %02x %04x %04x\n", type, req, value, index);
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
		if (r >= 0)
			data.resize(r);
		else if (errno == EAGAIN)
			throw TimeoutException("timeout sending control transfer");
		else
			throw Exception("ioctl");
	}

	void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, bool interruptCurrentTransaction, int timeout)
	{
		printf("write control %02x %02x %04x %04x\n", type, req, value, index);
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		_controls.push(
		[fd, ctrl, interruptCurrentTransaction]()
		{
			int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
			if (r >= 0)
			{
				if (interruptCurrentTransaction)
					throw std::runtime_error("transaction aborted");
				else
					return;
			}
			else if (errno == EAGAIN)
				throw TimeoutException("timeout sending control transfer");
			else
				throw Exception("ioctl");
		});
	}

	void Device::ProcessControl()
	{
		scoped_mutex_lock l(_mutex);
		while(!_controls.empty())
		{
			try { _controls.front()(); } catch(const std::exception &ex) { _controls.pop(); throw; }
			_controls.pop();
		}
	}

}}
<commit_msg>slightly cleaned up code, aligned buffer size to transfer size<commit_after>/*
    This file is part of Android File Transfer For Linux.
    Copyright (C) 2015  Vladimir Menshakov

    Android File Transfer For Linux 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.

    Android File Transfer For Linux 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 Android File Transfer For Linux.
    If not, see <http://www.gnu.org/licenses/>.
 */

#include <usb/Device.h>
#include <usb/Exception.h>
#include <mtp/usb/TimeoutException.h>
#include <mtp/usb/DeviceBusyException.h>
#include <mtp/usb/DeviceNotFoundException.h>
#include <mtp/ByteArray.h>

#include <unistd.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <sys/time.h>

#include "linux/usbdevice_fs.h"

#define IOCTL(...) do \
{ \
	int r = ioctl(__VA_ARGS__); \
	if (r < 0) \
	{ \
		if (errno == EBUSY) \
			throw DeviceBusyException(); \
		else if (errno == ENODEV) \
			throw DeviceNotFoundException(); \
		else \
			throw Exception("ioctl(" #__VA_ARGS__ ")"); \
	} \
} while(false)

namespace mtp { namespace usb
{

	FileHandler::~FileHandler()
	{ close(_fd); }

	InterfaceToken::InterfaceToken(int fd, unsigned interfaceNumber): _fd(fd), _interfaceNumber(interfaceNumber)
	{
		IOCTL(_fd, USBDEVFS_CLAIMINTERFACE, &interfaceNumber);
	}

	InterfaceToken::~InterfaceToken()
	{
		ioctl(_fd, USBDEVFS_RELEASEINTERFACE, &_interfaceNumber);
	}

#define PRINT_CAP(CAP, NAME) \
	if (capabilities & (CAP)) \
	{ \
		printf(NAME " "); \
		capabilities &= ~(CAP); \
	}

	Device::Device(int fd, const EndpointPtr &controlEp): _fd(fd), _capabilities(0), _controlEp(controlEp)
	{
		try { IOCTL(_fd.Get(), USBDEVFS_GET_CAPABILITIES, &_capabilities); }
		catch(const std::exception &ex)
		{ fprintf(stderr, "get usbfs capabilities failed: %s\n", ex.what()); }
		printf("capabilities = 0x%02x: ", (unsigned)_capabilities);
		if (_capabilities)
		{
			u32 capabilities = _capabilities;
			PRINT_CAP(USBDEVFS_CAP_ZERO_PACKET, "<zero>");
			PRINT_CAP(USBDEVFS_CAP_BULK_CONTINUATION, "<bulk-continuation>");
			PRINT_CAP(USBDEVFS_CAP_NO_PACKET_SIZE_LIM, "<no-packet-size-limit>");
			PRINT_CAP(USBDEVFS_CAP_BULK_SCATTER_GATHER, "<bulk-scatter-gather>");
			PRINT_CAP(USBDEVFS_CAP_REAP_AFTER_DISCONNECT, "<reap-after-disconnect>");
			if (capabilities)
				printf("<unknown capability 0x%02x>", capabilities);
			printf("\n");
		}
		else
			printf("[none]\n");
	}

	Device::~Device()
	{ }

	int Device::GetConfiguration() const
	{
		return 0;
	}

	void Device::SetConfiguration(int idx)
	{
		fprintf(stderr, "SetConfiguration(%d): not implemented", idx);
	}

	struct Device::Urb : Noncopyable
	{
		static const int 		MaxBufferSize = 4096;
		int						Fd;
		int						PacketSize;
		ByteArray				Buffer;
		usbdevfs_urb			KernelUrb;

		Urb(int fd, u8 type, const EndpointPtr & ep): Fd(fd), PacketSize(ep->GetMaxPacketSize()), Buffer(std::max(PacketSize, MaxBufferSize / PacketSize * PacketSize)), KernelUrb()
		{
			KernelUrb.type			= type;
			KernelUrb.endpoint		= ep->GetAddress();
			KernelUrb.buffer		= Buffer.data();
			KernelUrb.buffer_length = Buffer.size();
		}

		size_t GetTransferSize() const
		{ return Buffer.size(); }

		void Submit()
		{
			IOCTL(Fd, USBDEVFS_SUBMITURB, &KernelUrb);
		}

		void Discard()
		{
			int r = ioctl(Fd, USBDEVFS_DISCARDURB, &KernelUrb);
			if (r != 0)
			{
				perror("ioctl(USBDEVFS_DISCARDURB)");
			}
		}

		size_t Send(const IObjectInputStreamPtr &inputStream)
		{
			size_t r = inputStream->Read(Buffer.data(), Buffer.size());
			//HexDump("write", ByteArray(Buffer.data(), Buffer.data() + r));
			KernelUrb.buffer_length = r;
			return r;
		}

		size_t Send(const ByteArray &inputData)
		{
			size_t r = std::min(Buffer.size(), inputData.size());
			std::copy(inputData.data(), inputData.data() + r, Buffer.data());
			KernelUrb.buffer_length = r;
			return r;
		}

		size_t Recv(const IObjectOutputStreamPtr &outputStream)
		{
			//HexDump("read", ByteArray(Buffer.data(), Buffer.data() + KernelUrb.actual_length));
			return outputStream->Write(Buffer.data(), KernelUrb.actual_length);
		}

		ByteArray Recv()
		{ return ByteArray(Buffer.begin(), Buffer.begin() + KernelUrb.actual_length); }

		template<unsigned Flag>
		void SetFlag(bool value)
		{
			if (value)
				KernelUrb.flags |= Flag;
			else
				KernelUrb.flags &= ~Flag;
		}

		void SetContinuationFlag(bool continuation)
		{ SetFlag<USBDEVFS_URB_BULK_CONTINUATION>(continuation); }

		void SetZeroPacketFlag(bool zero)
		{ SetFlag<USBDEVFS_URB_ZERO_PACKET>(zero); }
	};

	void * Device::Reap(int timeout)
	{
		timeval started = {};
		if (gettimeofday(&started, NULL) == -1)
			throw usb::Exception("gettimeofday");

		pollfd fd = {};
		fd.fd		= _fd.Get();
		fd.events	= POLLOUT;
		int r = poll(&fd, 1, timeout);

		timeval now = {};
		if (gettimeofday(&now, NULL) == -1)
			throw usb::Exception("gettimeofday");

		if (r < 0)
			throw Exception("poll");

		if (r == 0 && timeout > 0)
		{
			int ms = (now.tv_sec - started.tv_sec) * 1000 + (now.tv_usec - started.tv_usec) / 1000;
			fprintf(stderr, "%d ms since the last poll call\n", ms);
		}

		usbdevfs_urb *urb;
		r = ioctl(_fd.Get(), USBDEVFS_REAPURBNDELAY, &urb);
		if (r == 0)
			return urb;
		else if (errno == EAGAIN)
			throw TimeoutException("timeout reaping usb urb");
		else
			throw Exception("ioctl");
	}

	void Device::ClearHalt(const EndpointPtr & ep)
	{
		try
		{ unsigned index = ep->GetAddress(); IOCTL(_fd.Get(), USBDEVFS_CLEAR_HALT, &index); }
		catch(const std::exception &ex)
		{ fprintf(stderr, "clearing halt status for ep %02x: %s\n", (unsigned)ep->GetAddress(), ex.what()); }
	}

	void Device::Submit(const UrbPtr &urb, int timeout)
	{
		urb->Submit();
		{
			scoped_mutex_lock l(_mutex);
			_urbs.insert(std::make_pair(&urb->KernelUrb, urb));
		}
		try
		{
			while(true)
			{
				UrbPtr completedUrb;
				{
					void *completedKernelUrb = Reap(timeout);
					scoped_mutex_lock l(_mutex);
					auto urbIt = _urbs.find(completedKernelUrb);
					if (urbIt == _urbs.end())
					{
						fprintf(stderr, "got unknown urb: %p\n", completedKernelUrb);
						continue;
					}
					completedUrb = urbIt->second;
					_urbs.erase(urbIt);
				}
				if (completedUrb == urb)
					break;
			}
		}
		catch(const TimeoutException &ex)
		{
			urb->Discard();
			throw;
		}
		catch(const std::exception &ex)
		{
			fprintf(stderr, "error while submitting urb: %s\n", ex.what());
			urb->Discard();
			throw;
		}
	}

	void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout)
	{
		UrbPtr urb = std::make_shared<Urb>(_fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb->GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			r = urb->Send(inputStream);
			urb->SetZeroPacketFlag(r != transferSize);
			urb->SetContinuationFlag(continuation);
			continuation = true;
			Submit(urb, timeout);

			ProcessControl();
		}
		while(r == transferSize);
	}

	void Device::ReadBulk(const EndpointPtr & ep, const IObjectOutputStreamPtr &outputStream, int timeout)
	{
		UrbPtr urb = std::make_shared<Urb>(_fd.Get(), USBDEVFS_URB_TYPE_BULK, ep);
		size_t transferSize = urb->GetTransferSize();

		size_t r;
		bool continuation = false;
		do
		{
			urb->SetContinuationFlag(continuation);
			continuation = true;
			Submit(urb, timeout);

			ProcessControl();
			r = urb->Recv(outputStream);
		}
		while(r == transferSize);
	}

	u8 Device::TransactionType(const EndpointPtr &ep)
	{
		EndpointType type = ep->GetType();
		switch(type)
		{
		case EndpointType::Control:
			return USBDEVFS_URB_TYPE_CONTROL;
		case EndpointType::Isochronous:
			return USBDEVFS_URB_TYPE_ISO;
		case EndpointType::Bulk:
			return USBDEVFS_URB_TYPE_BULK;
		case EndpointType::Interrupt:
			return USBDEVFS_URB_TYPE_INTERRUPT;
		default:
			throw std::runtime_error("invalid endpoint type");
		}
	}

	void Device::ReadControl(u8 type, u8 req, u16 value, u16 index, ByteArray &data, int timeout)
	{
		printf("read control %02x %02x %04x %04x\n", type, req, value, index);
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
		if (r >= 0)
			data.resize(r);
		else if (errno == EAGAIN)
			throw TimeoutException("timeout sending control transfer");
		else
			throw Exception("ioctl");
	}

	void Device::WriteControl(u8 type, u8 req, u16 value, u16 index, const ByteArray &data, bool interruptCurrentTransaction, int timeout)
	{
		printf("write control %02x %02x %04x %04x\n", type, req, value, index);
		usbdevfs_ctrltransfer ctrl = { };
		ctrl.bRequestType = type;
		ctrl.bRequest = req;
		ctrl.wValue = value;
		ctrl.wIndex = index;
		ctrl.wLength = data.size();
		ctrl.data = const_cast<u8 *>(data.data());
		ctrl.timeout = timeout;

		int fd = _fd.Get();

		_controls.push(
		[fd, ctrl, interruptCurrentTransaction]()
		{
			int r = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
			if (r >= 0)
			{
				if (interruptCurrentTransaction)
					throw std::runtime_error("transaction aborted");
				else
					return;
			}
			else if (errno == EAGAIN)
				throw TimeoutException("timeout sending control transfer");
			else
				throw Exception("ioctl");
		});
	}

	void Device::ProcessControl()
	{
		scoped_mutex_lock l(_mutex);
		while(!_controls.empty())
		{
			try { _controls.front()(); } catch(const std::exception &ex) { _controls.pop(); throw; }
			_controls.pop();
		}
	}

}}
<|endoftext|>


<commit_before>#include <string>
#include <iostream>
#include <getopt.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <sysexits.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <stdexcept>
#include <boost/ptr_container/ptr_list.hpp>
#include <ev.h>
#include <memory>

#include "Socket.hpp"
#include "utils/TimestampLog.hpp"
#include "utils/output.hpp"
#include "VelbusMessage/VelbusMessage.hpp"

static const size_t READ_SIZE = 4096;
static const int MAX_CONN_BACKLOG = 32;

class IOError : public std::runtime_error {
public:
	IOError( const std::string &what ) :
	    std::runtime_error( what ) {}
};
class EOFreached : public std::runtime_error {
public:
	EOFreached() :
	    std::runtime_error( std::string("EOF reached") ) {}
};

struct connection {
	Socket sock;
	std::string buf;
	std::string id;
	ev_io watcher;
};

std::auto_ptr<std::ostream> log;
Socket s_listen;
struct connection c_serial;
boost::ptr_list< struct connection > c_network;

std::string read(int from) throw(IOError, EOFreached) {
	char buf[READ_SIZE];
	int n = read(from, buf, sizeof(buf));
	if( n == -1 ) {
		char error_descr[256];
		strerror_r(errno, error_descr, sizeof(error_descr));
		std::string e;
		e = "Could not read(): ";
		e.append(error_descr);
		throw IOError( e );
	} else if( n == 0 ) {
		throw EOFreached();
	}
	return std::string(buf, n);
}

void write(int to, std::string const &what) throw(IOError) {
	int rv = write(to, what.data(), what.length());
	if( rv == -1 ) {
		char error_descr[256];
		strerror_r(errno, error_descr, sizeof(error_descr));
		std::string e;
		e = "Could not write(): ";
		e.append(error_descr);
		throw IOError( e );
	} else if( rv != what.length() ) {
		throw IOError( "Not enough bytes written" );
	}
}

void kill_connection(EV_P_ ev_io *w) {
	// Remove from event loop
	ev_io_stop(EV_A_ w );

	// Find and erase this connection in the list
	for( typeof(c_network.begin()) i = c_network.begin(); i != c_network.end(); ++i ) {
		if( &(i->watcher) == w ) {
			c_network.erase(i);
			break; // Stop searching
		}
	}
}

void serial_ready_to_read(EV_P_ ev_io *w, int revents) throw() {
	std::string buf;
	try {
		buf = read(c_serial.sock);
	} catch( IOError &e ) {
		*log << c_serial.id << " : IO error: " << e.what() << "\n" << std::flush;
		throw;
	} catch( EOFreached &e ) {
		*log << c_serial.id << " : EOF reached\n" << std::flush;
		throw;
	}

	c_serial.buf.append(buf);
	while(1) {
		std::auto_ptr<VelbusMessage::VelbusMessage> m;
		try {
			m.reset( VelbusMessage::parse_and_consume(c_serial.buf) );
			*log << c_serial.id << " : " << m->string() << "\n" << std::flush;

		} catch( VelbusMessage::InsufficientData &e ) {
			break; // out of while, and wait for more data
		} catch( VelbusMessage::FormError &e ) {
			*log << c_serial.id << " : Form Error in data, ignoring byte "
			     << "0x" << hex(c_serial.buf[0]) << "\n" << std::flush;
			c_serial.buf = c_serial.buf.substr(1);
		}

		for( typeof(c_network.begin()) i = c_network.begin(); i != c_network.end(); ++i ) {
			try {
				write(i->sock, m->message());
				// TODO: maybe write in non-blocking mode?
			} catch( IOError &e ) {
				*log << i->id << " : IO error, closing connection: " << e.what() << "\n" << std::flush;
				kill_connection(EV_A_ w);
			}
		}
	}
}

void socket_ready_to_read(EV_P_ ev_io *w, int revents) throw() {
	struct connection *c = reinterpret_cast<struct connection*>(w->data);
	std::string buf;
	try {
		buf = read(c->sock);

	} catch( IOError &e ) {
		*log << c->id << " : IO error, closing connection: " << e.what() << "\n" << std::flush;
		kill_connection(EV_A_ w);
		return; // early

	} catch( EOFreached &e ) {
		*log << c->id << " : Disconnect\n" << std::flush;
		kill_connection(EV_A_ w);
		return; // early
	}

	c->buf.append(buf);
	while(1) {
		std::auto_ptr<VelbusMessage::VelbusMessage> m;
		try {
			m.reset( VelbusMessage::parse_and_consume(c->buf) );
			*log << c->id << " : " << m->string() << "\n" << std::flush;

		} catch( VelbusMessage::InsufficientData &e ) {
			break; // out of while, and wait for more data
		} catch( VelbusMessage::FormError &e ) {
			*log << c->id << " : Form Error in data, ignoring byte "
			     << "0x" << hex(c->buf[0]) << "\n" << std::flush;
			c->buf = c->buf.substr(1);
		}

		try {
			write(c_serial.sock, m->message());
		} catch( IOError &e ) {
			throw;
		}
		for( typeof(c_network.begin()) i = c_network.begin(); i != c_network.end(); ++i ) {
			if( &(*i) == c ) continue; // Don't loop input to same socket
			try {
				write(i->sock, m->message());
			} catch( IOError &e ) {
				*log << i->id << " : IO error, closing connection: " << e.what() << "\n" << std::flush;
				kill_connection(EV_A_ w);
			}
		}
	}
}

void incomming_connection(EV_P_ ev_io *w, int revents) {
	std::auto_ptr<struct connection> new_con( new struct connection );
	std::auto_ptr<SockAddr::SockAddr> client_addr;
	new_con->sock = s_listen.accept(&client_addr);
	new_con->id = client_addr->string();
	*log << new_con->id << " : Connection opened\n" << std::flush;

	ev_io_init( &new_con->watcher, socket_ready_to_read, new_con->sock, EV_READ );
	new_con->watcher.data = new_con.get();
	ev_io_start( EV_A_ &new_con->watcher );

	c_network.push_back( new_con.release() );
}

int main(int argc, char* argv[]) {
	// Defaults
	std::string serial_port("/dev/ttyS0");
	std::string bind_addr("[::1]:8445");

	log.reset( new TimestampLog( std::cerr ) );

	{ // Parse options
		char optstring[] = "?hs:b:";
		struct option longopts[] = {
			{"help",			no_argument, NULL, '?'},
			{"serialport",		required_argument, NULL, 's'},
			{"bind",			required_argument, NULL, 'b'},
			{NULL, 0, 0, 0}
		};
		int longindex;
		int opt;
		while( (opt = getopt_long(argc, argv, optstring, longopts, &longindex)) != -1 ) {
			switch(opt) {
			case '?':
			case 'h':
				std::cerr <<
				//  >---------------------- Standard terminal width ---------------------------------<
					"Options:\n"
					"  -h -? --help                    Displays this help message and exits\n"
					"  --serialport -s  /dev/ttyS0     The serial device to use\n"
					"  --bind -b host:port             Bind to the specified address\n"
					"                                  host and port resolving can be bypassed by\n"
					"                                  placing [] around them\n"
					;
				exit(EX_USAGE);
			case 's':
				serial_port = optarg;
				break;
			case 'b':
				bind_addr = optarg;
				break;
			}
		}
	}

	{ // Open serial port
		c_serial.id = "SERIAL";
		c_serial.sock = open(serial_port.c_str(), O_RDWR | O_NOCTTY);
		// Open in Read-Write; don't become controlling TTY
		if( c_serial.sock == -1 ) {
			std::cerr << "Could not open \"" << serial_port << "\": ";
			perror("open()");
			exit(EX_NOINPUT);
		}
		*log << "Opened port \"" << serial_port << "\"\n" << std::flush;

		// Setting up port
		struct termios options;
		tcgetattr(c_serial.sock, &options);

		cfsetispeed(&options, B38400);
		cfsetospeed(&options, B38400);

		options.c_cflag |= CLOCAL; // Don't change owner of port
		options.c_cflag |= CREAD; // Enable receiver

		options.c_cflag &= ~CSIZE; // Mask the character size bits
		options.c_cflag |= CS8;    // Select 8 data bits

		options.c_cflag &= ~PARENB; // Clear parity
		options.c_cflag &= ~CSTOPB; // Clear "2 Stopbits" => 1 stopbit

		options.c_cflag &= ~CRTSCTS; // Disable hardware flow control

		options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Raw mode

		options.c_iflag |= IGNPAR; // Ignore parity (since none is used)
		options.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control

		options.c_oflag &= ~OPOST; // Disable output processing = raw mode

		// Apply options
		tcsetattr(c_serial.sock, TCSANOW, &options);

		// Manually setting RTS high & DTR low
		int status;
		ioctl(c_serial.sock, TIOCMGET, &status); // Get MODEM-bits
		status &= ~TIOCM_DTR; // DTR = 0
		status |= TIOCM_RTS; // RTS = 1
		ioctl(c_serial.sock, TIOCMSET, &status); // Write MODEM-bits

		*log << "Configured port \"" << serial_port << "\"\n" << std::flush;
	}

	{ // Open listening socket
		std::string host, port;

		/* Address format is
		 *   - hostname:portname
		 *   - [numeric ip]:portname
		 *   - hostname:[portnumber]
		 *   - [numeric ip:[portnumber]
		 */
		size_t c = bind_addr.rfind(":");
		if( c == std::string::npos ) {
			std::cerr << "Invalid bind string \"" << bind_addr << "\": could not find ':'\n";
			exit(EX_DATAERR);
		}
		host = bind_addr.substr(0, c);
		port = bind_addr.substr(c+1);

		std::auto_ptr< boost::ptr_vector< SockAddr::SockAddr> > bind_sa
			= SockAddr::resolve( host, port, 0, SOCK_STREAM, 0);
		if( bind_sa->size() == 0 ) {
			std::cerr << "Can not bind to \"" << bind_addr << "\": Could not resolve\n";
			exit(EX_DATAERR);
		} else if( bind_sa->size() > 1 ) {
			// TODO: allow this
			std::cerr << "Can not bind to \"" << bind_addr << "\": Resolves to multiple entries:\n";
			for( typeof(bind_sa->begin()) i = bind_sa->begin(); i != bind_sa->end(); i++ ) {
				std::cerr << "  " << i->string() << "\n";
			}
			exit(EX_DATAERR);
		}
		s_listen = Socket::socket( (*bind_sa)[0].proto_family() , SOCK_STREAM, 0);
		s_listen.set_reuseaddr();
		s_listen.bind((*bind_sa)[0]);
		s_listen.listen(MAX_CONN_BACKLOG);
		*log << "Listening on " << (*bind_sa)[0].string() << "\n" << std::flush;
	}

	{
		ev_io_init( &c_serial.watcher, serial_ready_to_read, c_serial.sock, EV_READ );
		ev_io_start( EV_DEFAULT_ &c_serial.watcher );

		ev_io e_listen;
		ev_io_init( &e_listen, incomming_connection, s_listen, EV_READ );
		ev_io_start( EV_DEFAULT_ &e_listen );

		*log << "Setup done, starting event loop\n" << std::flush;

		ev_loop(EV_DEFAULT_ 0);
	}

	return 0;
}
<commit_msg>Added SIGINT handling<commit_after>#include <string>
#include <iostream>
#include <getopt.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <sysexits.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>
#include <stdexcept>
#include <boost/ptr_container/ptr_list.hpp>
#include <ev.h>
#include <memory>

#include "Socket.hpp"
#include "utils/TimestampLog.hpp"
#include "utils/output.hpp"
#include "VelbusMessage/VelbusMessage.hpp"

static const size_t READ_SIZE = 4096;
static const int MAX_CONN_BACKLOG = 32;

class IOError : public std::runtime_error {
public:
	IOError( const std::string &what ) :
	    std::runtime_error( what ) {}
};
class EOFreached : public std::runtime_error {
public:
	EOFreached() :
	    std::runtime_error( std::string("EOF reached") ) {}
};

struct connection {
	Socket sock;
	std::string buf;
	std::string id;
	ev_io watcher;
};

std::auto_ptr<std::ostream> log;
Socket s_listen;
struct connection c_serial;
boost::ptr_list< struct connection > c_network;

std::string read(int from) throw(IOError, EOFreached) {
	char buf[READ_SIZE];
	int n = read(from, buf, sizeof(buf));
	if( n == -1 ) {
		char error_descr[256];
		strerror_r(errno, error_descr, sizeof(error_descr));
		std::string e;
		e = "Could not read(): ";
		e.append(error_descr);
		throw IOError( e );
	} else if( n == 0 ) {
		throw EOFreached();
	}
	return std::string(buf, n);
}

void write(int to, std::string const &what) throw(IOError) {
	int rv = write(to, what.data(), what.length());
	if( rv == -1 ) {
		char error_descr[256];
		strerror_r(errno, error_descr, sizeof(error_descr));
		std::string e;
		e = "Could not write(): ";
		e.append(error_descr);
		throw IOError( e );
	} else if( rv != what.length() ) {
		throw IOError( "Not enough bytes written" );
	}
}

void kill_connection(EV_P_ ev_io *w) {
	// Remove from event loop
	ev_io_stop(EV_A_ w );

	// Find and erase this connection in the list
	for( typeof(c_network.begin()) i = c_network.begin(); i != c_network.end(); ++i ) {
		if( &(i->watcher) == w ) {
			c_network.erase(i);
			break; // Stop searching
		}
	}
}

void received_sigint(EV_P_ ev_signal *w, int revents) throw() {
	*log << "Received SIGINT, exiting\n" << std::flush;
	ev_unloop(EV_A_ EVUNLOOP_ALL);
}

void serial_ready_to_read(EV_P_ ev_io *w, int revents) throw() {
	std::string buf;
	try {
		buf = read(c_serial.sock);
	} catch( IOError &e ) {
		*log << c_serial.id << " : IO error: " << e.what() << "\n" << std::flush;
		throw;
	} catch( EOFreached &e ) {
		*log << c_serial.id << " : EOF reached\n" << std::flush;
		throw;
	}

	c_serial.buf.append(buf);
	while(1) {
		std::auto_ptr<VelbusMessage::VelbusMessage> m;
		try {
			m.reset( VelbusMessage::parse_and_consume(c_serial.buf) );
			*log << c_serial.id << " : " << m->string() << "\n" << std::flush;

		} catch( VelbusMessage::InsufficientData &e ) {
			break; // out of while, and wait for more data
		} catch( VelbusMessage::FormError &e ) {
			*log << c_serial.id << " : Form Error in data, ignoring byte "
			     << "0x" << hex(c_serial.buf[0]) << "\n" << std::flush;
			c_serial.buf = c_serial.buf.substr(1);
		}

		for( typeof(c_network.begin()) i = c_network.begin(); i != c_network.end(); ++i ) {
			try {
				write(i->sock, m->message());
				// TODO: maybe write in non-blocking mode?
			} catch( IOError &e ) {
				*log << i->id << " : IO error, closing connection: " << e.what() << "\n" << std::flush;
				kill_connection(EV_A_ w);
			}
		}
	}
}

void socket_ready_to_read(EV_P_ ev_io *w, int revents) throw() {
	struct connection *c = reinterpret_cast<struct connection*>(w->data);
	std::string buf;
	try {
		buf = read(c->sock);

	} catch( IOError &e ) {
		*log << c->id << " : IO error, closing connection: " << e.what() << "\n" << std::flush;
		kill_connection(EV_A_ w);
		return; // early

	} catch( EOFreached &e ) {
		*log << c->id << " : Disconnect\n" << std::flush;
		kill_connection(EV_A_ w);
		return; // early
	}

	c->buf.append(buf);
	while(1) {
		std::auto_ptr<VelbusMessage::VelbusMessage> m;
		try {
			m.reset( VelbusMessage::parse_and_consume(c->buf) );
			*log << c->id << " : " << m->string() << "\n" << std::flush;

		} catch( VelbusMessage::InsufficientData &e ) {
			break; // out of while, and wait for more data
		} catch( VelbusMessage::FormError &e ) {
			*log << c->id << " : Form Error in data, ignoring byte "
			     << "0x" << hex(c->buf[0]) << "\n" << std::flush;
			c->buf = c->buf.substr(1);
		}

		try {
			write(c_serial.sock, m->message());
		} catch( IOError &e ) {
			throw;
		}
		for( typeof(c_network.begin()) i = c_network.begin(); i != c_network.end(); ++i ) {
			if( &(*i) == c ) continue; // Don't loop input to same socket
			try {
				write(i->sock, m->message());
			} catch( IOError &e ) {
				*log << i->id << " : IO error, closing connection: " << e.what() << "\n" << std::flush;
				kill_connection(EV_A_ w);
			}
		}
	}
}

void incomming_connection(EV_P_ ev_io *w, int revents) {
	std::auto_ptr<struct connection> new_con( new struct connection );
	std::auto_ptr<SockAddr::SockAddr> client_addr;
	new_con->sock = s_listen.accept(&client_addr);
	new_con->id = client_addr->string();
	*log << new_con->id << " : Connection opened\n" << std::flush;

	ev_io_init( &new_con->watcher, socket_ready_to_read, new_con->sock, EV_READ );
	new_con->watcher.data = new_con.get();
	ev_io_start( EV_A_ &new_con->watcher );

	c_network.push_back( new_con.release() );
}

int main(int argc, char* argv[]) {
	// Defaults
	std::string serial_port("/dev/ttyS0");
	std::string bind_addr("[::1]:8445");

	log.reset( new TimestampLog( std::cerr ) );

	{ // Parse options
		char optstring[] = "?hs:b:";
		struct option longopts[] = {
			{"help",			no_argument, NULL, '?'},
			{"serialport",		required_argument, NULL, 's'},
			{"bind",			required_argument, NULL, 'b'},
			{NULL, 0, 0, 0}
		};
		int longindex;
		int opt;
		while( (opt = getopt_long(argc, argv, optstring, longopts, &longindex)) != -1 ) {
			switch(opt) {
			case '?':
			case 'h':
				std::cerr <<
				//  >---------------------- Standard terminal width ---------------------------------<
					"Options:\n"
					"  -h -? --help                    Displays this help message and exits\n"
					"  --serialport -s  /dev/ttyS0     The serial device to use\n"
					"  --bind -b host:port             Bind to the specified address\n"
					"                                  host and port resolving can be bypassed by\n"
					"                                  placing [] around them\n"
					;
				exit(EX_USAGE);
			case 's':
				serial_port = optarg;
				break;
			case 'b':
				bind_addr = optarg;
				break;
			}
		}
	}

	{ // Open serial port
		c_serial.id = "SERIAL";
		c_serial.sock = open(serial_port.c_str(), O_RDWR | O_NOCTTY);
		// Open in Read-Write; don't become controlling TTY
		if( c_serial.sock == -1 ) {
			std::cerr << "Could not open \"" << serial_port << "\": ";
			perror("open()");
			exit(EX_NOINPUT);
		}
		*log << "Opened port \"" << serial_port << "\"\n" << std::flush;

		// Setting up port
		struct termios options;
		tcgetattr(c_serial.sock, &options);

		cfsetispeed(&options, B38400);
		cfsetospeed(&options, B38400);

		options.c_cflag |= CLOCAL; // Don't change owner of port
		options.c_cflag |= CREAD; // Enable receiver

		options.c_cflag &= ~CSIZE; // Mask the character size bits
		options.c_cflag |= CS8;    // Select 8 data bits

		options.c_cflag &= ~PARENB; // Clear parity
		options.c_cflag &= ~CSTOPB; // Clear "2 Stopbits" => 1 stopbit

		options.c_cflag &= ~CRTSCTS; // Disable hardware flow control

		options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Raw mode

		options.c_iflag |= IGNPAR; // Ignore parity (since none is used)
		options.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control

		options.c_oflag &= ~OPOST; // Disable output processing = raw mode

		// Apply options
		tcsetattr(c_serial.sock, TCSANOW, &options);

		// Manually setting RTS high & DTR low
		int status;
		ioctl(c_serial.sock, TIOCMGET, &status); // Get MODEM-bits
		status &= ~TIOCM_DTR; // DTR = 0
		status |= TIOCM_RTS; // RTS = 1
		ioctl(c_serial.sock, TIOCMSET, &status); // Write MODEM-bits

		*log << "Configured port \"" << serial_port << "\"\n" << std::flush;
	}

	{ // Open listening socket
		std::string host, port;

		/* Address format is
		 *   - hostname:portname
		 *   - [numeric ip]:portname
		 *   - hostname:[portnumber]
		 *   - [numeric ip:[portnumber]
		 */
		size_t c = bind_addr.rfind(":");
		if( c == std::string::npos ) {
			std::cerr << "Invalid bind string \"" << bind_addr << "\": could not find ':'\n";
			exit(EX_DATAERR);
		}
		host = bind_addr.substr(0, c);
		port = bind_addr.substr(c+1);

		std::auto_ptr< boost::ptr_vector< SockAddr::SockAddr> > bind_sa
			= SockAddr::resolve( host, port, 0, SOCK_STREAM, 0);
		if( bind_sa->size() == 0 ) {
			std::cerr << "Can not bind to \"" << bind_addr << "\": Could not resolve\n";
			exit(EX_DATAERR);
		} else if( bind_sa->size() > 1 ) {
			// TODO: allow this
			std::cerr << "Can not bind to \"" << bind_addr << "\": Resolves to multiple entries:\n";
			for( typeof(bind_sa->begin()) i = bind_sa->begin(); i != bind_sa->end(); i++ ) {
				std::cerr << "  " << i->string() << "\n";
			}
			exit(EX_DATAERR);
		}
		s_listen = Socket::socket( (*bind_sa)[0].proto_family() , SOCK_STREAM, 0);
		s_listen.set_reuseaddr();
		s_listen.bind((*bind_sa)[0]);
		s_listen.listen(MAX_CONN_BACKLOG);
		*log << "Listening on " << (*bind_sa)[0].string() << "\n" << std::flush;
	}

	{
		ev_signal ev_signal_watcher;
		ev_signal_init( &ev_signal_watcher, received_sigint, SIGINT);
		ev_signal_start( EV_DEFAULT_ &ev_signal_watcher);

		ev_io_init( &c_serial.watcher, serial_ready_to_read, c_serial.sock, EV_READ );
		ev_io_start( EV_DEFAULT_ &c_serial.watcher );

		ev_io e_listen;
		ev_io_init( &e_listen, incomming_connection, s_listen, EV_READ );
		ev_io_start( EV_DEFAULT_ &e_listen );

		*log << "Setup done, starting event loop\n" << std::flush;

		ev_loop(EV_DEFAULT_ 0);
	}

	return 0;
}
<|endoftext|>


<commit_before>#include <io/FileCollection.hpp>

#include <sys/poll.h>
#include <unistd.h>

using namespace std;
using namespace logger;
using namespace lang;

#define HAS_FIONREAD

#ifdef HAS_FIONREAD
#include <fcntl.h>
#include <sys/ioctl.h>
#endif

namespace io {

FileCollection::FileCollection() {
    pipe( m_pipe );
    m_map[m_pipe[0]] = NULL;
    m_log = &LogManager::instance().getLogContext("IO", "FileCollection::Static");
}

void FileCollection::interrupt() {
    m_log->printfln(DEBUG, "Interrupt called");
    byte bytes[1];
	bytes[0] = 0;
    write(m_pipe[1], bytes, 1);
}

void FileCollection::subscribeForRead( int fd, FileCollectionObserver* observer,
    Deallocator<FileCollectionObserver>* dealloc ) {
    m_log->printfln(DEBUG, "Subscribing new file descriptor %d with observer %p for read", fd, observer);

    m_map[fd] = observer;
    if ( dealloc ) {
        m_deallocators[observer] = dealloc;
    }

    interrupt();
}

void FileCollection::subscribeForWrite( int fd, FileCollectionObserver* observer,
       dealloc_T* dealloc ) {

    m_log->printfln(DEBUG, "SubscrIbing new file descriptor %d with observer %p for write", fd, observer);
    m_map[fd] = observer;
    if( dealloc ) {
        m_deallocators[observer] = dealloc;
    }
    interrupt();
}

void FileCollection::fireEvent( int fd, int events ) {
    m_log->printfln(DEBUG, "FireEvent for file descriptor %d events=%d,%d,%d|%d,%d,%d", fd,
        !!(events&POLLIN),
        !!(events&POLLOUT),
        !!(events&POLLPRI),

        !!(events&POLLERR),
        !!(events&POLLHUP),
        !!(events&POLLNVAL)
    );

    FileCollectionObserver* obs = m_map[fd];
    if( obs ) {
        obs->observe(fd, events);
    }
}

void FileCollection::run() {
    LogContext& log = LogManager::instance().getLogContext("IO", "FileCollection::Run");
    log.printfln(DEBUG, "Start FileCollection::run");

    for( ; ; ) {
        vector<struct pollfd> poll_data;
        map<int, FileCollectionObserver* >::iterator itr;

        /* Add the poll data from the reading map */
        for( itr = m_map.begin() ; itr != m_map.end() ; ++ itr ) {
            log.printfln(TRACE,"Adding for input set fd %d", itr->first);

            struct pollfd pfd;
            pfd.revents = 0;
            pfd.fd = itr->first;
            pfd.events = POLLIN | POLLPRI;
            poll_data.push_back(pfd);
        }

        /* Add all the data from the writing map */
        for( itr = m_write_map.begin(); itr != m_write_map.end() ; ++ itr ) {
            log.printfln(TRACE,"Adding for output set fd %d", itr->first);

            struct pollfd pfd;
            pfd.revents = 0;
            pfd.fd = itr->first;
            pfd.events = POLLOUT;
            poll_data.push_back(pfd);
        }

        int rc = poll(&poll_data[0], poll_data.size(), 5000);

        if( rc == -1 ) {
            throw CException("Error in poll", rc);
        }

        if( rc != 0 ) {
            log.printfln(DEBUG, "Events detected");
            vector<struct pollfd>::iterator vitr;

            for( vitr = poll_data.begin(); vitr != poll_data.end(); ++ vitr ) {
                if( vitr->revents != 0 ) {

                    if( vitr->fd == m_pipe[0] ) {
                        /* There was an interrupt, throw away the
                         * data */
                        log.printfln(DEBUG, "There is an interrupt");
                        byte bytes[1024];
                        read(m_pipe[0], bytes, 1024);
                    } else {
                        /* Fire the event for the correct file descriptor */
                        if( vitr->revents & POLLHUP ) {

                            /* There was a hangup, we need to remove the
                             * file descriptor */
                            log.printfln(DEBUG, "Unsubscribing hungup file descriptor %d", vitr->fd);
                            unsubscribe( vitr->fd );
                        } else {
#ifdef  HAS_FIONREAD
                            long n_read = 1;
                            int err;
                            if( (err = ioctl( vitr->fd, FIONREAD, &n_read )) ){
                                log.printfln(WARN, "ioctl failed: %s", strerror(err));
                            }
                            if( n_read > 0 ) {
#endif
                                /* This file descriptor had an event.
                                 * Fire that event */
                                fireEvent(vitr->fd, vitr->revents);
#ifdef  HAS_FIONREAD
                            } else {
                                log.printfln(DEBUG, "Unsubscrbing %d due to EOF", vitr->fd);
                            }
#endif
                        }
                    }

                }
            }
        }

    }
}

bool FileCollection::unsubscribe(int fd, map_T& a_map) {
    map_T::iterator itr = a_map.find(fd);

    if( itr == a_map.end() ) {
        return false;
    } else {
        FileCollectionObserver* observer = itr->second;
        m_map.erase(itr);
        dealloc_map_T::iterator ditr = m_deallocators.find(observer);

        if( ditr != m_deallocators.end() ) {
            ditr->second->deallocate(observer);
            m_deallocators.erase(ditr);
        }
    }

    return true;
}

FileCollection::~FileCollection() {
    close(m_pipe[0]);
    close(m_pipe[1]);
}

}
<commit_msg>patch #2 for FileColelction<commit_after>#include <io/FileCollection.hpp>

#include <sys/poll.h>
#include <unistd.h>

using namespace std;
using namespace logger;
using namespace lang;

#define HAS_FIONREAD

#ifdef HAS_FIONREAD
#include <fcntl.h>
#include <sys/ioctl.h>
#endif

namespace io {

FileCollection::FileCollection() {
    pipe( m_pipe );
    m_map[m_pipe[0]] = NULL;
    m_log = &LogManager::instance().getLogContext("IO", "FileCollection::Static");
}

void FileCollection::interrupt() {
    m_log->printfln(DEBUG, "Interrupt called");
    byte bytes[1];
	bytes[0] = 0;
    write(m_pipe[1], bytes, 1);
}

void FileCollection::subscribeForRead( int fd, FileCollectionObserver* observer,
    Deallocator<FileCollectionObserver>* dealloc ) {
    m_log->printfln(DEBUG, "Subscribing new file descriptor %d with observer %p for read", fd, observer);

    m_map[fd] = observer;
    if ( dealloc ) {
        m_deallocators[observer] = dealloc;
    }

    interrupt();
}

void FileCollection::subscribeForWrite( int fd, FileCollectionObserver* observer,
       dealloc_T* dealloc ) {

    m_log->printfln(DEBUG, "SubscrIbing new file descriptor %d with observer %p for write", fd, observer);
    m_map[fd] = observer;
    if( dealloc ) {
        m_deallocators[observer] = dealloc;
    }
    interrupt();
}

void FileCollection::fireEvent( int fd, int events ) {
    m_log->printfln(DEBUG, "FireEvent for file descriptor %d events=%d,%d,%d|%d,%d,%d", fd,
        !!(events&POLLIN),
        !!(events&POLLOUT),
        !!(events&POLLPRI),

        !!(events&POLLERR),
        !!(events&POLLHUP),
        !!(events&POLLNVAL)
    );

    FileCollectionObserver* obs = m_map[fd];
    if( obs ) {
        obs->observe(fd, events);
    }
}

void FileCollection::run() {
    LogContext& log = LogManager::instance().getLogContext("IO", "FileCollection::Run");
    log.printfln(DEBUG, "Start FileCollection::run");

    for( ; ; ) {
        vector<struct pollfd> poll_data;
        map<int, FileCollectionObserver* >::iterator itr;

        /* Add the poll data from the reading map */
        for( itr = m_map.begin() ; itr != m_map.end() ; ++ itr ) {
            log.printfln(TRACE,"Adding for input set fd %d", itr->first);

            struct pollfd pfd;
            pfd.revents = 0;
            pfd.fd = itr->first;
            pfd.events = POLLIN | POLLPRI;
            poll_data.push_back(pfd);
        }

        /* Add all the data from the writing map */
        for( itr = m_write_map.begin(); itr != m_write_map.end() ; ++ itr ) {
            log.printfln(TRACE,"Adding for output set fd %d", itr->first);

            struct pollfd pfd;
            pfd.revents = 0;
            pfd.fd = itr->first;
            pfd.events = POLLOUT;
            poll_data.push_back(pfd);
        }

        int rc = poll(&poll_data[0], poll_data.size(), 5000);

        if( rc == -1 ) {
            throw CException("Error in poll", rc);
        }

        if( rc != 0 ) {
            log.printfln(DEBUG, "Events detected");
            vector<struct pollfd>::iterator vitr;

            for( vitr = poll_data.begin(); vitr != poll_data.end(); ++ vitr ) {
                if( vitr->revents != 0 ) {

                    if( vitr->fd == m_pipe[0] ) {
                        /* There was an interrupt, throw away the
                         * data */
                        log.printfln(DEBUG, "There is an interrupt");
                        byte bytes[1024];
                        read(m_pipe[0], bytes, 1024);
                    } else {
                        /* Fire the event for the correct file descriptor */
                        if( vitr->revents & POLLHUP ) {

                            /* There was a hangup, we need to remove the
                             * file descriptor */
                            log.printfln(DEBUG, "Unsubscribing hungup file descriptor %d", vitr->fd);
                            unsubscribe( vitr->fd );
                        } else {
#ifdef  HAS_FIONREAD
                            long n_read = 1;
                            int err = 0;
                            if( (err = ioctl( vitr->fd, FIONREAD, &n_read )) ){
                                log.printfln(WARN, "ioctl failed: %s", strerror(err));
                            }
                            if( n_read > 0 ) {
#endif
                                /* This file descriptor had an event.
                                 * Fire that event */
                                fireEvent(vitr->fd, vitr->revents);
#ifdef  HAS_FIONREAD
                            } else {
                                log.printfln(DEBUG, "Unsubscrbing %d due to EOF", vitr->fd);
                                unsubscribe( vitr->fd );
                            }
#endif
                        }
                    }

                }
            }
        }

    }
}

bool FileCollection::unsubscribe(int fd, map_T& a_map) {
    map_T::iterator itr = a_map.find(fd);

    if( itr == a_map.end() ) {
        return false;
    } else {
        FileCollectionObserver* observer = itr->second;
        m_map.erase(itr);
        dealloc_map_T::iterator ditr = m_deallocators.find(observer);

        if( ditr != m_deallocators.end() ) {
            ditr->second->deallocate(observer);
            m_deallocators.erase(ditr);
        }
    }

    return true;
}

FileCollection::~FileCollection() {
    close(m_pipe[0]);
    close(m_pipe[1]);
}

}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <iostream>
#include <iterator>
#include <vector>

#include "elang/shell/compiler.h"

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "elang/base/zone_allocated.h"
#include "elang/cg/generator.h"
#include "elang/compiler/analysis/name_resolver.h"
#include "elang/compiler/ast/class.h"
#include "elang/compiler/ast/factory.h"
#include "elang/compiler/ast/method.h"
#include "elang/compiler/ast/namespace.h"
#include "elang/compiler/compilation_session.h"
#include "elang/compiler/compilation_unit.h"
#include "elang/compiler/ir/factory.h"
#include "elang/compiler/ir/nodes.h"
#include "elang/compiler/public/compiler_error_code.h"
#include "elang/compiler/public/compiler_error_data.h"
#include "elang/compiler/semantics.h"
#include "elang/compiler/source_code.h"
#include "elang/compiler/source_code_position.h"
#include "elang/compiler/syntax/parser.h"
#include "elang/compiler/token_type.h"
#include "elang/hir/factory.h"
#include "elang/hir/factory_config.h"
#include "elang/hir/formatters/text_formatter.h"
#include "elang/hir/types.h"
#include "elang/hir/type_factory.h"
#include "elang/hir/values.h"
#include "elang/lir/factory.h"
#include "elang/lir/formatters/text_formatter.h"
#include "elang/shell/namespace_builder.h"
#include "elang/shell/node_query.h"
#include "elang/shell/source_file_stream.h"

namespace elang {
namespace compiler {
namespace shell {

namespace {

//////////////////////////////////////////////////////////////////////
//
// ReadableErrorData
//
struct ReadableErrorData {
  const ErrorData* error_data;

  explicit ReadableErrorData(const ErrorData& data) : error_data(&data) {}
};

std::ostream& operator<<(std::ostream& ostream,
                         const ReadableErrorData& readable) {
  static const char* const mnemonics[] = {
#define V(category, subcategory, name) #category "." #subcategory "." #name,
      FOR_EACH_COMPILER_ERROR_CODE(V, V)
#undef V
  };
  auto const it = std::begin(mnemonics) +
                  static_cast<size_t>(readable.error_data->error_code());
  ostream << (it < std::end(mnemonics) ? *it : "InvalidErrorCode");
  for (auto const token : readable.error_data->tokens())
    ostream << " " << token;
  return ostream;
}

//////////////////////////////////////////////////////////////////////
//
// FileSourceCode
//
class FileSourceCode final : public ::elang::compiler::SourceCode,
                             public ZoneAllocated {
 public:
  explicit FileSourceCode(const base::FilePath& file_path);
  ~FileSourceCode() = default;

  const base::FilePath& file_path() const { return stream().file_path(); }
  const SourceFileStream& stream() const { return *stream_; }

 private:
  // elang::compiler::SourceCode
  ::elang::compiler::CharacterStream* GetStream() final;

  base::FilePath file_path_;
  std::unique_ptr<SourceFileStream> stream_;

  DISALLOW_COPY_AND_ASSIGN(FileSourceCode);
};

FileSourceCode::FileSourceCode(const base::FilePath& file_path)
    : SourceCode(file_path.value()), stream_(new SourceFileStream(file_path)) {
}

::elang::compiler::CharacterStream* FileSourceCode::GetStream() {
  return stream_.get();
}

std::unique_ptr<hir::FactoryConfig> NewFactoryConfig(
    CompilationSession* session) {
  auto config = std::make_unique<hir::FactoryConfig>();
  config->atomic_string_factory = session->atomic_string_factory();
  config->string_type_name = session->NewAtomicString(L"System.String");
  return config;
}

// TODO(eval1749) We should load "System" namespace from file instead of
// building here.
void PopulateNamespace(NameResolver* name_resolver) {
  NamespaceBuilder builder(name_resolver);

  builder.NewClass("Object", "");
  builder.NewClass("ValueType", "Object");
  builder.NewClass("Enum", "ValueType");

  builder.NewClass("Bool", "ValueType");
  builder.NewClass("Char", "ValueType");
  builder.NewClass("Float32", "ValueType");
  builder.NewClass("Float64", "ValueType");
  builder.NewClass("Int16", "ValueType");
  builder.NewClass("Int32", "ValueType");
  builder.NewClass("Int64", "ValueType");
  builder.NewClass("Int8", "ValueType");
  builder.NewClass("IntPtr", "ValueType");
  builder.NewClass("UInt16", "ValueType");
  builder.NewClass("UInt32", "ValueType");
  builder.NewClass("UInt64", "ValueType");
  builder.NewClass("UInt8", "ValueType");
  builder.NewClass("UIntPtr", "ValueType");
  builder.NewClass("Void", "ValueType");

  builder.NewClass("String", "Object");

  // public class Console {
  //   public static void WriteLine(String string);
  //   public static void WriteLine(String string, Object object);
  // }
  auto const console_class_body = builder.NewClass("Console", "Object");
  auto const console_class = console_class_body->owner();

  auto const write_line = builder.ast_factory()->NewMethodGroup(
      console_class, builder.NewName("WriteLine"));

  auto const write_line_string = builder.ast_factory()->NewMethod(
      console_class_body, write_line,
      Modifiers(Modifier::Extern, Modifier::Public, Modifier::Static),
      builder.NewTypeReference(TokenType::Void), write_line->name(), {});
  write_line_string->SetParameters({
      builder.NewParameter(write_line_string, 0, "System.String", "string"),
  });

  auto const write_line_string_object = builder.ast_factory()->NewMethod(
      console_class_body, write_line,
      Modifiers(Modifier::Extern, Modifier::Public, Modifier::Static),
      builder.NewTypeReference(TokenType::Void), write_line->name(), {});
  write_line_string_object->SetParameters({
      builder.NewParameter(write_line_string_object, 0, "System.String",
                           "string"),
      builder.NewParameter(write_line_string_object, 1, "System.Object",
                           "object"),
  });

  write_line->AddMethod(write_line_string);
  console_class_body->AddMember(write_line_string);
  write_line->AddMethod(write_line_string_object);
  console_class_body->AddMember(write_line_string_object);
  console_class->AddNamedMember(write_line);
}

// Collect methods having following signature:
//  - void Main()
//  - void Main(String[])
//  - int Main()
//  - int Main(String[])
// Note: In HIR, objects are passed as pointers rather than object.
std::vector<ast::Node*> CollectMainMethods(CompilationSession* session,
                                           NameResolver* name_resolver) {
  auto const semantics = session->semantics();
  auto const name_main = session->NewAtomicString(L"Main");
  auto const int32_type =
      semantics->ValueOf(session->QueryAstNode(L"System.Int32"))
          ->as<ir::Type>();
  auto const string_type =
      semantics->ValueOf(session->QueryAstNode(L"System.String"))
          ->as<ir::Type>();
  auto const string_array_type =
      name_resolver->factory()->NewArrayType(string_type, {-1});
  auto const void_type =
      semantics->ValueOf(session->QueryAstNode(L"System.Void"))->as<ir::Type>();

  MethodQuery query1(name_main, void_type, {ParameterQuery(void_type)});
  MethodQuery query2(name_main, void_type, {ParameterQuery(string_array_type)});
  MethodQuery query3(name_main, int32_type, {ParameterQuery(void_type)});
  MethodQuery query4(name_main, int32_type,
                     {ParameterQuery(string_array_type)});
  OrQuery query({&query1, &query2, &query3, &query4});
  return QueryAllNodes(session, &query);
}

lir::Function* Generate(lir::Factory* factory, hir::Function* hir_function) {
  ::elang::cg::Generator generator(factory, hir_function);
  return generator.Generate();
}

}  // namespace

Compiler::Compiler()
    : is_dump_hir_(false),
      is_dump_lir_(false),
      session_(new CompilationSession()) {
}

Compiler::~Compiler() {
}

void Compiler::AddSourceFile(const base::FilePath& file_path) {
  auto const source_code = new (session()->zone()) FileSourceCode(file_path);
  if (!source_code->stream().file().IsValid()) {
    std::cerr << "Unable to open file "
              << source_code->stream().file_path().value() << "("
              << base::File::ErrorToString(
                     source_code->stream().file().error_details()) << ")"
              << std::endl;
    return;
  }
  auto const compilation_unit = session()->NewCompilationUnit(source_code);
  compiler::Parser parser(session(), compilation_unit);
  parser.Run();
}

int Compiler::CompileAndGo() {
  if (!session()->errors().empty())
    return 1;

  std::unique_ptr<hir::FactoryConfig> factory_config(
      NewFactoryConfig(session()));
  std::unique_ptr<hir::Factory> factory(new hir::Factory(*factory_config));

  NameResolver name_resolver(session());
  PopulateNamespace(&name_resolver);

  if (!session()->Compile(&name_resolver, factory.get()))
    return 1;

  auto const main_methods = CollectMainMethods(session(), &name_resolver);
  if (main_methods.empty()) {
    std::cerr << "No Main method." << std::endl;
    return 1;
  }
  if (main_methods.size() > 1u) {
    std::cerr << "More than one main methods:" << std::endl;
    for (auto const method : main_methods)
      std::cerr << "  " << *method << std::endl;
    return 1;
  }
  auto const main_method = main_methods.front()->as<ast::Method>();
  auto const main_function = session()->FunctionOf(main_method);
  if (!main_function) {
    std::cerr << "No function for main method." << *main_method;
    return 1;
  }
  if (is_dump_hir_) {
    hir::TextFormatter formatter(&std::cout);
    formatter.FormatFunction(main_function);
  }
  std::unique_ptr<lir::Factory> lir_factory(new lir::Factory());
  auto const lir_function = Generate(lir_factory.get(), main_function);
  if (is_dump_lir_) {
    lir::TextFormatter formatter(lir_factory->literals(), &std::cout);
    formatter.FormatFunction(lir_function);
  }
  return 0;
}

void Compiler::ReportErrors() {
  if (session()->errors().empty())
    return;

  for (auto const error : session()->errors()) {
    auto const& location = error->location();
    std::cerr << location.source_code()->name() << "("
              << location.start().line() + 1
              << "): " << ReadableErrorData(*error) << std::endl;
  }
}

}  // namespace shell
}  // namespace compiler
}  // namespace elang
<commit_msg>elang/shell: Stop compilation when HIR or LIR have errors.<commit_after>// Copyright 2014-2015 Project Vogue. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <iostream>
#include <iterator>
#include <vector>

#include "elang/shell/compiler.h"

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "elang/base/zone_allocated.h"
#include "elang/cg/generator.h"
#include "elang/compiler/analysis/name_resolver.h"
#include "elang/compiler/ast/class.h"
#include "elang/compiler/ast/factory.h"
#include "elang/compiler/ast/method.h"
#include "elang/compiler/ast/namespace.h"
#include "elang/compiler/compilation_session.h"
#include "elang/compiler/compilation_unit.h"
#include "elang/compiler/ir/factory.h"
#include "elang/compiler/ir/nodes.h"
#include "elang/compiler/public/compiler_error_code.h"
#include "elang/compiler/public/compiler_error_data.h"
#include "elang/compiler/semantics.h"
#include "elang/compiler/source_code.h"
#include "elang/compiler/source_code_position.h"
#include "elang/compiler/syntax/parser.h"
#include "elang/compiler/token_type.h"
#include "elang/hir/error_data.h"
#include "elang/hir/factory.h"
#include "elang/hir/factory_config.h"
#include "elang/hir/formatters/text_formatter.h"
#include "elang/hir/types.h"
#include "elang/hir/type_factory.h"
#include "elang/hir/values.h"
#include "elang/lir/error_data.h"
#include "elang/lir/factory.h"
#include "elang/lir/formatters/text_formatter.h"
#include "elang/shell/namespace_builder.h"
#include "elang/shell/node_query.h"
#include "elang/shell/source_file_stream.h"

namespace elang {
namespace compiler {
namespace shell {

namespace {

//////////////////////////////////////////////////////////////////////
//
// ReadableErrorData
//
struct ReadableErrorData {
  const ErrorData* error_data;

  explicit ReadableErrorData(const ErrorData& data) : error_data(&data) {}
};

std::ostream& operator<<(std::ostream& ostream,
                         const ReadableErrorData& readable) {
  static const char* const mnemonics[] = {
#define V(category, subcategory, name) #category "." #subcategory "." #name,
      FOR_EACH_COMPILER_ERROR_CODE(V, V)
#undef V
  };
  auto const it = std::begin(mnemonics) +
                  static_cast<size_t>(readable.error_data->error_code());
  ostream << (it < std::end(mnemonics) ? *it : "InvalidErrorCode");
  for (auto const token : readable.error_data->tokens())
    ostream << " " << token;
  return ostream;
}

//////////////////////////////////////////////////////////////////////
//
// FileSourceCode
//
class FileSourceCode final : public ::elang::compiler::SourceCode,
                             public ZoneAllocated {
 public:
  explicit FileSourceCode(const base::FilePath& file_path);
  ~FileSourceCode() = default;

  const base::FilePath& file_path() const { return stream().file_path(); }
  const SourceFileStream& stream() const { return *stream_; }

 private:
  // elang::compiler::SourceCode
  ::elang::compiler::CharacterStream* GetStream() final;

  base::FilePath file_path_;
  std::unique_ptr<SourceFileStream> stream_;

  DISALLOW_COPY_AND_ASSIGN(FileSourceCode);
};

FileSourceCode::FileSourceCode(const base::FilePath& file_path)
    : SourceCode(file_path.value()), stream_(new SourceFileStream(file_path)) {
}

::elang::compiler::CharacterStream* FileSourceCode::GetStream() {
  return stream_.get();
}

std::unique_ptr<hir::FactoryConfig> NewFactoryConfig(
    CompilationSession* session) {
  auto config = std::make_unique<hir::FactoryConfig>();
  config->atomic_string_factory = session->atomic_string_factory();
  config->string_type_name = session->NewAtomicString(L"System.String");
  return config;
}

// TODO(eval1749) We should load "System" namespace from file instead of
// building here.
void PopulateNamespace(NameResolver* name_resolver) {
  NamespaceBuilder builder(name_resolver);

  builder.NewClass("Object", "");
  builder.NewClass("ValueType", "Object");
  builder.NewClass("Enum", "ValueType");

  builder.NewClass("Bool", "ValueType");
  builder.NewClass("Char", "ValueType");
  builder.NewClass("Float32", "ValueType");
  builder.NewClass("Float64", "ValueType");
  builder.NewClass("Int16", "ValueType");
  builder.NewClass("Int32", "ValueType");
  builder.NewClass("Int64", "ValueType");
  builder.NewClass("Int8", "ValueType");
  builder.NewClass("IntPtr", "ValueType");
  builder.NewClass("UInt16", "ValueType");
  builder.NewClass("UInt32", "ValueType");
  builder.NewClass("UInt64", "ValueType");
  builder.NewClass("UInt8", "ValueType");
  builder.NewClass("UIntPtr", "ValueType");
  builder.NewClass("Void", "ValueType");

  builder.NewClass("String", "Object");

  // public class Console {
  //   public static void WriteLine(String string);
  //   public static void WriteLine(String string, Object object);
  // }
  auto const console_class_body = builder.NewClass("Console", "Object");
  auto const console_class = console_class_body->owner();

  auto const write_line = builder.ast_factory()->NewMethodGroup(
      console_class, builder.NewName("WriteLine"));

  auto const write_line_string = builder.ast_factory()->NewMethod(
      console_class_body, write_line,
      Modifiers(Modifier::Extern, Modifier::Public, Modifier::Static),
      builder.NewTypeReference(TokenType::Void), write_line->name(), {});
  write_line_string->SetParameters({
      builder.NewParameter(write_line_string, 0, "System.String", "string"),
  });

  auto const write_line_string_object = builder.ast_factory()->NewMethod(
      console_class_body, write_line,
      Modifiers(Modifier::Extern, Modifier::Public, Modifier::Static),
      builder.NewTypeReference(TokenType::Void), write_line->name(), {});
  write_line_string_object->SetParameters({
      builder.NewParameter(write_line_string_object, 0, "System.String",
                           "string"),
      builder.NewParameter(write_line_string_object, 1, "System.Object",
                           "object"),
  });

  write_line->AddMethod(write_line_string);
  console_class_body->AddMember(write_line_string);
  write_line->AddMethod(write_line_string_object);
  console_class_body->AddMember(write_line_string_object);
  console_class->AddNamedMember(write_line);
}

// Collect methods having following signature:
//  - void Main()
//  - void Main(String[])
//  - int Main()
//  - int Main(String[])
// Note: In HIR, objects are passed as pointers rather than object.
std::vector<ast::Node*> CollectMainMethods(CompilationSession* session,
                                           NameResolver* name_resolver) {
  auto const semantics = session->semantics();
  auto const name_main = session->NewAtomicString(L"Main");
  auto const int32_type =
      semantics->ValueOf(session->QueryAstNode(L"System.Int32"))
          ->as<ir::Type>();
  auto const string_type =
      semantics->ValueOf(session->QueryAstNode(L"System.String"))
          ->as<ir::Type>();
  auto const string_array_type =
      name_resolver->factory()->NewArrayType(string_type, {-1});
  auto const void_type =
      semantics->ValueOf(session->QueryAstNode(L"System.Void"))->as<ir::Type>();

  MethodQuery query1(name_main, void_type, {ParameterQuery(void_type)});
  MethodQuery query2(name_main, void_type, {ParameterQuery(string_array_type)});
  MethodQuery query3(name_main, int32_type, {ParameterQuery(void_type)});
  MethodQuery query4(name_main, int32_type,
                     {ParameterQuery(string_array_type)});
  OrQuery query({&query1, &query2, &query3, &query4});
  return QueryAllNodes(session, &query);
}

lir::Function* Generate(lir::Factory* factory, hir::Function* hir_function) {
  ::elang::cg::Generator generator(factory, hir_function);
  return generator.Generate();
}

bool ReportHirError(const hir::Factory* factory) {
  if (factory->errors().empty())
    return false;
  for (auto const error : factory->errors())
    std::cerr << *error << std::endl;
  return true;
}

bool ReportLirError(const lir::Factory* factory) {
  if (factory->errors().empty())
    return false;
  for (auto const error : factory->errors())
    std::cerr << *error << std::endl;
  return true;
}

}  // namespace

Compiler::Compiler()
    : is_dump_hir_(false),
      is_dump_lir_(false),
      session_(new CompilationSession()) {
}

Compiler::~Compiler() {
}

void Compiler::AddSourceFile(const base::FilePath& file_path) {
  auto const source_code = new (session()->zone()) FileSourceCode(file_path);
  if (!source_code->stream().file().IsValid()) {
    std::cerr << "Unable to open file "
              << source_code->stream().file_path().value() << "("
              << base::File::ErrorToString(
                     source_code->stream().file().error_details()) << ")"
              << std::endl;
    return;
  }
  auto const compilation_unit = session()->NewCompilationUnit(source_code);
  compiler::Parser parser(session(), compilation_unit);
  parser.Run();
}

int Compiler::CompileAndGo() {
  if (!session()->errors().empty())
    return 1;

  std::unique_ptr<hir::FactoryConfig> factory_config(
      NewFactoryConfig(session()));
  std::unique_ptr<hir::Factory> factory(new hir::Factory(*factory_config));

  NameResolver name_resolver(session());
  PopulateNamespace(&name_resolver);

  if (!session()->Compile(&name_resolver, factory.get()))
    return 1;

  if (ReportHirError(factory.get()))
    return 1;

  // Find entry point.
  auto const main_methods = CollectMainMethods(session(), &name_resolver);
  if (main_methods.empty()) {
    std::cerr << "No Main method." << std::endl;
    return 1;
  }
  if (main_methods.size() > 1u) {
    std::cerr << "More than one main methods:" << std::endl;
    for (auto const method : main_methods)
      std::cerr << "  " << *method << std::endl;
    return 1;
  }
  auto const main_method = main_methods.front()->as<ast::Method>();
  auto const main_function = session()->FunctionOf(main_method);
  if (!main_function) {
    std::cerr << "No function for main method." << *main_method;
    return 1;
  }
  if (is_dump_hir_) {
    hir::TextFormatter formatter(&std::cout);
    formatter.FormatFunction(main_function);
  }

  // Translate HIR to LIR
  std::unique_ptr<lir::Factory> lir_factory(new lir::Factory());
  auto const lir_function = Generate(lir_factory.get(), main_function);

  if (ReportLirError(lir_factory.get()))
    return 1;

  if (is_dump_lir_) {
    lir::TextFormatter formatter(lir_factory->literals(), &std::cout);
    formatter.FormatFunction(lir_function);
  }
  return 0;
}

void Compiler::ReportErrors() {
  if (session()->errors().empty())
    return;

  for (auto const error : session()->errors()) {
    auto const& location = error->location();
    std::cerr << location.source_code()->name() << "("
              << location.start().line() + 1
              << "): " << ReadableErrorData(*error) << std::endl;
  }
}

}  // namespace shell
}  // namespace compiler
}  // namespace elang
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <string>

#include "version.h"

// Name of client reported in the 'version' message. Report the same name
// for both bitcoind and bitcoin-qt, to make it harder for attackers to
// target servers or GUI users specifically.
const std::string CLIENT_NAME("cleanwatercoin");

// Client version number
#define CLIENT_VERSION_SUFFIX   ""

// The following part of the code determines the CLIENT_BUILD variable.
// Several mechanisms are used for this:
// * first, if HAVE_BUILD_INFO is defined, include build.h, a file that is
//   generated by the build environment, possibly containing the output
//   of git-describe in a macro called BUILD_DESC
// * secondly, if this is an exported version of the code, GIT_ARCHIVE will
//   be defined (automatically using the export-subst git attribute), and
//   GIT_COMMIT will contain the commit id.
// * then, three options exist for determining CLIENT_BUILD:
//   * if BUILD_DESC is defined, use that literally (output of git-describe)
//   * if not, but GIT_COMMIT is defined, use v[maj].[min].[rev].[build]-g[commit]
//   * otherwise, use v[maj].[min].[rev].[build]-unk
// finally CLIENT_VERSION_SUFFIX is added

// First, include build.h if requested
#ifdef HAVE_BUILD_INFO
#    include "build.h"
#endif

// git will put "#define GIT_ARCHIVE 1" on the next line inside archives.
#define GIT_ARCHIVE 1
#ifdef GIT_ARCHIVE
#    define GIT_COMMIT_ID ""
#    define GIT_COMMIT_DATE "$Format:%cD"
#endif

#define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \
    "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "" commit

#define BUILD_DESC_FROM_UNKNOWN(maj,min,rev,build) \
    "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) ""

#ifndef BUILD_DESC
#    ifdef GIT_COMMIT_ID
#        define BUILD_DESC BUILD_DESC_FROM_COMMIT(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION, DISPLAY_VERSION_BUILD, GIT_COMMIT_ID)
#    else
#        define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION, DISPLAY_VERSION_BUILD)
#    endif
#endif

#ifndef BUILD_DATE
#    ifdef GIT_COMMIT_DATE
#        define BUILD_DATE GIT_COMMIT_DATE
#    else
#        define BUILD_DATE __DATE__ ", " __TIME__
#    endif
#endif

const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);
const std::string CLIENT_DATE(BUILD_DATE);
<commit_msg>Fix for correct date format in Debug window<commit_after>// Copyright (c) 2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <string>

#include "version.h"

// Name of client reported in the 'version' message. Report the same name
// for both bitcoind and bitcoin-qt, to make it harder for attackers to
// target servers or GUI users specifically.
const std::string CLIENT_NAME("cleanwatercoin");

// Client version number
#define CLIENT_VERSION_SUFFIX   ""

// The following part of the code determines the CLIENT_BUILD variable.
// Several mechanisms are used for this:
// * first, if HAVE_BUILD_INFO is defined, include build.h, a file that is
//   generated by the build environment, possibly containing the output
//   of git-describe in a macro called BUILD_DESC
// * secondly, if this is an exported version of the code, GIT_ARCHIVE will
//   be defined (automatically using the export-subst git attribute), and
//   GIT_COMMIT will contain the commit id.
// * then, three options exist for determining CLIENT_BUILD:
//   * if BUILD_DESC is defined, use that literally (output of git-describe)
//   * if not, but GIT_COMMIT is defined, use v[maj].[min].[rev].[build]-g[commit]
//   * otherwise, use v[maj].[min].[rev].[build]-unk
// finally CLIENT_VERSION_SUFFIX is added

// First, include build.h if requested
#ifdef HAVE_BUILD_INFO
#    include "build.h"
#endif

// git will put "#define GIT_ARCHIVE 1" on the next line inside archives.
#define GIT_ARCHIVE 1
#ifdef GIT_ARCHIVE
#    define GIT_COMMIT_ID ""
#    define GIT_COMMIT_DATE "$Format:%cD$"
#endif

#define BUILD_DESC_FROM_COMMIT(maj,min,rev,build,commit) \
    "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "" commit

#define BUILD_DESC_FROM_UNKNOWN(maj,min,rev,build) \
    "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) ""

#ifndef BUILD_DESC
#    ifdef GIT_COMMIT_ID
#        define BUILD_DESC BUILD_DESC_FROM_COMMIT(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION, DISPLAY_VERSION_BUILD, GIT_COMMIT_ID)
#    else
#        define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION, DISPLAY_VERSION_BUILD)
#    endif
#endif

#ifndef BUILD_DATE
#    ifdef GIT_COMMIT_DATE
#        define BUILD_DATE GIT_COMMIT_DATE
#    else
#        define BUILD_DATE __DATE__ ", " __TIME__
#    endif
#endif

const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);
const std::string CLIENT_DATE(BUILD_DATE);
<|endoftext|>


<commit_before>#include <htmlrenderer.h>
#include <xmlpullparser.h>
#include <sstream>

using namespace noos;

htmlrenderer::htmlrenderer(unsigned int width) : w(width) { }

std::vector<std::string> htmlrenderer::render(const std::string& source) {
	std::vector<std::string> lines;
	std::vector<std::string> links;
	unsigned int link_count = 0;
	std::string curline;
	int indent_level = 0;

	std::istringstream input(source);
	xmlpullparser xpp;
	xpp.setInput(input);

	for (xmlpullparser::event e = xpp.next(); e != xmlpullparser::END_DOCUMENT; e = xpp.next()) {
		switch (e) {
			case xmlpullparser::START_TAG:
				if (xpp.getText() == "a") {
					std::string link = xpp.getAttributeValue("href");
					if (link.length() > 0) {
						links.push_back(link);
						std::ostringstream ref;
						ref << "[" << link_count << "]";
						link_count++;
						curline.append(ref.str());
					}
				} else if (xpp.getText() == "br") {
					lines.push_back(curline);
					curline = "";
				} else if (xpp.getText() == "img") {
					std::string imgurl = xpp.getAttributeValue("src");
					if (imgurl.length() > 0) {
						links.push_back(imgurl);
						std::ostringstream ref;
						ref << "[" << link_count << "]";
						link_count++;
						curline.append(ref.str());
					}
				} else if (xpp.getText() == "blockquote") {
					++indent_level;
					lines.push_back(curline);
					lines.push_back(std::string(""));
					prepare_newline(curline, indent_level);
				} else if (xpp.getText() == "p") {
					lines.push_back(curline);
					lines.push_back(std::string(""));
					prepare_newline(curline, indent_level);
				}
				break;
			case xmlpullparser::END_TAG:
				if (xpp.getText() == "blockquote") {
					--indent_level;
					if (indent_level < 0)
					  indent_level = 0;
					lines.push_back(curline);
					lines.push_back(std::string(""));
					prepare_newline(curline, indent_level);
				}
				break;
			case xmlpullparser::TEXT:
				{
					std::vector<std::string> words = xmlpullparser::tokenize(xpp.getText());
					unsigned int i=0;
					for (std::vector<std::string>::iterator it=words.begin();it!=words.end();++it,++i) {
						if ((curline.length() + it->length()) >= w) {
							lines.push_back(curline);
							prepare_newline(curline, indent_level);
						}
						curline.append(*it);
						if (i < words.size()-1)
							curline.append(" ");
					}
				}
				break;
			default:
				/* do nothing */
				break;
		}
	}
	lines.push_back(curline);

	if (links.size() > 0) {
		lines.push_back(std::string(""));
		lines.push_back(std::string("Links: "));
		for (unsigned int i=0;i<links.size();++i) {
			std::ostringstream line;
			line << "[" << i << "]: " << links[i];
			lines.push_back(line.str());
		}
	}

	return lines;
}

void htmlrenderer::prepare_newline(std::string& line, int indent_level) {
	line = "";
	for (int i=0;i<indent_level;++i) {
		line.append("  ");
	}
}
<commit_msg>Andreas Krennmair: 	improved the HTML rendering where too many blank lines would be inserted.<commit_after>#include <htmlrenderer.h>
#include <xmlpullparser.h>
#include <sstream>

using namespace noos;

htmlrenderer::htmlrenderer(unsigned int width) : w(width) { }

std::vector<std::string> htmlrenderer::render(const std::string& source) {
	std::vector<std::string> lines;
	std::vector<std::string> links;
	unsigned int link_count = 0;
	std::string curline;
	int indent_level = 0;

	std::istringstream input(source);
	xmlpullparser xpp;
	xpp.setInput(input);

	for (xmlpullparser::event e = xpp.next(); e != xmlpullparser::END_DOCUMENT; e = xpp.next()) {
		switch (e) {
			case xmlpullparser::START_TAG:
				if (xpp.getText() == "a") {
					std::string link = xpp.getAttributeValue("href");
					if (link.length() > 0) {
						links.push_back(link);
						std::ostringstream ref;
						ref << "[" << link_count << "]";
						link_count++;
						curline.append(ref.str());
					}
				} else if (xpp.getText() == "br") {
					if (curline.length() > 0)
						lines.push_back(curline);
					prepare_newline(curline, indent_level);
				} else if (xpp.getText() == "img") {
					std::string imgurl = xpp.getAttributeValue("src");
					if (imgurl.length() > 0) {
						links.push_back(imgurl);
						std::ostringstream ref;
						ref << "[" << link_count << "]";
						link_count++;
						curline.append(ref.str());
					}
				} else if (xpp.getText() == "blockquote") {
					++indent_level;
					if (curline.length() > 0)
						lines.push_back(curline);
					lines.push_back(std::string(""));
					prepare_newline(curline, indent_level);
				} else if (xpp.getText() == "p") {
					if (curline.length() > 0)
						lines.push_back(curline);
					lines.push_back(std::string(""));
					prepare_newline(curline, indent_level);
				}
				break;
			case xmlpullparser::END_TAG:
				if (xpp.getText() == "blockquote") {
					--indent_level;
					if (indent_level < 0)
						indent_level = 0;
					if (curline.length() > 0)
						lines.push_back(curline);
					lines.push_back(std::string(""));
					prepare_newline(curline, indent_level);
				}
				break;
			case xmlpullparser::TEXT:
				{
					std::vector<std::string> words = xmlpullparser::tokenize(xpp.getText());
					unsigned int i=0;
					for (std::vector<std::string>::iterator it=words.begin();it!=words.end();++it,++i) {
						if ((curline.length() + it->length()) >= w) {
							lines.push_back(curline);
							prepare_newline(curline, indent_level);
						}
						curline.append(*it);
						if (i < words.size()-1)
							curline.append(" ");
					}
				}
				break;
			default:
				/* do nothing */
				break;
		}
	}
	lines.push_back(curline);

	if (links.size() > 0) {
		lines.push_back(std::string(""));
		lines.push_back(std::string("Links: "));
		for (unsigned int i=0;i<links.size();++i) {
			std::ostringstream line;
			line << "[" << i << "]: " << links[i];
			lines.push_back(line.str());
		}
	}

	return lines;
}

void htmlrenderer::prepare_newline(std::string& line, int indent_level) {
	line = "";
	for (int i=0;i<indent_level;++i) {
		line.append("  ");
	}
}
<|endoftext|>