<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/buffer.hpp"
#include "libtorrent/http_parser.hpp"

#include <boost/bind.hpp>
#include <boost/ref.hpp>
#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif
#include <boost/thread/mutex.hpp>
#include <cstdlib>
#include <boost/config.hpp>

using boost::bind;
using namespace libtorrent;

namespace libtorrent
{
	// defined in broadcast_socket.cpp
	address guess_local_address(io_service&);
}

static error_code ec;

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(1)
	, m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143", ec), 6771)
		, bind(&lsd::on_announce, self(), _1, _2, _3))
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
}

lsd::~lsd() {}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	std::stringstream btsearch;
	btsearch << "BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: " << listen_port << "\r\n"
		"Infohash: " << ih << "\r\n"
		"\r\n\r\n";
	std::string const& msg = btsearch.str();

	m_retry_count = 1;
	error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " ==> announce: ih: " << ih << " port: " << listen_port << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::resend_announce(error_code const& e, std::string msg)
{
	if (e) return;

	error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::on_announce(udp::endpoint const& from, char* buffer
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;

	http_parser p;

	bool error = false;
	p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred)
		, error);

	if (!p.header_finished() || error)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: incomplete HTTP message\n";
#endif
		return;
	}

	if (p.method() != "bt-search")
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid HTTP method: " << p.method() << std::endl;
#endif
		return;
	}

	std::string const& port_str = p.header("port");
	if (port_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing port" << std::endl;
#endif
		return;
	}

	std::string const& ih_str = p.header("infohash");
	if (ih_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing infohash" << std::endl;
#endif
		return;
	}

	sha1_hash ih(0);
	std::istringstream ih_sstr(ih_str);
	ih_sstr >> ih;
	int port = std::atoi(port_str.c_str());

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " *** incoming local announce " << from.address()
			<< ":" << port << " ih: " << ih << std::endl;
#endif
		// we got an announce, pass it on through the callback
#ifndef BOOST_NO_EXCEPTIONS
		try {
#endif
			m_callback(tcp::endpoint(from.address(), port), ih);
#ifndef BOOST_NO_EXCEPTIONS
		}
		catch (std::exception&) {}
#endif
	}
}

void lsd::close()
{
	m_socket.close();
	error_code ec;
	m_broadcast_timer.cancel(ec);
	m_disabled = true;
	m_callback.clear();
}

<commit_msg>fixed locale dependence in lsd.cpp<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/buffer.hpp"
#include "libtorrent/http_parser.hpp"
#include "libtorrent/escape_string.hpp"

#include <boost/bind.hpp>
#include <boost/ref.hpp>
#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif
#include <boost/thread/mutex.hpp>
#include <cstdlib>
#include <boost/config.hpp>

using boost::bind;
using namespace libtorrent;

namespace libtorrent
{
	// defined in broadcast_socket.cpp
	address guess_local_address(io_service&);
}

static error_code ec;

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(1)
	, m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143", ec), 6771)
		, bind(&lsd::on_announce, self(), _1, _2, _3))
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
}

lsd::~lsd() {}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	std::stringstream btsearch;
	btsearch << "BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: " << to_string(listen_port).elems << "\r\n"
		"Infohash: " << ih << "\r\n"
		"\r\n\r\n";
	std::string const& msg = btsearch.str();

	m_retry_count = 1;
	error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " ==> announce: ih: " << ih << " port: " << to_string(listen_port).elems << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::resend_announce(error_code const& e, std::string msg)
{
	if (e) return;

	error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::on_announce(udp::endpoint const& from, char* buffer
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;

	http_parser p;

	bool error = false;
	p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred)
		, error);

	if (!p.header_finished() || error)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: incomplete HTTP message\n";
#endif
		return;
	}

	if (p.method() != "bt-search")
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid HTTP method: " << p.method() << std::endl;
#endif
		return;
	}

	std::string const& port_str = p.header("port");
	if (port_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing port" << std::endl;
#endif
		return;
	}

	std::string const& ih_str = p.header("infohash");
	if (ih_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing infohash" << std::endl;
#endif
		return;
	}

	sha1_hash ih(0);
	std::istringstream ih_sstr(ih_str);
	ih_sstr >> ih;
	int port = std::atoi(port_str.c_str());

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " *** incoming local announce " << from.address()
			<< ":" << to_string(port).elems << " ih: " << ih << std::endl;
#endif
		// we got an announce, pass it on through the callback
#ifndef BOOST_NO_EXCEPTIONS
		try {
#endif
			m_callback(tcp::endpoint(from.address(), port), ih);
#ifndef BOOST_NO_EXCEPTIONS
		}
		catch (std::exception&) {}
#endif
	}
}

void lsd::close()
{
	m_socket.close();
	error_code ec;
	m_broadcast_timer.cancel(ec);
	m_disabled = true;
	m_callback.clear();
}

<|endoftext|>


<commit_before>#include <vector>
#include <utility>
#include "nan.h"
#include "spellchecker.h"
#include "worker.h"

using Nan::ObjectWrap;
using namespace spellchecker;
using namespace v8;

namespace {

class Spellchecker : public Nan::ObjectWrap {
  SpellcheckerImplementation* impl;

  static NAN_METHOD(New) {
    Nan::HandleScope scope;
    Spellchecker* that = new Spellchecker();
    that->Wrap(info.This());

    info.GetReturnValue().Set(info.This());
  }

  static NAN_METHOD(SetDictionary) {
    Nan::HandleScope scope;

    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    std::string language = *Nan::Utf8String(info[0]);
    std::string directory = ".";
    if (info.Length() > 1) {
      directory = *Nan::Utf8String(info[1]);
    }

    bool result = that->impl->SetDictionary(language, directory);
    info.GetReturnValue().Set(Nan::New(result));
  }

  static NAN_METHOD(IsMisspelled) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::string word = *Nan::Utf8String(info[0]);

    info.GetReturnValue().Set(Nan::New(that->impl->IsMisspelled(word)));
  }

  static NAN_METHOD(CheckSpelling) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Local<String> string = Local<String>::Cast(info[0]);
    if (!string->IsString()) {
      return Nan::ThrowError("Bad argument");
    }

    Local<Array> result = Nan::New<Array>();
    info.GetReturnValue().Set(result);

    if (string->Length() == 0) {
      return;
    }

    std::vector<uint16_t> text(string->Length() + 1);
    string->Write(info.GetIsolate(), reinterpret_cast<uint16_t *>(text.data()));

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::vector<MisspelledRange> misspelled_ranges = that->impl->CheckSpelling(text.data(), text.size());

    std::vector<MisspelledRange>::const_iterator iter = misspelled_ranges.begin();
    for (; iter != misspelled_ranges.end(); ++iter) {
      size_t index = iter - misspelled_ranges.begin();
      uint32_t start = iter->start, end = iter->end;

      Local<Object> misspelled_range = Nan::New<Object>();
      misspelled_range->Set(Nan::New("start").ToLocalChecked(), Nan::New<Integer>(start));
      misspelled_range->Set(Nan::New("end").ToLocalChecked(), Nan::New<Integer>(end));
      result->Set(index, misspelled_range);
    }
  }

  static NAN_METHOD(CheckSpellingAsync) {
    Nan::HandleScope scope;
    if (info.Length() < 2) {
      return Nan::ThrowError("Bad argument");
    }

    Local<String> string = Local<String>::Cast(info[0]);
    if (!string->IsString()) {
      return Nan::ThrowError("Bad argument");
    }

    Nan::Callback *callback = new Nan::Callback(info[1].As<Function>());

    std::vector<uint16_t> corpus(string->Length() + 1);
    string->Write(info.GetIsolate(), reinterpret_cast<uint16_t *>(corpus.data()));

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    CheckSpellingWorker* worker = new CheckSpellingWorker(std::move(corpus), that->impl, callback);
    Nan::AsyncQueueWorker(worker);
  }

  static NAN_METHOD(Add) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::string word = *Nan::Utf8String(info[0]);

    that->impl->Add(word);
    return;
  }

  static NAN_METHOD(Remove) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::string word = *Nan::Utf8String(info[0]);

    that->impl->Remove(word);
    return;
  }

  static NAN_METHOD(GetAvailableDictionaries) {
    Nan::HandleScope scope;

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    std::string path = ".";
    if (info.Length() > 0) {
      std::string path = *Nan::Utf8String(info[0]);
    }

    std::vector<std::string> dictionaries =
      that->impl->GetAvailableDictionaries(path);

    Local<Array> result = Nan::New<Array>(dictionaries.size());
    for (size_t i = 0; i < dictionaries.size(); ++i) {
      const std::string& dict = dictionaries[i];
      result->Set(i, Nan::New(dict.data(), dict.size()).ToLocalChecked());
    }

    info.GetReturnValue().Set(result);
  }

  static NAN_METHOD(GetCorrectionsForMisspelling) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    std::string word = *Nan::Utf8String(info[0]);
    std::vector<std::string> corrections =
      that->impl->GetCorrectionsForMisspelling(word);

    Local<Array> result = Nan::New<Array>(corrections.size());
    for (size_t i = 0; i < corrections.size(); ++i) {
      const std::string& word = corrections[i];

      Nan::MaybeLocal<String> val = Nan::New<String>(word.data(), word.size());
      result->Set(i, val.ToLocalChecked());
    }

    info.GetReturnValue().Set(result);
  }

  Spellchecker() {
    impl = SpellcheckerFactory::CreateSpellchecker();
  }

  // actual destructor
  virtual ~Spellchecker() {
    delete impl;
  }

 public:
  static void Init(Local<Object> exports) {
    Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(Spellchecker::New);

    tpl->SetClassName(Nan::New<String>("Spellchecker").ToLocalChecked());
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    Nan::SetPrototypeMethod(tpl, "setDictionary", Spellchecker::SetDictionary);
    Nan::SetPrototypeMethod(tpl, "getAvailableDictionaries", Spellchecker::GetAvailableDictionaries);
    Nan::SetPrototypeMethod(tpl, "getCorrectionsForMisspelling", Spellchecker::GetCorrectionsForMisspelling);
    Nan::SetPrototypeMethod(tpl, "isMisspelled", Spellchecker::IsMisspelled);
    Nan::SetPrototypeMethod(tpl, "checkSpelling", Spellchecker::CheckSpelling);
    Nan::SetPrototypeMethod(tpl, "checkSpellingAsync", Spellchecker::CheckSpellingAsync);
    Nan::SetPrototypeMethod(tpl, "add", Spellchecker::Add);
    Nan::SetPrototypeMethod(tpl, "remove", Spellchecker::Remove);

    Isolate* isolate = exports->GetIsolate();
    Local<Context> context = isolate->GetCurrentContext();
    exports->Set(Nan::New("Spellchecker").ToLocalChecked(), tpl->GetFunction(context).ToLocalChecked());
  }
};

void Init(Local<Object> exports, Local<Object> module) {
  Spellchecker::Init(exports);
}

}  // namespace

NODE_MODULE(spellchecker, Init)
<commit_msg>Keep compatibility with Node v8<commit_after>#include <vector>
#include <utility>
#include "nan.h"
#include "spellchecker.h"
#include "worker.h"

using Nan::ObjectWrap;
using namespace spellchecker;
using namespace v8;

namespace {

class Spellchecker : public Nan::ObjectWrap {
  SpellcheckerImplementation* impl;

  static NAN_METHOD(New) {
    Nan::HandleScope scope;
    Spellchecker* that = new Spellchecker();
    that->Wrap(info.This());

    info.GetReturnValue().Set(info.This());
  }

  static NAN_METHOD(SetDictionary) {
    Nan::HandleScope scope;

    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    std::string language = *Nan::Utf8String(info[0]);
    std::string directory = ".";
    if (info.Length() > 1) {
      directory = *Nan::Utf8String(info[1]);
    }

    bool result = that->impl->SetDictionary(language, directory);
    info.GetReturnValue().Set(Nan::New(result));
  }

  static NAN_METHOD(IsMisspelled) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::string word = *Nan::Utf8String(info[0]);

    info.GetReturnValue().Set(Nan::New(that->impl->IsMisspelled(word)));
  }

  static NAN_METHOD(CheckSpelling) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Local<String> string = Local<String>::Cast(info[0]);
    if (!string->IsString()) {
      return Nan::ThrowError("Bad argument");
    }

    Local<Array> result = Nan::New<Array>();
    info.GetReturnValue().Set(result);

    if (string->Length() == 0) {
      return;
    }

    std::vector<uint16_t> text(string->Length() + 1);
    string->Write(
#if V8_MAJOR_VERSION > 6
        info.GetIsolate(),
#endif
        reinterpret_cast<uint16_t *>(text.data()));

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::vector<MisspelledRange> misspelled_ranges = that->impl->CheckSpelling(text.data(), text.size());

    std::vector<MisspelledRange>::const_iterator iter = misspelled_ranges.begin();
    for (; iter != misspelled_ranges.end(); ++iter) {
      size_t index = iter - misspelled_ranges.begin();
      uint32_t start = iter->start, end = iter->end;

      Local<Object> misspelled_range = Nan::New<Object>();
      misspelled_range->Set(Nan::New("start").ToLocalChecked(), Nan::New<Integer>(start));
      misspelled_range->Set(Nan::New("end").ToLocalChecked(), Nan::New<Integer>(end));
      result->Set(index, misspelled_range);
    }
  }

  static NAN_METHOD(CheckSpellingAsync) {
    Nan::HandleScope scope;
    if (info.Length() < 2) {
      return Nan::ThrowError("Bad argument");
    }

    Local<String> string = Local<String>::Cast(info[0]);
    if (!string->IsString()) {
      return Nan::ThrowError("Bad argument");
    }

    Nan::Callback *callback = new Nan::Callback(info[1].As<Function>());

    std::vector<uint16_t> corpus(string->Length() + 1);
    string->Write(
#if V8_MAJOR_VERSION > 6
        info.GetIsolate(),
#endif
        reinterpret_cast<uint16_t *>(corpus.data()));

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    CheckSpellingWorker* worker = new CheckSpellingWorker(std::move(corpus), that->impl, callback);
    Nan::AsyncQueueWorker(worker);
  }

  static NAN_METHOD(Add) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::string word = *Nan::Utf8String(info[0]);

    that->impl->Add(word);
    return;
  }

  static NAN_METHOD(Remove) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());
    std::string word = *Nan::Utf8String(info[0]);

    that->impl->Remove(word);
    return;
  }

  static NAN_METHOD(GetAvailableDictionaries) {
    Nan::HandleScope scope;

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    std::string path = ".";
    if (info.Length() > 0) {
      std::string path = *Nan::Utf8String(info[0]);
    }

    std::vector<std::string> dictionaries =
      that->impl->GetAvailableDictionaries(path);

    Local<Array> result = Nan::New<Array>(dictionaries.size());
    for (size_t i = 0; i < dictionaries.size(); ++i) {
      const std::string& dict = dictionaries[i];
      result->Set(i, Nan::New(dict.data(), dict.size()).ToLocalChecked());
    }

    info.GetReturnValue().Set(result);
  }

  static NAN_METHOD(GetCorrectionsForMisspelling) {
    Nan::HandleScope scope;
    if (info.Length() < 1) {
      return Nan::ThrowError("Bad argument");
    }

    Spellchecker* that = Nan::ObjectWrap::Unwrap<Spellchecker>(info.Holder());

    std::string word = *Nan::Utf8String(info[0]);
    std::vector<std::string> corrections =
      that->impl->GetCorrectionsForMisspelling(word);

    Local<Array> result = Nan::New<Array>(corrections.size());
    for (size_t i = 0; i < corrections.size(); ++i) {
      const std::string& word = corrections[i];

      Nan::MaybeLocal<String> val = Nan::New<String>(word.data(), word.size());
      result->Set(i, val.ToLocalChecked());
    }

    info.GetReturnValue().Set(result);
  }

  Spellchecker() {
    impl = SpellcheckerFactory::CreateSpellchecker();
  }

  // actual destructor
  virtual ~Spellchecker() {
    delete impl;
  }

 public:
  static void Init(Local<Object> exports) {
    Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(Spellchecker::New);

    tpl->SetClassName(Nan::New<String>("Spellchecker").ToLocalChecked());
    tpl->InstanceTemplate()->SetInternalFieldCount(1);

    Nan::SetPrototypeMethod(tpl, "setDictionary", Spellchecker::SetDictionary);
    Nan::SetPrototypeMethod(tpl, "getAvailableDictionaries", Spellchecker::GetAvailableDictionaries);
    Nan::SetPrototypeMethod(tpl, "getCorrectionsForMisspelling", Spellchecker::GetCorrectionsForMisspelling);
    Nan::SetPrototypeMethod(tpl, "isMisspelled", Spellchecker::IsMisspelled);
    Nan::SetPrototypeMethod(tpl, "checkSpelling", Spellchecker::CheckSpelling);
    Nan::SetPrototypeMethod(tpl, "checkSpellingAsync", Spellchecker::CheckSpellingAsync);
    Nan::SetPrototypeMethod(tpl, "add", Spellchecker::Add);
    Nan::SetPrototypeMethod(tpl, "remove", Spellchecker::Remove);

    Isolate* isolate = exports->GetIsolate();
    Local<Context> context = isolate->GetCurrentContext();
    exports->Set(Nan::New("Spellchecker").ToLocalChecked(), tpl->GetFunction(context).ToLocalChecked());
  }
};

void Init(Local<Object> exports, Local<Object> module) {
  Spellchecker::Init(exports);
}

}  // namespace

NODE_MODULE(spellchecker, Init)
<|endoftext|>


<commit_before>/* Copyright (c) 2011-2014, Robert J. Hansen <rjh@secret-alchemy.com>
 * and others.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Code standards:
 *   This is a small enough project we don't need a formal coding standard.
 *   That said, here are some helpful tips for people who want to submit
 *   patches:
 *
 *   - If it's not 100% ISO C++11, it won't get in.
 *   - It must compile cleanly and without warnings under both GNU G++
 *     and Clang++, even with "-W -Wextra -ansi -pedantic".
 *     (Exceptions can be made for warnings that are actually compiler
 *     conformance issues: for instance, Clang++ will warn that 'long long'
 *     is a C++11 extension, even when you run it in -std=C++11 mode.)
 *   - C++ offers 'and', 'or' and 'not' keywords instead of &&, || and !.
 *     I like these: I think they're more readable.  Please use them.
 *   - C++11 uniform initialization syntax, please.
 *   - Please try to follow the formatting conventions.  It's mostly
 *     straight-up astyle format, with occasional tweaks where necessary
 *     to get nice hardcopy printouts.
 *   - If you write a new function it must have a Doxygen block
 *     documenting it.
 *
 * Contributor history:
 *
 * Robert J. Hansen <rjh@secret-alchemy.com>
 *   - most everything
 */

#include "../config.h"
#include <sstream>
#include <sys/stat.h>
#include <syslog.h>
#include <set>
#include <string>
#include <time.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <algorithm>
#include <limits.h>
#include "handler.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include <utility>
#include <signal.h>

/* Additional defines necessary on FreeBSD: */
/* Necessary for sockaddr and sockaddr_in structures */
#ifdef __FreeBSD__
#include <sys/socket.h>
#include <netinet/in.h>
#endif

using std::stringstream;
using std::string;
using std::set;
using std::transform;
using std::find_if;
using std::not1;
using std::ptr_fun;
using std::ifstream;
using std::cerr;
using std::vector;
using std::remove_if;
using std::sort;
using std::pair;
using std::unique_ptr;

typedef unsigned long long ULONG64;
typedef pair<ULONG64, ULONG64> pair64;

#define INFO LOG_MAKEPRI(LOG_USER, LOG_INFO)
#define WARN LOG_MAKEPRI(LOG_USER, LOG_WARNING)
#define DEBUG LOG_MAKEPRI(LOG_USER, LOG_DEBUG)
#define CRITICAL LOG_MAKEPRI(LOG_USER, LOG_CRIT)
#define ALERT LOG_MAKEPRI(LOG_USER, LOG_ALERT)
#define EMERGENCY LOG_MAKEPRI(LOG_USER, LOG_EMERG)
#define MAX_PENDING_REQUESTS 20
#define BUFFER_SIZE 128

namespace
{
/** Tracks whether the server should only support protocol 1.0. */
bool old_only { false };

/** Tracks whether the server should support status queries. */
bool status_enabled { false };

/** Tracks whether the server should run as a daemon. */
bool standalone { false };

/** Our set of hashes, represented as a block of contiguous memory.
  * Note that the current NSRL library contains approximately 40
  * million values, each at roughly 32 bytes (rounded to binary
  * powers to make the math easier).  This is 2**25 values times
  * 2**5 bytes each = 2**30 bytes, or about a gig of RAM.
  *
  * Moral of the story: populating this set is computationally
  * expensive. */
vector<pair64> hash_set;

/** Tracks where we look for the location of the
  * reference data set. */
string RDS_LOC { PKGDATADIR "/hashes.txt" };

/** Which port to listen on */
uint16_t PORT { 9120 };

/** Determines whether a string represents a valid uppercase
  * MD5 hash. */

auto good_line(const string& line)
{
    if (line.size() != 32)
        return false;

    auto iter = line.cbegin();
    auto end = line.cend();
    while (iter != end and
            ((*iter >= '0' and *iter <= '9') or
             (*iter >= 'A' and *iter <= 'F')))
        ++iter;
    return (iter == end) ? true : false;
}

/** Loads hashes from disk and stores them in a fast-accessing
  * in-memory data structure.  This will be slow. */
void load_hashes()
{
    vector<char> buf(BUFFER_SIZE, 0);
    uint32_t line_count { 0 }, hash_count { 0 };
    ifstream infile { RDS_LOC.c_str() };

	hash_set.reserve(40000000);

    if (not infile)
    {
        syslog(WARN, "couldn't open hashes file %s",
               RDS_LOC.c_str());
        exit(EXIT_FAILURE);
    }

    while (infile)
    {
        memset(static_cast<void*>(&buf[0]), 0, BUFFER_SIZE);
        infile.getline(&buf[0], BUFFER_SIZE);
        line_count += 1;
        const string line(buf.cbegin(), find(buf.cbegin(), buf.cend(), 0));

        if (0 == line.size())
        {
            continue;
        }

        if (! good_line(line))
        {
            syslog(ALERT, "hash file appears corrupt!  Loading no hashes.");
            syslog(ALERT, "offending line is #%d", line_count);
            syslog(ALERT, "offending line has %d bytes", (int) line.size());
            syslog(ALERT, "Content follows:");
            syslog(ALERT, "%s", line.c_str());
            syslog(ALERT, "shutting down!");
            exit(EXIT_FAILURE);
            return;
        }
        hash_count += 1;

        if (0 == hash_count % 1000000)
        {
            syslog(INFO, "loaded %u million hashes", hash_count / 1000000);
        }

        hash_set.push_back(to_pair64(line));
    }
    infile.close();
    syslog(INFO, "read in %u unique hashes",
           static_cast<uint32_t>(hash_set.size()));
    sort(hash_set.begin(), hash_set.end());
}

/** Converts our application into a proper daemon. */
void daemonize()
{
    const auto pid = fork();
    if (pid < 0)
    {
        syslog(WARN, "couldn't fork!");
        exit(EXIT_FAILURE);
    }
    else if (pid > 0)
    {
        exit(EXIT_SUCCESS);
    }

    syslog(INFO, "daemon started");
    umask(0);

    if (setsid() < 0)
    {
        syslog(WARN, "couldn't set sid");
        exit(EXIT_FAILURE);
    }
    // Technically, the root directory is the only one guaranteed
    // to exist on the filesystem.  Therefore, it's the only safe
    // directory to point our daemon at.  I doubt this is strictly
    // necessary, but remembering to completely rebase a daemon is
    // part of just good hacking etiquette.
    if (0 > chdir("/"))
    {
        syslog(WARN, "couldn't chdir to root");
        exit(EXIT_FAILURE);
    }
    // No extraneous filehandles for us.  Daemons lack stdio, so
    // shut 'em on down.
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
}

/** Creates a server socket that will listen for clients. */
auto make_socket()
{
    const auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
    if (sock < 0)
    {
        syslog(WARN, "couldn't create a server socket");
        exit(EXIT_FAILURE);
    }

    sockaddr_in server;
    memset(static_cast<void*>(&server), 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(PORT);
    if (0 > bind(sock, reinterpret_cast<sockaddr*>(&server),
                 sizeof(server)))
    {
        syslog(WARN, "couldn't bind to port 9120");
        exit(EXIT_FAILURE);
    }
    if (0 > listen(sock, MAX_PENDING_REQUESTS))
    {
        syslog(WARN, "couldn't listen for clients");
        exit(EXIT_FAILURE);
    }
    syslog(INFO, "ready for clients");
    return sock;
}

/** Checks a string to see if it's a valid base-10 number. */
auto is_num(const string& num)
{
    auto b = num.cbegin();
    auto e = num.cend();

    return (e == find_if(b, e, not1(ptr_fun(::isdigit))))
           ? ::atoi(num.c_str())
           : -1;
}

void show_usage(const char* program_name)
{
    cerr <<
         "Usage: " << program_name << " [-vbhsSo -f FILE -p PORT -t TIMEOUT]\n\n" <<
         "-v : print version information\n" <<
         "-b : get information on reporting bugs\n" <<
         "-f : specify an alternate RDS (default: "<< PKGDATADIR <<
         "/NSRLFile.txt)\n" <<
         "-s : allow clients to query server status (default: disabled)\n" <<
         "-h : show this help message\n" <<
         "-p : listen on PORT, between 1024 and 65535 (default: 9120)\n\n";
    exit(EXIT_FAILURE);
}

void parse_options(int argc, char* argv[])
{
    int32_t opt { 0 };

    while (-1 != (opt = getopt(argc, argv, "bsvof:hp:t:S")))
    {
        switch (opt)
        {
        case 'v':
            cerr << argv[0] << " " << PACKAGE_VERSION << "\n\n";
            exit(0);
            break;
        case 'b':
            cerr << argv[0] << " " << PACKAGE_VERSION
                 << "\n" << PACKAGE_URL << "\n" <<
                 "Praise, blame and bug reports to " << PACKAGE_BUGREPORT << ".\n\n" <<
                 "Please be sure to include your operating system, version of your\n" <<
                 "operating system, and a detailed description of how to recreate\n" <<
                 "your bug.\n\n";
            exit(0);
            break;
        case 'f':
			{
            	RDS_LOC = string((const char*) optarg);
            	ifstream infile { RDS_LOC.c_str() };
            	if (not infile)
            	{
                	cerr <<
                    	 "Error: the specified dataset file could not be found.\n\n";
                	exit(EXIT_FAILURE);
            	}
			}
            break;
        case 'h':
            show_usage(argv[0]);
            exit(0);
            break;
        case 'p':
			{
            	auto port_num = string(optarg);
				stringstream ss;
				ss << port_num;
				ss >> PORT;
				if (! ss)
				{
					show_usage(argv[0]);
					exit(EXIT_FAILURE);
				}
			}
            break;
        case 's':
            status_enabled = true;
            break;
        default:
            show_usage(argv[0]);
            exit(EXIT_FAILURE);
        }
    }
}

}
/** An externally available const reference to the hash set. */
const vector<pair64>& hashes { hash_set };

/** An externally available const reference to the variable storing
  * whether or not status checking should be enabled. */
const bool& enable_status { status_enabled };

/** magic happens here */
int main(int argc, char* argv[])
{
    parse_options(argc, argv);
    daemonize();
    load_hashes();

	int32_t client_sock {0};
    int32_t svr_sock { make_socket() };
	sockaddr_in client;
	socklen_t client_length { sizeof(client) };
    bool in_parent = true;

    signal(SIGCHLD, SIG_IGN);

SERVER_LOOP:
    if (0 > (client_sock = accept(svr_sock,
                                  reinterpret_cast<sockaddr*>(&client),
                                  &client_length)))
    {
        syslog(WARN, "dropped a connection");
        goto SERVER_LOOP;
    }

	string ipaddr { inet_ntoa(client.sin_addr) };
	if (0 == fork()) {
		handle_client(client_sock, ipaddr);
    } else {
        close(client_sock);
        goto SERVER_LOOP;
    }
}
<commit_msg>Hmm.  Weird: child processes aren't dying.<commit_after>/* Copyright (c) 2011-2014, Robert J. Hansen <rjh@secret-alchemy.com>
 * and others.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Code standards:
 *   This is a small enough project we don't need a formal coding standard.
 *   That said, here are some helpful tips for people who want to submit
 *   patches:
 *
 *   - If it's not 100% ISO C++11, it won't get in.
 *   - It must compile cleanly and without warnings under both GNU G++
 *     and Clang++, even with "-W -Wextra -ansi -pedantic".
 *     (Exceptions can be made for warnings that are actually compiler
 *     conformance issues: for instance, Clang++ will warn that 'long long'
 *     is a C++11 extension, even when you run it in -std=C++11 mode.)
 *   - C++ offers 'and', 'or' and 'not' keywords instead of &&, || and !.
 *     I like these: I think they're more readable.  Please use them.
 *   - C++11 uniform initialization syntax, please.
 *   - Please try to follow the formatting conventions.  It's mostly
 *     straight-up astyle format, with occasional tweaks where necessary
 *     to get nice hardcopy printouts.
 *   - If you write a new function it must have a Doxygen block
 *     documenting it.
 *
 * Contributor history:
 *
 * Robert J. Hansen <rjh@secret-alchemy.com>
 *   - most everything
 */

#include "../config.h"
#include <sstream>
#include <sys/stat.h>
#include <syslog.h>
#include <set>
#include <string>
#include <time.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <algorithm>
#include <limits.h>
#include "handler.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <memory>
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include <utility>
#include <signal.h>

/* Additional defines necessary on FreeBSD: */
/* Necessary for sockaddr and sockaddr_in structures */
#ifdef __FreeBSD__
#include <sys/socket.h>
#include <netinet/in.h>
#endif

using std::stringstream;
using std::string;
using std::set;
using std::transform;
using std::find_if;
using std::not1;
using std::ptr_fun;
using std::ifstream;
using std::cerr;
using std::vector;
using std::remove_if;
using std::sort;
using std::pair;
using std::unique_ptr;

typedef unsigned long long ULONG64;
typedef pair<ULONG64, ULONG64> pair64;

#define INFO LOG_MAKEPRI(LOG_USER, LOG_INFO)
#define WARN LOG_MAKEPRI(LOG_USER, LOG_WARNING)
#define DEBUG LOG_MAKEPRI(LOG_USER, LOG_DEBUG)
#define CRITICAL LOG_MAKEPRI(LOG_USER, LOG_CRIT)
#define ALERT LOG_MAKEPRI(LOG_USER, LOG_ALERT)
#define EMERGENCY LOG_MAKEPRI(LOG_USER, LOG_EMERG)
#define MAX_PENDING_REQUESTS 20
#define BUFFER_SIZE 128

namespace
{
/** Tracks whether the server should only support protocol 1.0. */
bool old_only { false };

/** Tracks whether the server should support status queries. */
bool status_enabled { false };

/** Tracks whether the server should run as a daemon. */
bool standalone { false };

/** Our set of hashes, represented as a block of contiguous memory.
  * Note that the current NSRL library contains approximately 40
  * million values, each at roughly 32 bytes (rounded to binary
  * powers to make the math easier).  This is 2**25 values times
  * 2**5 bytes each = 2**30 bytes, or about a gig of RAM.
  *
  * Moral of the story: populating this set is computationally
  * expensive. */
vector<pair64> hash_set;

/** Tracks where we look for the location of the
  * reference data set. */
string RDS_LOC { PKGDATADIR "/hashes.txt" };

/** Which port to listen on */
uint16_t PORT { 9120 };

/** Determines whether a string represents a valid uppercase
  * MD5 hash. */

auto good_line(const string& line)
{
    if (line.size() != 32)
        return false;

    auto iter = line.cbegin();
    auto end = line.cend();
    while (iter != end and
            ((*iter >= '0' and *iter <= '9') or
             (*iter >= 'A' and *iter <= 'F')))
        ++iter;
    return (iter == end) ? true : false;
}

/** Loads hashes from disk and stores them in a fast-accessing
  * in-memory data structure.  This will be slow. */
void load_hashes()
{
    vector<char> buf(BUFFER_SIZE, 0);
    uint32_t line_count { 0 }, hash_count { 0 };
    ifstream infile { RDS_LOC.c_str() };

	hash_set.reserve(40000000);

    if (not infile)
    {
        syslog(WARN, "couldn't open hashes file %s",
               RDS_LOC.c_str());
        exit(EXIT_FAILURE);
    }

    while (infile)
    {
        memset(static_cast<void*>(&buf[0]), 0, BUFFER_SIZE);
        infile.getline(&buf[0], BUFFER_SIZE);
        line_count += 1;
        const string line(buf.cbegin(), find(buf.cbegin(), buf.cend(), 0));

        if (0 == line.size())
        {
            continue;
        }

        if (! good_line(line))
        {
            syslog(ALERT, "hash file appears corrupt!  Loading no hashes.");
            syslog(ALERT, "offending line is #%d", line_count);
            syslog(ALERT, "offending line has %d bytes", (int) line.size());
            syslog(ALERT, "Content follows:");
            syslog(ALERT, "%s", line.c_str());
            syslog(ALERT, "shutting down!");
            exit(EXIT_FAILURE);
            return;
        }
        hash_count += 1;

        if (0 == hash_count % 1000000)
        {
            syslog(INFO, "loaded %u million hashes", hash_count / 1000000);
        }

        hash_set.push_back(to_pair64(line));
    }
    infile.close();
    syslog(INFO, "read in %u unique hashes",
           static_cast<uint32_t>(hash_set.size()));
    sort(hash_set.begin(), hash_set.end());
}

/** Converts our application into a proper daemon. */
void daemonize()
{
    const auto pid = fork();
    if (pid < 0)
    {
        syslog(WARN, "couldn't fork!");
        exit(EXIT_FAILURE);
    }
    else if (pid > 0)
    {
        exit(EXIT_SUCCESS);
    }

    syslog(INFO, "daemon started");
    umask(0);

    if (setsid() < 0)
    {
        syslog(WARN, "couldn't set sid");
        exit(EXIT_FAILURE);
    }
    // Technically, the root directory is the only one guaranteed
    // to exist on the filesystem.  Therefore, it's the only safe
    // directory to point our daemon at.  I doubt this is strictly
    // necessary, but remembering to completely rebase a daemon is
    // part of just good hacking etiquette.
    if (0 > chdir("/"))
    {
        syslog(WARN, "couldn't chdir to root");
        exit(EXIT_FAILURE);
    }
    // No extraneous filehandles for us.  Daemons lack stdio, so
    // shut 'em on down.
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
}

/** Creates a server socket that will listen for clients. */
auto make_socket()
{
    const auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
    if (sock < 0)
    {
        syslog(WARN, "couldn't create a server socket");
        exit(EXIT_FAILURE);
    }

    sockaddr_in server;
    memset(static_cast<void*>(&server), 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(PORT);
    if (0 > bind(sock, reinterpret_cast<sockaddr*>(&server),
                 sizeof(server)))
    {
        syslog(WARN, "couldn't bind to port 9120");
        exit(EXIT_FAILURE);
    }
    if (0 > listen(sock, MAX_PENDING_REQUESTS))
    {
        syslog(WARN, "couldn't listen for clients");
        exit(EXIT_FAILURE);
    }
    syslog(INFO, "ready for clients");
    return sock;
}

/** Checks a string to see if it's a valid base-10 number. */
auto is_num(const string& num)
{
    auto b = num.cbegin();
    auto e = num.cend();

    return (e == find_if(b, e, not1(ptr_fun(::isdigit))))
           ? ::atoi(num.c_str())
           : -1;
}

void show_usage(const char* program_name)
{
    cerr <<
         "Usage: " << program_name << " [-vbhsSo -f FILE -p PORT -t TIMEOUT]\n\n" <<
         "-v : print version information\n" <<
         "-b : get information on reporting bugs\n" <<
         "-f : specify an alternate RDS (default: "<< PKGDATADIR <<
         "/NSRLFile.txt)\n" <<
         "-s : allow clients to query server status (default: disabled)\n" <<
         "-h : show this help message\n" <<
         "-p : listen on PORT, between 1024 and 65535 (default: 9120)\n\n";
    exit(EXIT_FAILURE);
}

void parse_options(int argc, char* argv[])
{
    int32_t opt { 0 };

    while (-1 != (opt = getopt(argc, argv, "bsvof:hp:t:S")))
    {
        switch (opt)
        {
        case 'v':
            cerr << argv[0] << " " << PACKAGE_VERSION << "\n\n";
            exit(0);
            break;
        case 'b':
            cerr << argv[0] << " " << PACKAGE_VERSION
                 << "\n" << PACKAGE_URL << "\n" <<
                 "Praise, blame and bug reports to " << PACKAGE_BUGREPORT << ".\n\n" <<
                 "Please be sure to include your operating system, version of your\n" <<
                 "operating system, and a detailed description of how to recreate\n" <<
                 "your bug.\n\n";
            exit(0);
            break;
        case 'f':
			{
            	RDS_LOC = string((const char*) optarg);
            	ifstream infile { RDS_LOC.c_str() };
            	if (not infile)
            	{
                	cerr <<
                    	 "Error: the specified dataset file could not be found.\n\n";
                	exit(EXIT_FAILURE);
            	}
			}
            break;
        case 'h':
            show_usage(argv[0]);
            exit(0);
            break;
        case 'p':
			{
            	auto port_num = string(optarg);
				stringstream ss;
				ss << port_num;
				ss >> PORT;
				if (! ss)
				{
					show_usage(argv[0]);
					exit(EXIT_FAILURE);
				}
			}
            break;
        case 's':
            status_enabled = true;
            break;
        default:
            show_usage(argv[0]);
            exit(EXIT_FAILURE);
        }
    }
}

}
/** An externally available const reference to the hash set. */
const vector<pair64>& hashes { hash_set };

/** An externally available const reference to the variable storing
  * whether or not status checking should be enabled. */
const bool& enable_status { status_enabled };

/** magic happens here */
int main(int argc, char* argv[])
{
    parse_options(argc, argv);
    daemonize();
    load_hashes();

	int32_t client_sock {0};
    int32_t svr_sock { make_socket() };
	sockaddr_in client;
	socklen_t client_length { sizeof(client) };
    bool in_parent = true;

    signal(SIGCHLD, SIG_IGN);

SERVER_LOOP:
    if (0 > (client_sock = accept(svr_sock,
                                  reinterpret_cast<sockaddr*>(&client),
                                  &client_length)))
    {
        syslog(WARN, "dropped a connection");
        goto SERVER_LOOP;
    }

	string ipaddr { inet_ntoa(client.sin_addr) };
    if (0 == fork()) {
		handle_client(client_sock, ipaddr);
        return 0;
    }
    goto SERVER_LOOP;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <cstdio>

#include "json_object.h"
#include "json_loader.h"
#include "regex.h"
#include "grammar.h"
#include "grammar_loader.h"
#include "scope.h"
#include "tokenizer.h"

using namespace std;

void error(const char* msg) {
    cerr << msg << endl;
    exit(-1);
}

int file_size(FILE* file) {
    if (fseek(file, 0, SEEK_END) != 0) error("failed to seek");
    int size = ftell(file);
    if (size == -1) error("failed to tell size");
    if (fseek(file, 0, SEEK_SET) != 0) error("failed to seek");

    return size;
}

char* read_all(FILE* file, int& size) {
    size = file_size(file);
    char* buffer = new char[size];
    if(buffer == nullptr) error("can't allocate memeory");

    if(fread(buffer, size, 1, file) != 1) error("cannot read file");

    return buffer;
}

string read_file(const string& path) {
    FILE* file = fopen(path.c_str(), "rb");
    if(file == nullptr) error("error open file");

    char* buffer;
    int size;
    buffer = read_all(file, size);

    fclose(file);
    return string(buffer, buffer + size);
}

int main() {
    string syntax_data = read_file("test/c.json");
    shl::GrammarLoader loader;
    shl::Grammar g = loader.load(syntax_data);

    string source = read_file("test/linker_md.c");

    shl::Tokenizer tokenizer;
    auto tokens = tokenizer.tokenize(g, source);

}
<commit_msg>debug all the tokens<commit_after>#include <iostream>
#include <cstdio>

#include "json_object.h"
#include "json_loader.h"
#include "regex.h"
#include "grammar.h"
#include "grammar_loader.h"
#include "scope.h"
#include "tokenizer.h"

using namespace std;

void error(const char* msg) {
    cerr << msg << endl;
    exit(-1);
}

int file_size(FILE* file) {
    if (fseek(file, 0, SEEK_END) != 0) error("failed to seek");
    int size = ftell(file);
    if (size == -1) error("failed to tell size");
    if (fseek(file, 0, SEEK_SET) != 0) error("failed to seek");

    return size;
}

char* read_all(FILE* file, int& size) {
    size = file_size(file);
    char* buffer = new char[size];
    if(buffer == nullptr) error("can't allocate memeory");

    if(fread(buffer, size, 1, file) != 1) error("cannot read file");

    return buffer;
}

string read_file(const string& path) {
    FILE* file = fopen(path.c_str(), "rb");
    if(file == nullptr) error("error open file");

    char* buffer;
    int size;
    buffer = read_all(file, size);

    fclose(file);
    return string(buffer, buffer + size);
}

int main() {
    string syntax_data = read_file("test/c.json");
    shl::GrammarLoader loader;
    shl::Grammar g = loader.load(syntax_data);

    string source = read_file("test/linker_md.c");

    shl::Tokenizer tokenizer;
    auto tokens = tokenizer.tokenize(g, source);

    for (auto& pair : tokens) {
        cout << "(" << pair.first.position << "," << pair.first.end() - 1 << ")" << pair.second.name() << endl;
    }
    cout << tokens.size() << endl;

}
<|endoftext|>


<commit_before>#include "assets.hpp"

//////////
// Code //

// Creating an empty set of assets.
Assets::Assets() { }

// Destructing assets.
Assets::~Assets() {
    for (auto it = this->textures.begin(); it != this->textures.end(); it++)
        delete std::get<1>(*it);
    for (auto it = this->shaders.begin(); it != this->shaders.end(); it++)
        delete std::get<1>(*it);
}

// Adding different kinds of assets.
void Assets::addAnimation(std::string path, std::vector<Texture> textures, float frameLength, bool doesLoop) {
    this->animations[path] = new Animation(textures, frameLength, doesLoop);
}

void Assets::addAnimation(std::string path, std::vector<Texture> textures, float frameLength) {
    this->addAnimation(path, textures, frameLength);
}

void Assets::addTexture(std::string path) {
    this->textures[path] = new Texture(path);
}

void Assets::addShader(std::string path) {
    this->shaders[path] = new Shader(path);
}

// Getting the vector of animation timers.
std::vector<Timer*> Assets::getAnimationTimers() const {
    std::vector<Timer*> timers;

    for (auto it = this->animations.begin(); it != this->animations.end(); it++)
        if (std::get<1>(*it) != nullptr)
            timers.push_back(std::get<1>(*it)->getTimer());

    return timers;
}

// Getting different kind of assets.
Animation Assets::getAnimation(std::string path) const { return *this->animations.at(path); }
Texture Assets::getTexture(std::string path) const { return *this->textures.at(path); }
Shader Assets::getShader(std::string path) const { return *this->shaders.at(path); }

// Loading the default set of assets into the ref.
void loadAssets(Assets& assets) {
    // Loading all of the asteroid frames (I should make spritesheet loading and
    // move them all into a single thing).
    assets.addTexture("res/asteroid/01/01.png");
    assets.addTexture("res/asteroid/01/02.png");
    assets.addTexture("res/asteroid/01/03.png");
    assets.addTexture("res/asteroid/01/04.png");
    assets.addTexture("res/asteroid/01/05.png");
    assets.addTexture("res/asteroid/01/06.png");
    assets.addTexture("res/asteroid/01/07.png");
    assets.addTexture("res/asteroid/01/08.png");
    assets.addTexture("res/asteroid/01/09.png");
    assets.addTexture("res/asteroid/01/10.png");
    assets.addTexture("res/asteroid/01/11.png");
    assets.addTexture("res/asteroid/01/12.png");
    assets.addTexture("res/asteroid/01/13.png");
    assets.addTexture("res/asteroid/01/14.png");

    // The background.
    assets.addTexture("res/background.png");

    // The bullet.
    assets.addTexture("res/bullet.png");

    // The enemy.
    assets.addTexture("res/enemy/01.png");
    assets.addTexture("res/enemy/02.png");
    assets.addTexture("res/enemy/03.png");

    // The game shader.
    assets.addShader("res/game2d");

    // The player.
    assets.addTexture("res/player/01.png");
    assets.addTexture("res/player/02.png");
    assets.addTexture("res/player/03.png");
    assets.addTexture("res/player/04.png");
}
<commit_msg>Actually loaded an animation (I need to make this a little better.<commit_after>#include "assets.hpp"

//////////
// Code //

// Creating an empty set of assets.
Assets::Assets() { }

// Destructing assets.
Assets::~Assets() {
    for (auto it = this->animations.begin(); it != this->animations.end(); it++)
        delete std::get<1>(*it);
    for (auto it = this->textures.begin(); it != this->textures.end(); it++)
        delete std::get<1>(*it);
    for (auto it = this->shaders.begin(); it != this->shaders.end(); it++)
        delete std::get<1>(*it);
}

// Adding different kinds of assets.
void Assets::addAnimation(std::string path, std::vector<Texture> textures, float frameLength, bool doesLoop) {
    this->animations[path] = new Animation(textures, frameLength, doesLoop);
}

void Assets::addAnimation(std::string path, std::vector<Texture> textures, float frameLength) {
    this->addAnimation(path, textures, frameLength, true);
}

void Assets::addTexture(std::string path) {
    this->textures[path] = new Texture(path);
}

void Assets::addShader(std::string path) {
    this->shaders[path] = new Shader(path);
}

// Getting the vector of animation timers.
std::vector<Timer*> Assets::getAnimationTimers() const {
    std::vector<Timer*> timers;

    for (auto it = this->animations.begin(); it != this->animations.end(); it++)
        if (std::get<1>(*it) != nullptr)
            timers.push_back(std::get<1>(*it)->getTimer());

    return timers;
}

// Getting different kind of assets.
Animation Assets::getAnimation(std::string path) const { return *this->animations.at(path); }
Texture Assets::getTexture(std::string path) const { return *this->textures.at(path); }
Shader Assets::getShader(std::string path) const { return *this->shaders.at(path); }

// Loading the default set of assets into the ref.
void loadAssets(Assets& assets) {
    // Loading all of the asteroid frames (I should make spritesheet loading and
    // move them all into a single thing).
    assets.addTexture("res/asteroid/01/01.png");
    assets.addTexture("res/asteroid/01/02.png");
    assets.addTexture("res/asteroid/01/03.png");
    assets.addTexture("res/asteroid/01/04.png");
    assets.addTexture("res/asteroid/01/05.png");
    assets.addTexture("res/asteroid/01/06.png");
    assets.addTexture("res/asteroid/01/07.png");
    assets.addTexture("res/asteroid/01/08.png");
    assets.addTexture("res/asteroid/01/09.png");
    assets.addTexture("res/asteroid/01/10.png");
    assets.addTexture("res/asteroid/01/11.png");
    assets.addTexture("res/asteroid/01/12.png");
    assets.addTexture("res/asteroid/01/13.png");
    assets.addTexture("res/asteroid/01/14.png");

    // The background.
    assets.addTexture("res/background.png");

    // The bullet.
    assets.addTexture("res/bullet.png");

    // The enemy.
    assets.addTexture("res/enemy/01.png");
    assets.addTexture("res/enemy/02.png");
    assets.addTexture("res/enemy/03.png");

    // The game shader.
    assets.addShader("res/game2d");

    // The player.
    assets.addTexture("res/player/01.png");
    assets.addTexture("res/player/02.png");
    assets.addTexture("res/player/03.png");
    assets.addTexture("res/player/04.png");

    // Adding the player animation.
    std::vector<Texture> player {
        assets.getTexture("res/player/01.png"),
        assets.getTexture("res/player/02.png"),
        assets.getTexture("res/player/03.png"),
        assets.getTexture("res/player/04.png")
    };

    assets.addAnimation("player", player, 0.3);
}
<|endoftext|>


<commit_before>/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2008 Simon Peter <dn.tlp@gmx.net>, et al.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * got.cpp - GOT Player by Stas'M <binarymaster@mail.ru>
 *
 * Based on IMF Player by Simon Peter <dn.tlp@gmx.net>
 *
 * REFERENCES:
 * http://www.shikadi.net/moddingwiki/God_of_Thunder_Music_Format
 */

#include <string.h>

#include "got.h"
#include "database.h"

/*** public methods *************************************/

CPlayer *CgotPlayer::factory(Copl *newopl)
{
	return new CgotPlayer(newopl);
}

bool CgotPlayer::load(const std::string &filename, const CFileProvider &fp)
{
	binistream *f = fp.open(filename); if(!f) return false;

	// file validation section
	{
		if (!fp.extension(filename, ".got"))
		{
			fp.close(f);
			return false;
		}
		if (fp.filesize(f) % 3 != 0 || fp.filesize(f) < 9)
		{
			fp.close(f);
			return false;
		}
		if (f->readInt(2) != 1)
		{
			fp.close(f);
			return false;
		}
		f->seek(fp.filesize(f) - 4);
		if (f->readInt(4) != 0)
		{
			fp.close(f);
			return false;
		}
	}
	f->seek(0);
	CAdPlugDatabase::CKey key(*f);
	f->seek(2);

	// load section
	size = fp.filesize(f) / 3 - 1;

	data = new Sdata[size];
	for(unsigned int i = 0; i < size; i++) {
		data[i].time = f->readInt(1);
		data[i].reg = f->readInt(1); data[i].val = f->readInt(1);
	}

	CAdPlugDatabase::CKey menu_music;
	menu_music.crc16 = 0xA649;
	menu_music.crc32 = 0x0B61B41F;

	if (key == menu_music)
		rate = 140.0f;
	else
		rate = 120.0f;

	fp.close(f);
	rewind(0);
	return true;
}

bool CgotPlayer::update()
{
	do {
		del = data[pos].time;
		opl->write(data[pos].reg, data[pos].val);
		pos++;
	} while (!del && pos < size);

	if (pos >= size) {
		pos = 0;
		songend = true;
	} else
		timer = rate / (float)del;

	return !songend;
}

void CgotPlayer::rewind(int subsong)
{
	pos = 0; del = 0; timer = rate; songend = false;
	opl->init(); opl->write(1,32); // go to OPL2 mode
}
<commit_msg>Revert CRCs calculated by AdPlug<commit_after>/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2008 Simon Peter <dn.tlp@gmx.net>, et al.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * got.cpp - GOT Player by Stas'M <binarymaster@mail.ru>
 *
 * Based on IMF Player by Simon Peter <dn.tlp@gmx.net>
 *
 * REFERENCES:
 * http://www.shikadi.net/moddingwiki/God_of_Thunder_Music_Format
 */

#include <string.h>

#include "got.h"
#include "database.h"

/*** public methods *************************************/

CPlayer *CgotPlayer::factory(Copl *newopl)
{
	return new CgotPlayer(newopl);
}

bool CgotPlayer::load(const std::string &filename, const CFileProvider &fp)
{
	binistream *f = fp.open(filename); if(!f) return false;

	// file validation section
	{
		if (!fp.extension(filename, ".got"))
		{
			fp.close(f);
			return false;
		}
		if (fp.filesize(f) % 3 != 0 || fp.filesize(f) < 9)
		{
			fp.close(f);
			return false;
		}
		if (f->readInt(2) != 1)
		{
			fp.close(f);
			return false;
		}
		f->seek(fp.filesize(f) - 4);
		if (f->readInt(4) != 0)
		{
			fp.close(f);
			return false;
		}
	}
	f->seek(0);
	CAdPlugDatabase::CKey key(*f);
	f->seek(2);

	// load section
	size = fp.filesize(f) / 3 - 1;

	data = new Sdata[size];
	for(unsigned int i = 0; i < size; i++) {
		data[i].time = f->readInt(1);
		data[i].reg = f->readInt(1); data[i].val = f->readInt(1);
	}

	CAdPlugDatabase::CKey menu_music;
	menu_music.crc16 = 0xB627;
	menu_music.crc32 = 0x72036C41;

	if (key == menu_music)
		rate = 140.0f;
	else
		rate = 120.0f;

	fp.close(f);
	rewind(0);
	return true;
}

bool CgotPlayer::update()
{
	do {
		del = data[pos].time;
		opl->write(data[pos].reg, data[pos].val);
		pos++;
	} while (!del && pos < size);

	if (pos >= size) {
		pos = 0;
		songend = true;
	} else
		timer = rate / (float)del;

	return !songend;
}

void CgotPlayer::rewind(int subsong)
{
	pos = 0; del = 0; timer = rate; songend = false;
	opl->init(); opl->write(1,32); // go to OPL2 mode
}
<|endoftext|>


<commit_before>#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string>
#include "backup.hpp"

Backup::Backup()
{}

Backup::Backup(const string &filename)
{
}

bool Backup::execute(const vector<string> &lhs, const vector<string> &rhs)
{
	int outback = dup(1);
	string filename = "testA.txt";
	int out;
	out = open(filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
	dup2(out, 1);

	char* args[512];
	unsigned i;
	for (i = 0; i < lhs.size(); i++)
	{
		args[i] = (char*)lhs[i].c_str();
	}
	args[i] = NULL;

	char* rightArgs[512];
	unsigned j;
	for (j = 0; j < rhs.size(); j++)
	{
		rightArgs[j] = (char*)rhs[j].c_str();
	}
	rightArgs[i] = NULL;

	pid_t pid;
	pid = fork();
	int status;

	if (pid == -1)
	{
		perror("fork");
		exit(1);
	}
	if (pid == 0) // child process
	{
		if (execvp(args[0], args) == -1)
		{
			perror("exec");
			//exit(1);

		}
		exit(1);
	}
	else // parent process
	{
		if (waitpid(pid, &status, 0) == -1)
		{
			perror("wait");
			exit(1);
		}
		if (WEXITSTATUS(status) != 0)
		{
			return false;
		}
		dup2(outback, 1);
		//	dup2(outback, out);
		close(out);
	}

		pid_t pid2;
		pid2 = fork();
		if (pid2 == -1)
		{
			perror("fork");
			exit(1);
		}
		if (pid2 == 0) // child process
		{
			int in;
			in = open(filename.c_str(), O_RDONLY);
			dup2(in, 0);
			close(in);
			execvp(args[0], args);
			exit(0);
		}
		else // parent process
		{
			if (waitpid(pid2, &status, 0) == -1)
			{
				perror("wait");
				exit(1);
			}
			if (WEXITSTATUS(status) != 0)
			{
				return false;
			}
		}

	return true;
}

<commit_msg>added backup<commit_after>#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string>
#include "backup.hpp"

Backup::Backup()
{}

Backup::Backup(const string &filename)
{
}

bool Backup::execute(const vector<string> &lhs, const vector<string> &rhs)
{
	int outback = dup(1);
	string filename = "testA.txt";
	int out;
	out = open(filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
	dup2(out, 1);

	char* args[512];
	unsigned i;
	for (i = 0; i < lhs.size(); i++)
	{
		args[i] = (char*)lhs[i].c_str();
	}
	args[i] = NULL;

	char* rightArgs[512];
	unsigned j;
	for (j = 0; j < rhs.size(); j++)
	{
		rightArgs[j] = (char*)rhs[j].c_str();
	}
	rightArgs[i] = NULL;

	pid_t pid;
	pid = fork();
	int status;

	if (pid == -1)
	{
		perror("fork");
		exit(1);
	}
	if (pid == 0) // child process
	{
		if (execvp(args[0], args) == -1)
		{
			perror("exec");
			//exit(1);

		}
		exit(1);
	}
	else // parent process
	{
		if (waitpid(pid, &status, 0) == -1)
		{
			perror("wait");
			exit(1);
		}
		if (WEXITSTATUS(status) != 0)
		{
			return false;
		}
		dup2(outback, 1);
		//	dup2(outback, out);
		close(out);
	}

		pid_t pid2;
		pid2 = fork();
		if (pid2 == -1)
		{
			perror("fork");
			exit(1);
		}
		if (pid2 == 0) // child process
		{
			int in;
			in = open(filename.c_str(), O_RDONLY);
			dup2(in, 0);
			close(in);
			execvp(rightArgs[0], rightArgs);
			exit(0);
		}
		else // parent process
		{
			if (waitpid(pid2, &status, 0) == -1)
			{
				perror("wait");
				exit(1);
			}
			if (WEXITSTATUS(status) != 0)
			{
				return false;
			}
		}

	return true;
}

<|endoftext|>


<commit_before>/**
 * Base64 encoding and decoding of strings. Uses '+' for 62, '/' for 63, '=' for padding
 */

#ifndef BASE64_H_INCLUDED
#define BASE64_H_INCLUDED

/* binary_to_base64:
 *   Description:
 *     Converts a single byte from a binary value to the corresponding base64 character
 *   Parameters:
 *     v - Byte to convert
 *   Returns:
 *     ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character
 *     and 255 is returned
 */
unsigned char binary_to_base64(unsigned char v);

/* base64_to_binary:
 *   Description:
 *     Converts a single byte from a base64 character to the corresponding binary value
 *   Parameters:
 *     c - Base64 character (as ascii code)
 *   Returns:
 *     6-bit binary value
 */
unsigned char base64_to_binary(unsigned char c);

/* encode_base64_length:
 *   Description:
 *     Calculates length of base64 string needed for a given number of binary bytes
 *   Parameters:
 *     input_length - Amount of binary data in bytes
 *   Returns:
 *     Number of base64 characters needed to encode input_length bytes of binary data
 */
unsigned int encode_base64_length(unsigned int input_length);

/* decode_base64_length:
 *   Description:
 *     Calculates number of bytes of binary data in a base64 string
 *     Variant that does not use input_length no longer used within library, retained for API compatibility
 *   Parameters:
 *     input - Base64-encoded null-terminated string
 *     input_length (optional) - Number of bytes to read from input pointer
 *   Returns:
 *     Number of bytes of binary data in input
 */
unsigned int decode_base64_length(unsigned char input[]);
unsigned int decode_base64_length(unsigned char input[], unsigned int input_length);

/* encode_base64:
 *   Description:
 *     Converts an array of bytes to a base64 null-terminated string
 *   Parameters:
 *     input - Pointer to input data
 *     input_length - Number of bytes to read from input pointer
 *     output - Pointer to output string. Null terminator will be added automatically
 *   Returns:
 *     Length of encoded string in bytes (not including null terminator)
 */
unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]);

/* decode_base64:
 *   Description:
 *     Converts a base64 null-terminated string to an array of bytes
 *   Parameters:
 *     input - Pointer to input string
 *     input_length (optional) - Number of bytes to read from input pointer
 *     output - Pointer to output array
 *   Returns:
 *     Number of bytes in the decoded binary
 */
unsigned int decode_base64(unsigned char input[], unsigned char output[]);
unsigned int decode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]);

unsigned char binary_to_base64(unsigned char v) {
  // Capital letters - 'A' is ascii 65 and base64 0
  if(v < 26) return v + 'A';

  // Lowercase letters - 'a' is ascii 97 and base64 26
  if(v < 52) return v + 71;

  // Digits - '0' is ascii 48 and base64 52
  if(v < 62) return v - 4;

  #ifdef BASE64_URL
  // '-' is ascii 45 and base64 62
  if(v == 62) return '-';
  #else
  // '+' is ascii 43 and base64 62
  if(v == 62) return '+';
  #endif

  #ifdef BASE64_URL
  // '_' is ascii 95 and base64 62
  if(v == 63) return '_';
  #else
  // '/' is ascii 47 and base64 63
  if(v == 63) return '/';
  #endif

  return 64;
}

unsigned char base64_to_binary(unsigned char c) {
  // Capital letters - 'A' is ascii 65 and base64 0
  if('A' <= c && c <= 'Z') return c - 'A';

  // Lowercase letters - 'a' is ascii 97 and base64 26
  if('a' <= c && c <= 'z') return c - 71;

  // Digits - '0' is ascii 48 and base64 52
  if('0' <= c && c <= '9') return c + 4;

  #ifdef BASE64_URL
  // '-' is ascii 45 and base64 62
  if(c == '-') return 62;
  #else
  // '+' is ascii 43 and base64 62
  if(c == '+') return 62;
  #endif

  #ifdef BASE64_URL
  // '_' is ascii 95 and base64 62
  if(c == '_') return 63;
  #else
  // '/' is ascii 47 and base64 63
  if(c == '/') return 63;
  #endif

  return 255;
}

unsigned int encode_base64_length(unsigned int input_length) {
  return (input_length + 2)/3*4;
}

unsigned int decode_base64_length(unsigned char input[]) {
  return decode_base64_length(input, -1);
}

unsigned int decode_base64_length(unsigned char input[], unsigned int input_length) {
  unsigned char *start = input;

  while(base64_to_binary(input[0]) < 64 && (unsigned int) (input - start) < input_length) {
    ++input;
  }

  input_length = (unsigned int) (input - start);
  return input_length/4*3 + (input_length % 4 ? input_length % 4 - 1 : 0);
}

unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
  unsigned int full_sets = input_length/3;

  // While there are still full sets of 24 bits...
  for(unsigned int i = 0; i < full_sets; ++i) {
    output[0] = binary_to_base64(                         input[0] >> 2);
    output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
    output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
    output[3] = binary_to_base64( input[2] & 0x3F);

    input += 3;
    output += 4;
  }

  switch(input_length % 3) {
    case 0:
      output[0] = '\0';
      break;
    case 1:
      output[0] = binary_to_base64(                         input[0] >> 2);
      output[1] = binary_to_base64((input[0] & 0x03) << 4);
      output[2] = '=';
      output[3] = '=';
      output[4] = '\0';
      break;
    case 2:
      output[0] = binary_to_base64(                         input[0] >> 2);
      output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
      output[2] = binary_to_base64((input[1] & 0x0F) << 2);
      output[3] = '=';
      output[4] = '\0';
      break;
  }

  return encode_base64_length(input_length);
}

unsigned int decode_base64(unsigned char input[], unsigned char output[]) {
  return decode_base64(input, -1, output);
}

unsigned int decode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
  unsigned int output_length = decode_base64_length(input, input_length);

  // While there are still full sets of 24 bits...
  for(unsigned int i = 2; i < output_length; i += 3) {
    output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
    output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
    output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);

    input += 4;
    output += 3;
  }

  switch(output_length % 3) {
    case 1:
      output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
      break;
    case 2:
      output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
      output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
      break;
  }

  return output_length;
}

#endif // ifndef
<commit_msg>Use const in function signatures as requested in #8<commit_after>/**
 * Base64 encoding and decoding of strings. Uses '+' for 62, '/' for 63, '=' for padding
 */

#ifndef BASE64_H_INCLUDED
#define BASE64_H_INCLUDED

/* binary_to_base64:
 *   Description:
 *     Converts a single byte from a binary value to the corresponding base64 character
 *   Parameters:
 *     v - Byte to convert
 *   Returns:
 *     ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character
 *     and 255 is returned
 */
unsigned char binary_to_base64(unsigned char v);

/* base64_to_binary:
 *   Description:
 *     Converts a single byte from a base64 character to the corresponding binary value
 *   Parameters:
 *     c - Base64 character (as ascii code)
 *   Returns:
 *     6-bit binary value
 */
unsigned char base64_to_binary(unsigned char c);

/* encode_base64_length:
 *   Description:
 *     Calculates length of base64 string needed for a given number of binary bytes
 *   Parameters:
 *     input_length - Amount of binary data in bytes
 *   Returns:
 *     Number of base64 characters needed to encode input_length bytes of binary data
 */
unsigned int encode_base64_length(unsigned int input_length);

/* decode_base64_length:
 *   Description:
 *     Calculates number of bytes of binary data in a base64 string
 *     Variant that does not use input_length no longer used within library, retained for API compatibility
 *   Parameters:
 *     input - Base64-encoded null-terminated string
 *     input_length (optional) - Number of bytes to read from input pointer
 *   Returns:
 *     Number of bytes of binary data in input
 */
unsigned int decode_base64_length(const unsigned char input[]);
unsigned int decode_base64_length(const unsigned char input[], unsigned int input_length);

/* encode_base64:
 *   Description:
 *     Converts an array of bytes to a base64 null-terminated string
 *   Parameters:
 *     input - Pointer to input data
 *     input_length - Number of bytes to read from input pointer
 *     output - Pointer to output string. Null terminator will be added automatically
 *   Returns:
 *     Length of encoded string in bytes (not including null terminator)
 */
unsigned int encode_base64(const unsigned char input[], unsigned int input_length, unsigned char output[]);

/* decode_base64:
 *   Description:
 *     Converts a base64 null-terminated string to an array of bytes
 *   Parameters:
 *     input - Pointer to input string
 *     input_length (optional) - Number of bytes to read from input pointer
 *     output - Pointer to output array
 *   Returns:
 *     Number of bytes in the decoded binary
 */
unsigned int decode_base64(const unsigned char input[], unsigned char output[]);
unsigned int decode_base64(const unsigned char input[], unsigned int input_length, unsigned char output[]);

unsigned char binary_to_base64(unsigned char v) {
  // Capital letters - 'A' is ascii 65 and base64 0
  if(v < 26) return v + 'A';

  // Lowercase letters - 'a' is ascii 97 and base64 26
  if(v < 52) return v + 71;

  // Digits - '0' is ascii 48 and base64 52
  if(v < 62) return v - 4;

  #ifdef BASE64_URL
  // '-' is ascii 45 and base64 62
  if(v == 62) return '-';
  #else
  // '+' is ascii 43 and base64 62
  if(v == 62) return '+';
  #endif

  #ifdef BASE64_URL
  // '_' is ascii 95 and base64 62
  if(v == 63) return '_';
  #else
  // '/' is ascii 47 and base64 63
  if(v == 63) return '/';
  #endif

  return 64;
}

unsigned char base64_to_binary(unsigned char c) {
  // Capital letters - 'A' is ascii 65 and base64 0
  if('A' <= c && c <= 'Z') return c - 'A';

  // Lowercase letters - 'a' is ascii 97 and base64 26
  if('a' <= c && c <= 'z') return c - 71;

  // Digits - '0' is ascii 48 and base64 52
  if('0' <= c && c <= '9') return c + 4;

  #ifdef BASE64_URL
  // '-' is ascii 45 and base64 62
  if(c == '-') return 62;
  #else
  // '+' is ascii 43 and base64 62
  if(c == '+') return 62;
  #endif

  #ifdef BASE64_URL
  // '_' is ascii 95 and base64 62
  if(c == '_') return 63;
  #else
  // '/' is ascii 47 and base64 63
  if(c == '/') return 63;
  #endif

  return 255;
}

unsigned int encode_base64_length(unsigned int input_length) {
  return (input_length + 2)/3*4;
}

unsigned int decode_base64_length(const unsigned char input[]) {
  return decode_base64_length(input, -1);
}

unsigned int decode_base64_length(const unsigned char input[], unsigned int input_length) {
  const unsigned char *start = input;

  while(base64_to_binary(input[0]) < 64 && (unsigned int) (input - start) < input_length) {
    ++input;
  }

  input_length = (unsigned int) (input - start);
  return input_length/4*3 + (input_length % 4 ? input_length % 4 - 1 : 0);
}

unsigned int encode_base64(const unsigned char input[], unsigned int input_length, unsigned char output[]) {
  unsigned int full_sets = input_length/3;

  // While there are still full sets of 24 bits...
  for(unsigned int i = 0; i < full_sets; ++i) {
    output[0] = binary_to_base64(                         input[0] >> 2);
    output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
    output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
    output[3] = binary_to_base64( input[2] & 0x3F);

    input += 3;
    output += 4;
  }

  switch(input_length % 3) {
    case 0:
      output[0] = '\0';
      break;
    case 1:
      output[0] = binary_to_base64(                         input[0] >> 2);
      output[1] = binary_to_base64((input[0] & 0x03) << 4);
      output[2] = '=';
      output[3] = '=';
      output[4] = '\0';
      break;
    case 2:
      output[0] = binary_to_base64(                         input[0] >> 2);
      output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
      output[2] = binary_to_base64((input[1] & 0x0F) << 2);
      output[3] = '=';
      output[4] = '\0';
      break;
  }

  return encode_base64_length(input_length);
}

unsigned int decode_base64(const unsigned char input[], unsigned char output[]) {
  return decode_base64(input, -1, output);
}

unsigned int decode_base64(const unsigned char input[], unsigned int input_length, unsigned char output[]) {
  unsigned int output_length = decode_base64_length(input, input_length);

  // While there are still full sets of 24 bits...
  for(unsigned int i = 2; i < output_length; i += 3) {
    output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
    output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
    output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);

    input += 4;
    output += 3;
  }

  switch(output_length % 3) {
    case 1:
      output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
      break;
    case 2:
      output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
      output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
      break;
  }

  return output_length;
}

#endif // ifndef
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2015-2016.
// Distributed under the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#include "config.hpp"
#include "dataset.hpp" //Dataset handling

//Include methods
#include "standard.hpp" //Method 0
#include "holistic.hpp" //Method 1
#include "patches.hpp"  //Method 2

//Method-Agnostic commands
#include "evaluate_features.hpp" //evaluate_patches command

#include <atomic>
#include <mutex>
#include "dll/util/timers.hpp"

namespace {

using string_vector = std::vector<std::string>;

template <typename Set>
void extract_names(spot_dataset& dataset, Set& set, string_vector& train_image_names, string_vector& train_word_names, string_vector& test_image_names, string_vector& valid_image_names) {
    std::cout << set.train_set.size() << " training line images in set" << std::endl;
    std::cout << set.validation_set.size() << " validation line images in set" << std::endl;
    std::cout << set.test_set.size() << " test line images in set" << std::endl;

    for (auto& word_image : dataset.word_images) {
        auto& name = word_image.first;
        for (auto& train_image : set.train_set) {
            if (name.find(train_image) == 0) {
                train_image_names.push_back(name);
                train_word_names.emplace_back(name.begin(), name.end() - 4);
                break;
            }
        }
        for (auto& test_image : set.test_set) {
            if (name.find(test_image) == 0) {
                test_image_names.push_back(name);
                break;
            }
        }
        for (auto& valid_image : set.validation_set) {
            if (name.find(valid_image) == 0) {
                valid_image_names.push_back(name);
                break;
            }
        }
    }

    std::cout << train_image_names.size() << " training word images in set" << std::endl;
    std::cout << valid_image_names.size() << " validation word images in set" << std::endl;
    std::cout << test_image_names.size() << " test word images in set" << std::endl;
}

spot_dataset read_dataset(config& conf) {
    decltype(auto) dataset_path = conf.files[0];
    decltype(auto) cv_set = conf.files[1];

    std::cout << " Dataset: " << dataset_path << std::endl;
    std::cout << "     Set: " << cv_set << std::endl;

    spot_dataset dataset;

    if(conf.washington){
        dataset = read_washington(dataset_path);
    } else if(conf.parzival){
        dataset = read_parzival(dataset_path);
    } else if(conf.iam){
        dataset = read_iam(dataset_path);
    } else {
        std::cerr << "Invalid configuration of the dataset" << std::endl;
    }

    std::cout << dataset.line_images.size() << " line images loaded from the dataset" << std::endl;
    std::cout << dataset.word_images.size() << " word images loaded from the dataset" << std::endl;

    if (conf.washington) {
        conf.cv_full_path = dataset_path + "/sets/" + cv_set + "/";
    } else if (conf.parzival) {
        conf.cv_full_path = dataset_path + "/sets1/";
    } else if (conf.iam) {
        conf.cv_full_path = dataset_path + "/sets/";
    }

    conf.data_full_path = dataset_path + "/data/word_images_normalized/";

    return dataset;
}

int command_train(config& conf) {
    if (conf.files.size() < 2) {
        std::cout << "Train needs the path to the dataset and the cv set to use" << std::endl;
        return -1;
    }

    auto dataset = read_dataset(conf);

    decltype(auto) cv_set = conf.files[1];

    if (!dataset.sets.count(cv_set)) {
        std::cout << "The subset \"" << cv_set << "\" does not exist" << std::endl;
        return -1;
    }

    auto& set = dataset.sets[cv_set];

    string_vector train_image_names, train_word_names, test_image_names, valid_image_names;

    extract_names(dataset, set, train_image_names, train_word_names, test_image_names, valid_image_names);

    switch (conf.method) {
        case Method::Marti2001:
        case Method::Rath2007:
        case Method::Rath2003:
        case Method::Rodriguez2008:
        case Method::Vinciarelli2004:
        case Method::Terasawa2009:
            standard_train(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Holistic:
            holistic_train(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Patches:
            patches_train(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
    }

    dll::dump_timers();

    return 0;
}

int command_features(config& conf) {
    if (conf.files.size() < 2) {
        std::cout << "features needs the path to the dataset and the cv set to use" << std::endl;
        return -1;
    }

    auto dataset = read_dataset(conf);

    decltype(auto) cv_set = conf.files[1];

    if (!dataset.sets.count(cv_set)) {
        std::cout << "The subset \"" << cv_set << "\" does not exist" << std::endl;
        return -1;
    }

    auto& set = dataset.sets[cv_set];

    string_vector train_image_names, train_word_names, test_image_names, valid_image_names;

    extract_names(dataset, set, train_image_names, train_word_names, test_image_names, valid_image_names);

    switch (conf.method) {
        case Method::Marti2001:
        case Method::Rath2007:
        case Method::Rath2003:
        case Method::Rodriguez2008:
        case Method::Vinciarelli2004:
        case Method::Terasawa2009:
            standard_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Holistic:
            holistic_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Patches:
            patches_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
    }

    return 0;
}

int command_evaluate_features(config& conf) {
    if (conf.files.size() < 2) {
        std::cout << "evaluate_features needs the path to the dataset and the cv set to use" << std::endl;
        return -1;
    }

    auto dataset = read_dataset(conf);

    decltype(auto) cv_set = conf.files[1];

    if (!dataset.sets.count(cv_set)) {
        std::cout << "The subset \"" << cv_set << "\" does not exist" << std::endl;
        return -1;
    }

    auto& set = dataset.sets[cv_set];

    string_vector train_image_names, train_word_names, test_image_names, valid_image_names;

    extract_names(dataset, set, train_image_names, train_word_names, test_image_names, valid_image_names);

    //TODO At this point, we need to pass the features to DTW but we ned to match them to images first for evaluation
    //     Format them just like evaluation does before DTW

    evaluate_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);

    return 0;
}

} //end of anonymous namespace

int main(int argc, char** argv) {
    if (argc < 2) {
        print_usage();
        return -1;
    }

    auto conf = parse_args(argc, argv);

    if (conf.half) {
        conf.downscale = 2;
    } else if (conf.third) {
        conf.downscale = 3;
    } else if (conf.quarter) {
        conf.downscale = 4;
    }

    std::cout << "  Method: " << method_to_string(conf.method) << std::endl;

    if(conf.hmm && conf.htk){
        std::cout << "    Eval: HMM (HTK)" << std::endl;
    } else if(conf.hmm){
        std::cout << "    Eval: HMM (mlpack)" << std::endl;
    } else {
        std::cout << "    Eval: DTW" << std::endl;
    }

    if (conf.command == "evaluate") {
        conf.load = true;
        return command_train(conf);
    } else if (conf.command == "train") {
        return command_train(conf);
    } else if (conf.command == "features") {
        return command_features(conf);
    } else if (conf.command == "evaluate_features") {
        return command_evaluate_features(conf);
    }

    print_usage();

    return -1;
}
<commit_msg>Nicer display<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2015-2016.
// Distributed under the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#include "config.hpp"
#include "dataset.hpp" //Dataset handling

//Include methods
#include "standard.hpp" //Method 0
#include "holistic.hpp" //Method 1
#include "patches.hpp"  //Method 2

//Method-Agnostic commands
#include "evaluate_features.hpp" //evaluate_patches command

#include <atomic>
#include <mutex>
#include "dll/util/timers.hpp"

namespace {

using string_vector = std::vector<std::string>;

template <typename Set>
void extract_names(spot_dataset& dataset, Set& set, string_vector& train_image_names, string_vector& train_word_names, string_vector& test_image_names, string_vector& valid_image_names) {
    std::cout << set.train_set.size() << " training line images in set" << std::endl;
    std::cout << set.validation_set.size() << " validation line images in set" << std::endl;
    std::cout << set.test_set.size() << " test line images in set" << std::endl;

    for (auto& word_image : dataset.word_images) {
        auto& name = word_image.first;
        for (auto& train_image : set.train_set) {
            if (name.find(train_image) == 0) {
                train_image_names.push_back(name);
                train_word_names.emplace_back(name.begin(), name.end() - 4);
                break;
            }
        }
        for (auto& test_image : set.test_set) {
            if (name.find(test_image) == 0) {
                test_image_names.push_back(name);
                break;
            }
        }
        for (auto& valid_image : set.validation_set) {
            if (name.find(valid_image) == 0) {
                valid_image_names.push_back(name);
                break;
            }
        }
    }

    std::cout << train_image_names.size() << " training word images in set" << std::endl;
    std::cout << valid_image_names.size() << " validation word images in set" << std::endl;
    std::cout << test_image_names.size() << " test word images in set" << std::endl;
}

spot_dataset read_dataset(config& conf) {
    decltype(auto) dataset_path = conf.files[0];
    decltype(auto) cv_set = conf.files[1];

    std::cout << " Dataset: " << dataset_path << std::endl;
    std::cout << "     Set: " << cv_set << std::endl;

    spot_dataset dataset;

    if(conf.washington){
        dataset = read_washington(dataset_path);
    } else if(conf.parzival){
        dataset = read_parzival(dataset_path);
    } else if(conf.iam){
        dataset = read_iam(dataset_path);
    } else {
        std::cerr << "Invalid configuration of the dataset" << std::endl;
    }

    std::cout << dataset.line_images.size() << " line images loaded from the dataset" << std::endl;
    std::cout << dataset.word_images.size() << " word images loaded from the dataset" << std::endl;

    if (conf.washington) {
        conf.cv_full_path = dataset_path + "/sets/" + cv_set + "/";
    } else if (conf.parzival) {
        conf.cv_full_path = dataset_path + "/sets1/";
    } else if (conf.iam) {
        conf.cv_full_path = dataset_path + "/sets/";
    }

    conf.data_full_path = dataset_path + "/data/word_images_normalized/";

    return dataset;
}

int command_train(config& conf) {
    if (conf.files.size() < 2) {
        std::cout << "Train needs the path to the dataset and the cv set to use" << std::endl;
        return -1;
    }

    auto dataset = read_dataset(conf);

    decltype(auto) cv_set = conf.files[1];

    if (!dataset.sets.count(cv_set)) {
        std::cout << "The subset \"" << cv_set << "\" does not exist" << std::endl;
        return -1;
    }

    auto& set = dataset.sets[cv_set];

    string_vector train_image_names, train_word_names, test_image_names, valid_image_names;

    extract_names(dataset, set, train_image_names, train_word_names, test_image_names, valid_image_names);

    switch (conf.method) {
        case Method::Marti2001:
        case Method::Rath2007:
        case Method::Rath2003:
        case Method::Rodriguez2008:
        case Method::Vinciarelli2004:
        case Method::Terasawa2009:
            standard_train(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Holistic:
            holistic_train(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Patches:
            patches_train(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
    }

    dll::dump_timers();

    return 0;
}

int command_features(config& conf) {
    if (conf.files.size() < 2) {
        std::cout << "features needs the path to the dataset and the cv set to use" << std::endl;
        return -1;
    }

    auto dataset = read_dataset(conf);

    decltype(auto) cv_set = conf.files[1];

    if (!dataset.sets.count(cv_set)) {
        std::cout << "The subset \"" << cv_set << "\" does not exist" << std::endl;
        return -1;
    }

    auto& set = dataset.sets[cv_set];

    string_vector train_image_names, train_word_names, test_image_names, valid_image_names;

    extract_names(dataset, set, train_image_names, train_word_names, test_image_names, valid_image_names);

    switch (conf.method) {
        case Method::Marti2001:
        case Method::Rath2007:
        case Method::Rath2003:
        case Method::Rodriguez2008:
        case Method::Vinciarelli2004:
        case Method::Terasawa2009:
            standard_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Holistic:
            holistic_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
        case Method::Patches:
            patches_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);
            break;
    }

    return 0;
}

int command_evaluate_features(config& conf) {
    if (conf.files.size() < 2) {
        std::cout << "evaluate_features needs the path to the dataset and the cv set to use" << std::endl;
        return -1;
    }

    auto dataset = read_dataset(conf);

    decltype(auto) cv_set = conf.files[1];

    if (!dataset.sets.count(cv_set)) {
        std::cout << "The subset \"" << cv_set << "\" does not exist" << std::endl;
        return -1;
    }

    auto& set = dataset.sets[cv_set];

    string_vector train_image_names, train_word_names, test_image_names, valid_image_names;

    extract_names(dataset, set, train_image_names, train_word_names, test_image_names, valid_image_names);

    //TODO At this point, we need to pass the features to DTW but we ned to match them to images first for evaluation
    //     Format them just like evaluation does before DTW

    evaluate_features(dataset, set, conf, train_word_names, train_image_names, valid_image_names, test_image_names);

    return 0;
}

} //end of anonymous namespace

int main(int argc, char** argv) {
    if (argc < 2) {
        print_usage();
        return -1;
    }

    auto conf = parse_args(argc, argv);

    if (conf.half) {
        conf.downscale = 2;
    } else if (conf.third) {
        conf.downscale = 3;
    } else if (conf.quarter) {
        conf.downscale = 4;
    }

    std::cout << std::string(11, '#') << " Keyword spotting " << std::string(11, '#') << std::endl;
    std::cout << "  Method: " << method_to_string(conf.method) << std::endl;

    if(conf.hmm && conf.htk){
        std::cout << "    Eval: HMM (HTK)" << std::endl;
    } else if(conf.hmm){
        std::cout << "    Eval: HMM (mlpack)" << std::endl;
    } else {
        std::cout << "    Eval: DTW" << std::endl;
    }

    bool ok = true;
    int ret = 0;

    if (conf.command == "evaluate") {
        conf.load = true;
        ret = command_train(conf);
    } else if (conf.command == "train") {
        ret = command_train(conf);
    } else if (conf.command == "features") {
        ret = command_features(conf);
    } else if (conf.command == "evaluate_features") {
        ret = command_evaluate_features(conf);
    } else {
        ok = false;
    }

    std::cout << std::string(40, '#') << std::endl;

    if(!ok){
        print_usage();

        return -1;
    }

    return ret;
}
<|endoftext|>