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

#include "hphp/runtime/base/base-includes.h"
#include "hphp/runtime/vm/native-data.h"

#include "bson.h"
#include "utils.h"

extern "C" {
#include "libbson/src/bson/bson.h"
#include "libmongoc/src/mongoc/mongoc.h"
#include <stdio.h>
}

#define IMPLEMENT_GET_CLASS(cls) \
	Class* cls::getClass() { \
		if (s_class == nullptr) { \
			s_class = Unit::lookupClass(s_className.get()); \
			assert(s_class); \
		} \
		return s_class; \
	}

namespace HPHP {

const StaticString s_MongoDriverWriteResult_className("MongoDB\\Driver\\WriteResult");

/* {{{ MongoDB\Driver\QueryResult */
const StaticString s_MongoDriverQueryResult_className("MongoDB\\Driver\\QueryResult");

class MongoDBDriverQueryResultData
{
	public:
		static Class* s_class;
		static const StaticString s_className;

		static Class* getClass();

		mongoc_cursor_t *cursor;
		int              hint;
		bool             is_command_cursor;
		bson_t          *first_batch;

		void sweep() {
		}

		~MongoDBDriverQueryResultData() {
			sweep();
		};
};

Class* MongoDBDriverQueryResultData::s_class = nullptr;
const StaticString MongoDBDriverQueryResultData::s_className("MongoDBDriverQueryResult");
IMPLEMENT_GET_CLASS(MongoDBDriverQueryResultData);
/* }}} */

/* {{{ MongoDB\Driver\Query */
const StaticString s_MongoDriverQuery_className("MongoDB\\Driver\\Query");
/* }}} */

/* {{{ MongoDB\Manager */
class MongoDBManagerData
{
	public:
		static Class* s_class;
		static const StaticString s_className;

		mongoc_client_t *m_client;

		static Class* getClass();

		void sweep() {
			mongoc_client_destroy(m_client);
		}

		~MongoDBManagerData() {
			sweep();
		};
};

Class* MongoDBManagerData::s_class = nullptr;
const StaticString MongoDBManagerData::s_className("MongoDBManager");
IMPLEMENT_GET_CLASS(MongoDBManagerData);

static void HHVM_METHOD(MongoDBManager, __construct, const String &dsn, const Array &options, const Array &driverOptions)
{
	MongoDBManagerData* data = Native::data<MongoDBManagerData>(this_);
	mongoc_client_t *client;

	client = mongoc_client_new(dsn.c_str());

	if (!client) {
		throw Object(SystemLib::AllocExceptionObject("Can't connect"));
	}

	data->m_client = client;
}

static Object HHVM_METHOD(MongoDBManager, executeInsert, const String &ns, const Variant &document, const Object &writeConcern)
{
	static Class* c_foobar;
	bson_t *bson;
	MongoDBManagerData* data = Native::data<MongoDBManagerData>(this_);
	bson_error_t error;
	bson_t reply;
	mongoc_bulk_operation_t *batch;
	char *database;
	char *collection;
	int hint;

	VariantToBsonConverter converter(document);
	bson = bson_new();
	converter.convert(bson);

	/* Prepare */
	MongoDriver::Utils::splitNamespace(ns, &database, &collection);

	batch = mongoc_bulk_operation_new(true);
	mongoc_bulk_operation_insert(batch, bson);
	mongoc_bulk_operation_set_database(batch, database);
	mongoc_bulk_operation_set_collection(batch, collection);
	mongoc_bulk_operation_set_client(batch, data->m_client);

	/* Run operation */
	hint = mongoc_bulk_operation_execute(batch, &reply, &error);

	/* Destroy */
	bson_destroy(bson);
	mongoc_bulk_operation_destroy(batch);

	/* Prepare result */

	c_foobar = Unit::lookupClass(s_MongoDriverWriteResult_className.get());
	assert(c_foobar);
	ObjectData* obj = ObjectData::newInstance(c_foobar);

	obj->o_set(String("nInserted"), Variant(52), s_MongoDriverWriteResult_className.get());
	obj->o_set(String("nModified"), Variant(77), s_MongoDriverWriteResult_className.get());

	return Object(obj);
}

static Object HHVM_METHOD(MongoDBManager, executeQuery, const String &ns, Object &query, Object &readPreference)
{
	static Class* c_foobar;
	bson_t *bson_query = NULL, *bson_fields = NULL;
	const bson_t *doc;
	MongoDBManagerData* manager_data = Native::data<MongoDBManagerData>(this_);
	char *dbname;
	char *collname;
	mongoc_collection_t *collection;
	mongoc_cursor_t *cursor;

	uint32_t skip, limit, batch_size;
	mongoc_query_flags_t flags;

	/* Prepare */
	if (!MongoDriver::Utils::splitNamespace(ns, &dbname, &collname)) {
		throw Object(SystemLib::AllocInvalidArgumentExceptionObject("Invalid namespace"));
		return NULL;
	}

	/* Get query properties */
	auto zquery = query->o_get(String("query"), false, s_MongoDriverQuery_className);

	if (zquery.getType() == KindOfArray) {
		const Array& aquery = zquery.toArray();

		skip = aquery[String("skip")].toInt32();
		limit = aquery[String("limit")].toInt32();
		batch_size = aquery[String("batch_size")].toInt32();
		flags = (mongoc_query_flags_t) aquery[String("flags")].toInt32();

		VariantToBsonConverter converter(aquery[String("query")]);
		bson_query = bson_new();
		converter.convert(bson_query);

		if (aquery.exists(String("fields"))) {
			VariantToBsonConverter converter(aquery[String("fields")]);
			bson_fields = bson_new();
			converter.convert(bson_fields);
		}
	}

	/* Run query and get cursor */
	collection = mongoc_client_get_collection(manager_data->m_client, dbname, collname);
	cursor = mongoc_collection_find(collection, flags, skip, limit, batch_size, bson_query, bson_fields, NULL /*read_preference*/);
	mongoc_collection_destroy(collection);

	/* Check for errors */
	if (!mongoc_cursor_next(cursor, &doc)) {
		bson_error_t error;

		/* Could simply be no docs, which is not an error */
		if (mongoc_cursor_error(cursor, &error)) {
//			MongoDriver::Utils::throwExceptionFromBsonError(&error);
			mongoc_cursor_destroy(cursor);
			return NULL;
		}
	}

	/* Prepare result */
	c_foobar = Unit::lookupClass(s_MongoDriverQueryResult_className.get());
	assert(c_foobar);
	ObjectData* obj = ObjectData::newInstance(c_foobar);

	MongoDBDriverQueryResultData* result_data = Native::data<MongoDBDriverQueryResultData>(obj);

	result_data->cursor = cursor;
	result_data->hint = mongoc_cursor_get_hint(cursor);
	result_data->is_command_cursor = false;
	result_data->first_batch = doc ? bson_copy(doc) : NULL;

	return Object(obj);
}
/* }}} */

/* {{{ MongoDB\Driver\ReadPreference */
const StaticString s_MongoDriverReadPreference_className("MongoDB\\Driver\\ReadPreference");

class MongoDBDriverReadPreferenceData
{
	public:
		static Class* s_class;
		static const StaticString s_className;

		mongoc_read_prefs_t *m_read_preference = NULL;

		static Class* getClass();

		void sweep() {
			mongoc_read_prefs_destroy(m_read_preference);
		}

		~MongoDBDriverReadPreferenceData() {
			sweep();
		};
};

Class* MongoDBDriverReadPreferenceData::s_class = nullptr;
const StaticString MongoDBDriverReadPreferenceData::s_className("MongoDBDriverReadPreference");
IMPLEMENT_GET_CLASS(MongoDBDriverReadPreferenceData);

static void HHVM_METHOD(MongoDBDriverReadPreference, _setReadPreference, int readPreference)
{
	MongoDBDriverReadPreferenceData* data = Native::data<MongoDBDriverReadPreferenceData>(this_);

	data->m_read_preference = mongoc_read_prefs_new((mongoc_read_mode_t) readPreference);
}

static void HHVM_METHOD(MongoDBDriverReadPreference, _setReadPreferenceTags, const Variant &tagSets)
{
	MongoDBDriverReadPreferenceData* data = Native::data<MongoDBDriverReadPreferenceData>(this_);
	bson_t *bson;

	/* Convert argument */
	VariantToBsonConverter converter(tagSets);
	bson = bson_new();
	converter.convert(bson);

	/* Set and check errors */
	mongoc_read_prefs_set_tags(data->m_read_preference, bson);
	bson_destroy(bson);
	if (!mongoc_read_prefs_is_valid(data->m_read_preference)) {
		/* Throw exception */
		throw Object(SystemLib::AllocInvalidArgumentExceptionObject("Invalid tagSet"));
	}
}
/* }}} */

static class MongoDBExtension : public Extension {
	public:
		MongoDBExtension() : Extension("mongodb") {}

		virtual void moduleInit() {
			/* MongoDB\Manager */
			HHVM_MALIAS(MongoDB\\Manager, __construct, MongoDBManager, __construct);
			HHVM_MALIAS(MongoDB\\Manager, executeInsert, MongoDBManager, executeInsert);
			HHVM_MALIAS(MongoDB\\Manager, executeQuery, MongoDBManager, executeQuery);

			Native::registerNativeDataInfo<MongoDBManagerData>(MongoDBManagerData::s_className.get());

			/* MongoDb\Driver\Query */
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_NONE"), (int64_t) MONGOC_QUERY_NONE);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_TAILABLE_CURSOR"), (int64_t) MONGOC_QUERY_TAILABLE_CURSOR);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_SLAVE_OK"), (int64_t) MONGOC_QUERY_SLAVE_OK);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_OPLOG_REPLAY"), (int64_t) MONGOC_QUERY_OPLOG_REPLAY);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_NO_CURSOR_TIMEOUT"), (int64_t) MONGOC_QUERY_NO_CURSOR_TIMEOUT);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_AWAIT_DATA"), (int64_t) MONGOC_QUERY_AWAIT_DATA);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_EXHAUST"), (int64_t) MONGOC_QUERY_EXHAUST);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_PARTIAL"), (int64_t) MONGOC_QUERY_PARTIAL);

			/* MongoDb\Driver\ReadPreference */
			HHVM_MALIAS(MongoDB\\Driver\\ReadPreference, _setReadPreference, MongoDBDriverReadPreference, _setReadPreference);
			HHVM_MALIAS(MongoDB\\Driver\\ReadPreference, _setReadPreferenceTags, MongoDBDriverReadPreference, _setReadPreferenceTags);

			Native::registerNativeDataInfo<MongoDBDriverReadPreferenceData>(MongoDBDriverReadPreferenceData::s_className.get());

			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_PRIMARY"), (int64_t) MONGOC_READ_PRIMARY);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_PRIMARY_PREFERRED"), (int64_t) MONGOC_READ_PRIMARY_PREFERRED);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_SECONDARY"), (int64_t) MONGOC_READ_SECONDARY);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_SECONDARY_PREFERRED"), (int64_t) MONGOC_READ_SECONDARY_PREFERRED);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_NEAREST"), (int64_t) MONGOC_READ_NEAREST);

			/* MongoDb\Driver\QueryResult */
			Native::registerNativeDataInfo<MongoDBDriverQueryResultData>(MongoDBDriverQueryResultData::s_className.get());

			loadSystemlib("mongodb");
			mongoc_init();
		}
} s_mongodb_extension;

HHVM_GET_MODULE(mongodb)

} // namespace HPHP
<commit_msg>Make sure we thrown an exception for failures when splitting a namespace<commit_after>/**
 *  Copyright 2014-2015 MongoDB, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "hphp/runtime/base/base-includes.h"
#include "hphp/runtime/vm/native-data.h"

#include "bson.h"
#include "utils.h"

extern "C" {
#include "libbson/src/bson/bson.h"
#include "libmongoc/src/mongoc/mongoc.h"
#include <stdio.h>
}

#define IMPLEMENT_GET_CLASS(cls) \
	Class* cls::getClass() { \
		if (s_class == nullptr) { \
			s_class = Unit::lookupClass(s_className.get()); \
			assert(s_class); \
		} \
		return s_class; \
	}

namespace HPHP {

const StaticString s_MongoDriverWriteResult_className("MongoDB\\Driver\\WriteResult");

/* {{{ MongoDB\Driver\QueryResult */
const StaticString s_MongoDriverQueryResult_className("MongoDB\\Driver\\QueryResult");

class MongoDBDriverQueryResultData
{
	public:
		static Class* s_class;
		static const StaticString s_className;

		static Class* getClass();

		mongoc_cursor_t *cursor;
		int              hint;
		bool             is_command_cursor;
		bson_t          *first_batch;

		void sweep() {
		}

		~MongoDBDriverQueryResultData() {
			sweep();
		};
};

Class* MongoDBDriverQueryResultData::s_class = nullptr;
const StaticString MongoDBDriverQueryResultData::s_className("MongoDBDriverQueryResult");
IMPLEMENT_GET_CLASS(MongoDBDriverQueryResultData);
/* }}} */

/* {{{ MongoDB\Driver\Query */
const StaticString s_MongoDriverQuery_className("MongoDB\\Driver\\Query");
/* }}} */

/* {{{ MongoDB\Manager */
class MongoDBManagerData
{
	public:
		static Class* s_class;
		static const StaticString s_className;

		mongoc_client_t *m_client;

		static Class* getClass();

		void sweep() {
			mongoc_client_destroy(m_client);
		}

		~MongoDBManagerData() {
			sweep();
		};
};

Class* MongoDBManagerData::s_class = nullptr;
const StaticString MongoDBManagerData::s_className("MongoDBManager");
IMPLEMENT_GET_CLASS(MongoDBManagerData);

static void HHVM_METHOD(MongoDBManager, __construct, const String &dsn, const Array &options, const Array &driverOptions)
{
	MongoDBManagerData* data = Native::data<MongoDBManagerData>(this_);
	mongoc_client_t *client;

	client = mongoc_client_new(dsn.c_str());

	if (!client) {
		throw Object(SystemLib::AllocExceptionObject("Can't connect"));
	}

	data->m_client = client;
}

static Object HHVM_METHOD(MongoDBManager, executeInsert, const String &ns, const Variant &document, const Object &writeConcern)
{
	static Class* c_foobar;
	bson_t *bson;
	MongoDBManagerData* data = Native::data<MongoDBManagerData>(this_);
	bson_error_t error;
	bson_t reply;
	mongoc_bulk_operation_t *batch;
	char *database;
	char *collection;
	int hint;

	VariantToBsonConverter converter(document);
	bson = bson_new();
	converter.convert(bson);

	/* Prepare */
	if (!MongoDriver::Utils::splitNamespace(ns, &database, &collection)) {
		throw Object(SystemLib::AllocInvalidArgumentExceptionObject("Invalid namespace"));
		return NULL;
	}

	batch = mongoc_bulk_operation_new(true);
	mongoc_bulk_operation_insert(batch, bson);
	mongoc_bulk_operation_set_database(batch, database);
	mongoc_bulk_operation_set_collection(batch, collection);
	mongoc_bulk_operation_set_client(batch, data->m_client);

	/* Run operation */
	hint = mongoc_bulk_operation_execute(batch, &reply, &error);

	/* Destroy */
	bson_destroy(bson);
	mongoc_bulk_operation_destroy(batch);

	/* Prepare result */

	c_foobar = Unit::lookupClass(s_MongoDriverWriteResult_className.get());
	assert(c_foobar);
	ObjectData* obj = ObjectData::newInstance(c_foobar);

	obj->o_set(String("nInserted"), Variant(52), s_MongoDriverWriteResult_className.get());
	obj->o_set(String("nModified"), Variant(77), s_MongoDriverWriteResult_className.get());

	return Object(obj);
}

static Object HHVM_METHOD(MongoDBManager, executeQuery, const String &ns, Object &query, Object &readPreference)
{
	static Class* c_foobar;
	bson_t *bson_query = NULL, *bson_fields = NULL;
	const bson_t *doc;
	MongoDBManagerData* manager_data = Native::data<MongoDBManagerData>(this_);
	char *dbname;
	char *collname;
	mongoc_collection_t *collection;
	mongoc_cursor_t *cursor;

	uint32_t skip, limit, batch_size;
	mongoc_query_flags_t flags;

	/* Prepare */
	if (!MongoDriver::Utils::splitNamespace(ns, &dbname, &collname)) {
		throw Object(SystemLib::AllocInvalidArgumentExceptionObject("Invalid namespace"));
		return NULL;
	}

	/* Get query properties */
	auto zquery = query->o_get(String("query"), false, s_MongoDriverQuery_className);

	if (zquery.getType() == KindOfArray) {
		const Array& aquery = zquery.toArray();

		skip = aquery[String("skip")].toInt32();
		limit = aquery[String("limit")].toInt32();
		batch_size = aquery[String("batch_size")].toInt32();
		flags = (mongoc_query_flags_t) aquery[String("flags")].toInt32();

		VariantToBsonConverter converter(aquery[String("query")]);
		bson_query = bson_new();
		converter.convert(bson_query);

		if (aquery.exists(String("fields"))) {
			VariantToBsonConverter converter(aquery[String("fields")]);
			bson_fields = bson_new();
			converter.convert(bson_fields);
		}
	}

	/* Run query and get cursor */
	collection = mongoc_client_get_collection(manager_data->m_client, dbname, collname);
	cursor = mongoc_collection_find(collection, flags, skip, limit, batch_size, bson_query, bson_fields, NULL /*read_preference*/);
	mongoc_collection_destroy(collection);

	/* Check for errors */
	if (!mongoc_cursor_next(cursor, &doc)) {
		bson_error_t error;

		/* Could simply be no docs, which is not an error */
		if (mongoc_cursor_error(cursor, &error)) {
//			MongoDriver::Utils::throwExceptionFromBsonError(&error);
			mongoc_cursor_destroy(cursor);
			return NULL;
		}
	}

	/* Prepare result */
	c_foobar = Unit::lookupClass(s_MongoDriverQueryResult_className.get());
	assert(c_foobar);
	ObjectData* obj = ObjectData::newInstance(c_foobar);

	MongoDBDriverQueryResultData* result_data = Native::data<MongoDBDriverQueryResultData>(obj);

	result_data->cursor = cursor;
	result_data->hint = mongoc_cursor_get_hint(cursor);
	result_data->is_command_cursor = false;
	result_data->first_batch = doc ? bson_copy(doc) : NULL;

	return Object(obj);
}
/* }}} */

/* {{{ MongoDB\Driver\ReadPreference */
const StaticString s_MongoDriverReadPreference_className("MongoDB\\Driver\\ReadPreference");

class MongoDBDriverReadPreferenceData
{
	public:
		static Class* s_class;
		static const StaticString s_className;

		mongoc_read_prefs_t *m_read_preference = NULL;

		static Class* getClass();

		void sweep() {
			mongoc_read_prefs_destroy(m_read_preference);
		}

		~MongoDBDriverReadPreferenceData() {
			sweep();
		};
};

Class* MongoDBDriverReadPreferenceData::s_class = nullptr;
const StaticString MongoDBDriverReadPreferenceData::s_className("MongoDBDriverReadPreference");
IMPLEMENT_GET_CLASS(MongoDBDriverReadPreferenceData);

static void HHVM_METHOD(MongoDBDriverReadPreference, _setReadPreference, int readPreference)
{
	MongoDBDriverReadPreferenceData* data = Native::data<MongoDBDriverReadPreferenceData>(this_);

	data->m_read_preference = mongoc_read_prefs_new((mongoc_read_mode_t) readPreference);
}

static void HHVM_METHOD(MongoDBDriverReadPreference, _setReadPreferenceTags, const Variant &tagSets)
{
	MongoDBDriverReadPreferenceData* data = Native::data<MongoDBDriverReadPreferenceData>(this_);
	bson_t *bson;

	/* Convert argument */
	VariantToBsonConverter converter(tagSets);
	bson = bson_new();
	converter.convert(bson);

	/* Set and check errors */
	mongoc_read_prefs_set_tags(data->m_read_preference, bson);
	bson_destroy(bson);
	if (!mongoc_read_prefs_is_valid(data->m_read_preference)) {
		/* Throw exception */
		throw Object(SystemLib::AllocInvalidArgumentExceptionObject("Invalid tagSet"));
	}
}
/* }}} */

static class MongoDBExtension : public Extension {
	public:
		MongoDBExtension() : Extension("mongodb") {}

		virtual void moduleInit() {
			/* MongoDB\Manager */
			HHVM_MALIAS(MongoDB\\Manager, __construct, MongoDBManager, __construct);
			HHVM_MALIAS(MongoDB\\Manager, executeInsert, MongoDBManager, executeInsert);
			HHVM_MALIAS(MongoDB\\Manager, executeQuery, MongoDBManager, executeQuery);

			Native::registerNativeDataInfo<MongoDBManagerData>(MongoDBManagerData::s_className.get());

			/* MongoDb\Driver\Query */
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_NONE"), (int64_t) MONGOC_QUERY_NONE);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_TAILABLE_CURSOR"), (int64_t) MONGOC_QUERY_TAILABLE_CURSOR);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_SLAVE_OK"), (int64_t) MONGOC_QUERY_SLAVE_OK);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_OPLOG_REPLAY"), (int64_t) MONGOC_QUERY_OPLOG_REPLAY);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_NO_CURSOR_TIMEOUT"), (int64_t) MONGOC_QUERY_NO_CURSOR_TIMEOUT);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_AWAIT_DATA"), (int64_t) MONGOC_QUERY_AWAIT_DATA);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_EXHAUST"), (int64_t) MONGOC_QUERY_EXHAUST);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverQuery_className.get(), makeStaticString("FLAG_PARTIAL"), (int64_t) MONGOC_QUERY_PARTIAL);

			/* MongoDb\Driver\ReadPreference */
			HHVM_MALIAS(MongoDB\\Driver\\ReadPreference, _setReadPreference, MongoDBDriverReadPreference, _setReadPreference);
			HHVM_MALIAS(MongoDB\\Driver\\ReadPreference, _setReadPreferenceTags, MongoDBDriverReadPreference, _setReadPreferenceTags);

			Native::registerNativeDataInfo<MongoDBDriverReadPreferenceData>(MongoDBDriverReadPreferenceData::s_className.get());

			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_PRIMARY"), (int64_t) MONGOC_READ_PRIMARY);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_PRIMARY_PREFERRED"), (int64_t) MONGOC_READ_PRIMARY_PREFERRED);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_SECONDARY"), (int64_t) MONGOC_READ_SECONDARY);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_SECONDARY_PREFERRED"), (int64_t) MONGOC_READ_SECONDARY_PREFERRED);
			Native::registerClassConstant<KindOfInt64>(s_MongoDriverReadPreference_className.get(), makeStaticString("RP_NEAREST"), (int64_t) MONGOC_READ_NEAREST);

			/* MongoDb\Driver\QueryResult */
			Native::registerNativeDataInfo<MongoDBDriverQueryResultData>(MongoDBDriverQueryResultData::s_className.get());

			loadSystemlib("mongodb");
			mongoc_init();
		}
} s_mongodb_extension;

HHVM_GET_MODULE(mongodb)

} // namespace HPHP
<|endoftext|>


<commit_before>//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file implements the APINotesManager class.
//
//===----------------------------------------------------------------------===//

#include "clang/APINotes/APINotesManager.h"
#include "clang/APINotes/APINotesOptions.h"
#include "clang/APINotes/APINotesReader.h"
#include "clang/APINotes/APINotesYAMLCompiler.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceMgrAdapter.h"
#include "clang/Basic/Version.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include <sys/stat.h>

using namespace clang;
using namespace api_notes;

#define DEBUG_TYPE "API Notes"
STATISTIC(NumHeaderAPINotes,
          "non-framework API notes files loaded");
STATISTIC(NumPublicFrameworkAPINotes,
          "framework public API notes loaded");
STATISTIC(NumPrivateFrameworkAPINotes,
          "framework private API notes loaded");
STATISTIC(NumFrameworksSearched,
          "frameworks searched");
STATISTIC(NumDirectoriesSearched,
          "header directories searched");
STATISTIC(NumDirectoryCacheHits,
          "directory cache hits");

namespace {
  /// Prints two successive strings, which much be kept alive as long as the
  /// PrettyStackTrace entry.
  class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
    StringRef First, Second;
  public:
    PrettyStackTraceDoubleString(StringRef first, StringRef second)
        : First(first), Second(second) {}
    void print(raw_ostream &OS) const override {
      OS << First << Second;
    }
  };
}

APINotesManager::APINotesManager(SourceManager &sourceMgr,
                                 const LangOptions &langOpts)
  : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes) { }

APINotesManager::~APINotesManager() {
  // Free the API notes readers.
  for (const auto &entry : Readers) {
    if (auto reader = entry.second.dyn_cast<APINotesReader *>()) {
      delete reader;
    }
  }

  delete CurrentModuleReaders[0];
  delete CurrentModuleReaders[1];
}

std::unique_ptr<APINotesReader>
APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) {
  PrettyStackTraceDoubleString trace("Loading API notes from ",
                                     apiNotesFile->getName());

  // Open the source file.
  auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
  auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation());
  if (!sourceBuffer) return nullptr;

  // Compile the API notes source into a buffer.
  // FIXME: Either propagate OSType through or, better yet, improve the binary
  // APINotes format to maintain complete availability information.
  // FIXME: We don't even really need to go through the binary format at all;
  // we're just going to immediately deserialize it again.
  llvm::SmallVector<char, 1024> apiNotesBuffer;
  std::unique_ptr<llvm::MemoryBuffer> compiledBuffer;
  {
    SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(),
                                   diag::err_apinotes_message,
                                   diag::warn_apinotes_message,
                                   diag::note_apinotes_message,
                                   apiNotesFile);
    llvm::raw_svector_ostream OS(apiNotesBuffer);
    if (api_notes::compileAPINotes(sourceBuffer->getBuffer(),
                                   SourceMgr.getFileEntryForID(sourceFileID),
                                   OS,
                                   srcMgrAdapter.getDiagHandler(),
                                   srcMgrAdapter.getDiagContext()))
      return nullptr;

    // Make a copy of the compiled form into the buffer.
    compiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
               StringRef(apiNotesBuffer.data(), apiNotesBuffer.size()));
  }

  // Load the binary form we just compiled.
  auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion);
  assert(reader && "Could not load the API notes we just generated?");
  return reader;
}

bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,
                                   const FileEntry *APINotesFile) {
  assert(Readers.find(HeaderDir) == Readers.end());
  if (auto reader = loadAPINotes(APINotesFile)) {
    Readers[HeaderDir] = reader.release();
    return false;
  }

  Readers[HeaderDir] = nullptr;
  return true;
}

const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directory,
                                                   StringRef basename,
                                                   bool wantPublic) {
  FileManager &fileMgr = SourceMgr.getFileManager();

  llvm::SmallString<128> path;
  path += directory->getName();

  StringRef basenameSuffix = "";
  if (!wantPublic) basenameSuffix = "_private";

  // Look for the source API notes file.
  llvm::sys::path::append(path,
    llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION);
  return fileMgr.getFile(path, /*Open*/true);
}

const DirectoryEntry *APINotesManager::loadFrameworkAPINotes(
                        llvm::StringRef FrameworkPath,
                        llvm::StringRef FrameworkName,
                        bool Public) {
  FileManager &FileMgr = SourceMgr.getFileManager();

  llvm::SmallString<128> Path;
  Path += FrameworkPath;
  unsigned FrameworkNameLength = Path.size();

  // Form the path to the APINotes file.
  llvm::sys::path::append(Path, "APINotes");
  if (Public)
    llvm::sys::path::append(Path,
                            (llvm::Twine(FrameworkName) + "."
                              + SOURCE_APINOTES_EXTENSION));
  else
    llvm::sys::path::append(Path,
                            (llvm::Twine(FrameworkName) + "_private."
                              + SOURCE_APINOTES_EXTENSION));

  // Try to open the APINotes file.
  const FileEntry *APINotesFile = FileMgr.getFile(Path);
  if (!APINotesFile)
    return nullptr;

  // Form the path to the corresponding header directory.
  Path.resize(FrameworkNameLength);
  if (Public)
    llvm::sys::path::append(Path, "Headers");
  else
    llvm::sys::path::append(Path, "PrivateHeaders");

  // Try to access the header directory.
  const DirectoryEntry *HeaderDir = FileMgr.getDirectory(Path);
  if (!HeaderDir)
    return nullptr;

  // Try to load the API notes.
  if (loadAPINotes(HeaderDir, APINotesFile))
    return nullptr;

  // Success: return the header directory.
  if (Public)
    ++NumPublicFrameworkAPINotes;
  else
    ++NumPrivateFrameworkAPINotes;
  return HeaderDir;
}

static void checkPrivateAPINotesName(DiagnosticsEngine &diags,
                                     const FileEntry *file,
                                     const Module *module) {
  if (file->tryGetRealPathName().empty())
    return;

  StringRef realFilename =
      llvm::sys::path::filename(file->tryGetRealPathName());
  StringRef realStem = llvm::sys::path::stem(realFilename);
  if (realStem.endswith("_private"))
    return;

  unsigned diagID = diag::warn_apinotes_private_case;
  if (module->IsSystem)
    diagID = diag::warn_apinotes_private_case_system;

  diags.Report(SourceLocation(), diagID) << module->Name << realFilename;
}

/// \returns true if any of \p module's immediate submodules are defined in a
/// private module map
static bool hasPrivateSubmodules(const Module *module) {
  return llvm::any_of(module->submodules(), [](const Module *submodule) {
    return submodule->ModuleMapIsPrivate;
  });
}

bool APINotesManager::loadCurrentModuleAPINotes(
                   const Module *module,
                   bool lookInModule,
                   ArrayRef<std::string> searchPaths) {
  assert(!CurrentModuleReaders[0] &&
         "Already loaded API notes for the current module?");

  FileManager &fileMgr = SourceMgr.getFileManager();
  auto moduleName = module->getTopLevelModuleName();

  // First, look relative to the module itself.
  if (lookInModule) {
    bool foundAny = false;
    unsigned numReaders = 0;

    // Local function to try loading an API notes file in the given directory.
    auto tryAPINotes = [&](const DirectoryEntry *dir, bool wantPublic) {
      if (auto file = findAPINotesFile(dir, moduleName, wantPublic)) {
        foundAny = true;

        if (!wantPublic)
          checkPrivateAPINotesName(SourceMgr.getDiagnostics(), file, module);

        // Try to load the API notes file.
        CurrentModuleReaders[numReaders] = loadAPINotes(file).release();
        if (CurrentModuleReaders[numReaders])
          ++numReaders;
      }
    };

    if (module->IsFramework) {
      // For frameworks, we search in the "Headers" or "PrivateHeaders"
      // subdirectory.
      //
      // Public modules:
      // - Headers/Foo.apinotes
      // - PrivateHeaders/Foo_private.apinotes (if there are private submodules)
      // Private modules:
      // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has
      //   the word "Private" in it in practice)
      llvm::SmallString<128> path;
      path += module->Directory->getName();

      if (!module->ModuleMapIsPrivate) {
        unsigned pathLen = path.size();

        llvm::sys::path::append(path, "Headers");
        if (auto apinotesDir = fileMgr.getDirectory(path))
          tryAPINotes(apinotesDir, /*wantPublic=*/true);

        path.resize(pathLen);
      }

      if (module->ModuleMapIsPrivate || hasPrivateSubmodules(module)) {
        llvm::sys::path::append(path, "PrivateHeaders");
        if (auto privateAPINotesDir = fileMgr.getDirectory(path)) {
          tryAPINotes(privateAPINotesDir,
                      /*wantPublic=*/module->ModuleMapIsPrivate);
        }
      }
    } else {
      // Public modules:
      // - Foo.apinotes
      // - Foo_private.apinotes (if there are private submodules)
      // Private modules:
      // - Bar.apinotes (except that 'Bar' probably already has the word
      //   "Private" in it in practice)
      tryAPINotes(module->Directory, /*wantPublic=*/true);
      if (!module->ModuleMapIsPrivate && hasPrivateSubmodules(module))
        tryAPINotes(module->Directory, /*wantPublic=*/false);
    }

    if (foundAny)
      return numReaders > 0;
  }

  // Second, look for API notes for this module in the module API
  // notes search paths.
  for (const auto &searchPath : searchPaths) {
    if (auto searchDir = fileMgr.getDirectory(searchPath)) {
      if (auto file = findAPINotesFile(searchDir, moduleName)) {
        CurrentModuleReaders[0] = loadAPINotes(file).release();
        return !getCurrentModuleReaders().empty();
      }
    }
  }

  // Didn't find any API notes.
  return false;
}

llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocation Loc) {
  llvm::SmallVector<APINotesReader *, 2> Results;

  // If there are readers for the current module, return them.
  if (!getCurrentModuleReaders().empty()) {
    Results.append(getCurrentModuleReaders().begin(), getCurrentModuleReaders().end());
    return Results;
  }

  // If we're not allowed to implicitly load API notes files, we're done.
  if (!ImplicitAPINotes) return Results;

  // If we don't have source location information, we're done.
  if (Loc.isInvalid()) return Results;

  // API notes are associated with the expansion location. Retrieve the
  // file for this location.
  SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc);
  FileID ID = SourceMgr.getFileID(ExpansionLoc);
  if (ID.isInvalid()) return Results;
  const FileEntry *File = SourceMgr.getFileEntryForID(ID);
  if (!File) return Results;

  // Look for API notes in the directory corresponding to this file, or one of
  // its its parent directories.
  const DirectoryEntry *Dir = File->getDir();
  FileManager &FileMgr = SourceMgr.getFileManager();
  llvm::SetVector<const DirectoryEntry *,
                  SmallVector<const DirectoryEntry *, 4>,
                  llvm::SmallPtrSet<const DirectoryEntry *, 4>> DirsVisited;
  do {
    // Look for an API notes reader for this header search directory.
    auto Known = Readers.find(Dir);

    // If we already know the answer, chase it.
    if (Known != Readers.end()) {
      ++NumDirectoryCacheHits;

      // We've been redirected to another directory for answers. Follow it.
      if (auto OtherDir = Known->second.dyn_cast<const DirectoryEntry *>()) {
        DirsVisited.insert(Dir);
        Dir = OtherDir;
        continue;
      }

      // We have the answer.
      if (auto Reader = Known->second.dyn_cast<APINotesReader *>())
        Results.push_back(Reader);
      break;
    }

    // Look for API notes corresponding to this directory.
    StringRef Path = Dir->getName();
    if (llvm::sys::path::extension(Path) == ".framework") {
      // If this is a framework directory, check whether there are API notes
      // in the APINotes subdirectory.
      auto FrameworkName = llvm::sys::path::stem(Path);
      ++NumFrameworksSearched;

      // Look for API notes for both the public and private headers.
      const DirectoryEntry *PublicDir
        = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true);
      const DirectoryEntry *PrivateDir
        = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false);

      if (PublicDir || PrivateDir) {
        // We found API notes: don't ever look past the framework directory.
        Readers[Dir] = nullptr;

        // Pretend we found the result in the public or private directory,
        // as appropriate. All headers should be in one of those two places,
        // but be defensive here.
        if (!DirsVisited.empty()) {
          if (DirsVisited.back() == PublicDir) {
            DirsVisited.pop_back();
            Dir = PublicDir;
          } else if (DirsVisited.back() == PrivateDir) {
            DirsVisited.pop_back();
            Dir = PrivateDir;
          }
        }

        // Grab the result.
        if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
          Results.push_back(Reader);
        break;
      }
    } else {
      // Look for an APINotes file in this directory.
      llvm::SmallString<128> APINotesPath;
      APINotesPath += Dir->getName();
      llvm::sys::path::append(APINotesPath,
                              (llvm::Twine("APINotes.")
                                 + SOURCE_APINOTES_EXTENSION));

      // If there is an API notes file here, try to load it.
      ++NumDirectoriesSearched;
      if (const FileEntry *APINotesFile = FileMgr.getFile(APINotesPath)) {
        if (!loadAPINotes(Dir, APINotesFile)) {
          ++NumHeaderAPINotes;
          if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
            Results.push_back(Reader);
          break;
        }
      }
    }

    // We didn't find anything. Look at the parent directory.
    if (!DirsVisited.insert(Dir)) {
      Dir = 0;
      break;
    }

    StringRef ParentPath = llvm::sys::path::parent_path(Path);
    while (llvm::sys::path::stem(ParentPath) == "..") {
      ParentPath = llvm::sys::path::parent_path(ParentPath);
    }
    if (ParentPath.empty()) {
      Dir = nullptr;
    } else {
      Dir = FileMgr.getDirectory(ParentPath);
    }
  } while (Dir);

  // Path compression for all of the directories we visited, redirecting
  // them to the directory we ended on. If no API notes were found, the
  // resulting directory will be NULL, indicating no API notes.
  for (const auto Visited : DirsVisited) {
    Readers[Visited] = Dir;
  }

  return Results;
}
<commit_msg>[APINotes] Adopt FileManager's error-returning APIs<commit_after>//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file implements the APINotesManager class.
//
//===----------------------------------------------------------------------===//

#include "clang/APINotes/APINotesManager.h"
#include "clang/APINotes/APINotesOptions.h"
#include "clang/APINotes/APINotesReader.h"
#include "clang/APINotes/APINotesYAMLCompiler.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceMgrAdapter.h"
#include "clang/Basic/Version.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include <sys/stat.h>

using namespace clang;
using namespace api_notes;

#define DEBUG_TYPE "API Notes"
STATISTIC(NumHeaderAPINotes,
          "non-framework API notes files loaded");
STATISTIC(NumPublicFrameworkAPINotes,
          "framework public API notes loaded");
STATISTIC(NumPrivateFrameworkAPINotes,
          "framework private API notes loaded");
STATISTIC(NumFrameworksSearched,
          "frameworks searched");
STATISTIC(NumDirectoriesSearched,
          "header directories searched");
STATISTIC(NumDirectoryCacheHits,
          "directory cache hits");

namespace {
  /// Prints two successive strings, which much be kept alive as long as the
  /// PrettyStackTrace entry.
  class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
    StringRef First, Second;
  public:
    PrettyStackTraceDoubleString(StringRef first, StringRef second)
        : First(first), Second(second) {}
    void print(raw_ostream &OS) const override {
      OS << First << Second;
    }
  };
}

APINotesManager::APINotesManager(SourceManager &sourceMgr,
                                 const LangOptions &langOpts)
  : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes) { }

APINotesManager::~APINotesManager() {
  // Free the API notes readers.
  for (const auto &entry : Readers) {
    if (auto reader = entry.second.dyn_cast<APINotesReader *>()) {
      delete reader;
    }
  }

  delete CurrentModuleReaders[0];
  delete CurrentModuleReaders[1];
}

std::unique_ptr<APINotesReader>
APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) {
  PrettyStackTraceDoubleString trace("Loading API notes from ",
                                     apiNotesFile->getName());

  // Open the source file.
  auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
  auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation());
  if (!sourceBuffer) return nullptr;

  // Compile the API notes source into a buffer.
  // FIXME: Either propagate OSType through or, better yet, improve the binary
  // APINotes format to maintain complete availability information.
  // FIXME: We don't even really need to go through the binary format at all;
  // we're just going to immediately deserialize it again.
  llvm::SmallVector<char, 1024> apiNotesBuffer;
  std::unique_ptr<llvm::MemoryBuffer> compiledBuffer;
  {
    SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(),
                                   diag::err_apinotes_message,
                                   diag::warn_apinotes_message,
                                   diag::note_apinotes_message,
                                   apiNotesFile);
    llvm::raw_svector_ostream OS(apiNotesBuffer);
    if (api_notes::compileAPINotes(sourceBuffer->getBuffer(),
                                   SourceMgr.getFileEntryForID(sourceFileID),
                                   OS,
                                   srcMgrAdapter.getDiagHandler(),
                                   srcMgrAdapter.getDiagContext()))
      return nullptr;

    // Make a copy of the compiled form into the buffer.
    compiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
               StringRef(apiNotesBuffer.data(), apiNotesBuffer.size()));
  }

  // Load the binary form we just compiled.
  auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion);
  assert(reader && "Could not load the API notes we just generated?");
  return reader;
}

bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,
                                   const FileEntry *APINotesFile) {
  assert(Readers.find(HeaderDir) == Readers.end());
  if (auto reader = loadAPINotes(APINotesFile)) {
    Readers[HeaderDir] = reader.release();
    return false;
  }

  Readers[HeaderDir] = nullptr;
  return true;
}

const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directory,
                                                   StringRef basename,
                                                   bool wantPublic) {
  FileManager &fileMgr = SourceMgr.getFileManager();

  llvm::SmallString<128> path;
  path += directory->getName();

  StringRef basenameSuffix = "";
  if (!wantPublic) basenameSuffix = "_private";

  // Look for the source API notes file.
  llvm::sys::path::append(path,
    llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION);
  auto file = fileMgr.getFile(path, /*Open*/true);
  return file ? *file : nullptr;
}

const DirectoryEntry *APINotesManager::loadFrameworkAPINotes(
                        llvm::StringRef FrameworkPath,
                        llvm::StringRef FrameworkName,
                        bool Public) {
  FileManager &FileMgr = SourceMgr.getFileManager();

  llvm::SmallString<128> Path;
  Path += FrameworkPath;
  unsigned FrameworkNameLength = Path.size();

  // Form the path to the APINotes file.
  llvm::sys::path::append(Path, "APINotes");
  if (Public)
    llvm::sys::path::append(Path,
                            (llvm::Twine(FrameworkName) + "."
                              + SOURCE_APINOTES_EXTENSION));
  else
    llvm::sys::path::append(Path,
                            (llvm::Twine(FrameworkName) + "_private."
                              + SOURCE_APINOTES_EXTENSION));

  // Try to open the APINotes file.
  auto APINotesFile = FileMgr.getFile(Path);
  if (!APINotesFile)
    return nullptr;

  // Form the path to the corresponding header directory.
  Path.resize(FrameworkNameLength);
  if (Public)
    llvm::sys::path::append(Path, "Headers");
  else
    llvm::sys::path::append(Path, "PrivateHeaders");

  // Try to access the header directory.
  auto HeaderDir = FileMgr.getDirectory(Path);
  if (!HeaderDir)
    return nullptr;

  // Try to load the API notes.
  if (loadAPINotes(*HeaderDir, *APINotesFile))
    return nullptr;

  // Success: return the header directory.
  if (Public)
    ++NumPublicFrameworkAPINotes;
  else
    ++NumPrivateFrameworkAPINotes;
  return HeaderDir;
}

static void checkPrivateAPINotesName(DiagnosticsEngine &diags,
                                     const FileEntry *file,
                                     const Module *module) {
  if (file->tryGetRealPathName().empty())
    return;

  StringRef realFilename =
      llvm::sys::path::filename(file->tryGetRealPathName());
  StringRef realStem = llvm::sys::path::stem(realFilename);
  if (realStem.endswith("_private"))
    return;

  unsigned diagID = diag::warn_apinotes_private_case;
  if (module->IsSystem)
    diagID = diag::warn_apinotes_private_case_system;

  diags.Report(SourceLocation(), diagID) << module->Name << realFilename;
}

/// \returns true if any of \p module's immediate submodules are defined in a
/// private module map
static bool hasPrivateSubmodules(const Module *module) {
  return llvm::any_of(module->submodules(), [](const Module *submodule) {
    return submodule->ModuleMapIsPrivate;
  });
}

bool APINotesManager::loadCurrentModuleAPINotes(
                   const Module *module,
                   bool lookInModule,
                   ArrayRef<std::string> searchPaths) {
  assert(!CurrentModuleReaders[0] &&
         "Already loaded API notes for the current module?");

  FileManager &fileMgr = SourceMgr.getFileManager();
  auto moduleName = module->getTopLevelModuleName();

  // First, look relative to the module itself.
  if (lookInModule) {
    bool foundAny = false;
    unsigned numReaders = 0;

    // Local function to try loading an API notes file in the given directory.
    auto tryAPINotes = [&](const DirectoryEntry *dir, bool wantPublic) {
      if (auto file = findAPINotesFile(dir, moduleName, wantPublic)) {
        foundAny = true;

        if (!wantPublic)
          checkPrivateAPINotesName(SourceMgr.getDiagnostics(), file, module);

        // Try to load the API notes file.
        CurrentModuleReaders[numReaders] = loadAPINotes(file).release();
        if (CurrentModuleReaders[numReaders])
          ++numReaders;
      }
    };

    if (module->IsFramework) {
      // For frameworks, we search in the "Headers" or "PrivateHeaders"
      // subdirectory.
      //
      // Public modules:
      // - Headers/Foo.apinotes
      // - PrivateHeaders/Foo_private.apinotes (if there are private submodules)
      // Private modules:
      // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has
      //   the word "Private" in it in practice)
      llvm::SmallString<128> path;
      path += module->Directory->getName();

      if (!module->ModuleMapIsPrivate) {
        unsigned pathLen = path.size();

        llvm::sys::path::append(path, "Headers");
        if (auto apinotesDir = fileMgr.getDirectory(path))
          tryAPINotes(*apinotesDir, /*wantPublic=*/true);

        path.resize(pathLen);
      }

      if (module->ModuleMapIsPrivate || hasPrivateSubmodules(module)) {
        llvm::sys::path::append(path, "PrivateHeaders");
        if (auto privateAPINotesDir = fileMgr.getDirectory(path)) {
          tryAPINotes(*privateAPINotesDir,
                      /*wantPublic=*/module->ModuleMapIsPrivate);
        }
      }
    } else {
      // Public modules:
      // - Foo.apinotes
      // - Foo_private.apinotes (if there are private submodules)
      // Private modules:
      // - Bar.apinotes (except that 'Bar' probably already has the word
      //   "Private" in it in practice)
      tryAPINotes(module->Directory, /*wantPublic=*/true);
      if (!module->ModuleMapIsPrivate && hasPrivateSubmodules(module))
        tryAPINotes(module->Directory, /*wantPublic=*/false);
    }

    if (foundAny)
      return numReaders > 0;
  }

  // Second, look for API notes for this module in the module API
  // notes search paths.
  for (const auto &searchPath : searchPaths) {
    if (auto searchDir = fileMgr.getDirectory(searchPath)) {
      if (auto file = findAPINotesFile(*searchDir, moduleName)) {
        CurrentModuleReaders[0] = loadAPINotes(file).release();
        return !getCurrentModuleReaders().empty();
      }
    }
  }

  // Didn't find any API notes.
  return false;
}

llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocation Loc) {
  llvm::SmallVector<APINotesReader *, 2> Results;

  // If there are readers for the current module, return them.
  if (!getCurrentModuleReaders().empty()) {
    Results.append(getCurrentModuleReaders().begin(), getCurrentModuleReaders().end());
    return Results;
  }

  // If we're not allowed to implicitly load API notes files, we're done.
  if (!ImplicitAPINotes) return Results;

  // If we don't have source location information, we're done.
  if (Loc.isInvalid()) return Results;

  // API notes are associated with the expansion location. Retrieve the
  // file for this location.
  SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc);
  FileID ID = SourceMgr.getFileID(ExpansionLoc);
  if (ID.isInvalid()) return Results;
  const FileEntry *File = SourceMgr.getFileEntryForID(ID);
  if (!File) return Results;

  // Look for API notes in the directory corresponding to this file, or one of
  // its its parent directories.
  const DirectoryEntry *Dir = File->getDir();
  FileManager &FileMgr = SourceMgr.getFileManager();
  llvm::SetVector<const DirectoryEntry *,
                  SmallVector<const DirectoryEntry *, 4>,
                  llvm::SmallPtrSet<const DirectoryEntry *, 4>> DirsVisited;
  do {
    // Look for an API notes reader for this header search directory.
    auto Known = Readers.find(Dir);

    // If we already know the answer, chase it.
    if (Known != Readers.end()) {
      ++NumDirectoryCacheHits;

      // We've been redirected to another directory for answers. Follow it.
      if (auto OtherDir = Known->second.dyn_cast<const DirectoryEntry *>()) {
        DirsVisited.insert(Dir);
        Dir = OtherDir;
        continue;
      }

      // We have the answer.
      if (auto Reader = Known->second.dyn_cast<APINotesReader *>())
        Results.push_back(Reader);
      break;
    }

    // Look for API notes corresponding to this directory.
    StringRef Path = Dir->getName();
    if (llvm::sys::path::extension(Path) == ".framework") {
      // If this is a framework directory, check whether there are API notes
      // in the APINotes subdirectory.
      auto FrameworkName = llvm::sys::path::stem(Path);
      ++NumFrameworksSearched;

      // Look for API notes for both the public and private headers.
      const DirectoryEntry *PublicDir
        = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true);
      const DirectoryEntry *PrivateDir
        = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false);

      if (PublicDir || PrivateDir) {
        // We found API notes: don't ever look past the framework directory.
        Readers[Dir] = nullptr;

        // Pretend we found the result in the public or private directory,
        // as appropriate. All headers should be in one of those two places,
        // but be defensive here.
        if (!DirsVisited.empty()) {
          if (DirsVisited.back() == PublicDir) {
            DirsVisited.pop_back();
            Dir = PublicDir;
          } else if (DirsVisited.back() == PrivateDir) {
            DirsVisited.pop_back();
            Dir = PrivateDir;
          }
        }

        // Grab the result.
        if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
          Results.push_back(Reader);
        break;
      }
    } else {
      // Look for an APINotes file in this directory.
      llvm::SmallString<128> APINotesPath;
      APINotesPath += Dir->getName();
      llvm::sys::path::append(APINotesPath,
                              (llvm::Twine("APINotes.")
                                 + SOURCE_APINOTES_EXTENSION));

      // If there is an API notes file here, try to load it.
      ++NumDirectoriesSearched;
      if (auto APINotesFile = FileMgr.getFile(APINotesPath)) {
        if (!loadAPINotes(Dir, *APINotesFile)) {
          ++NumHeaderAPINotes;
          if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
            Results.push_back(Reader);
          break;
        }
      }
    }

    // We didn't find anything. Look at the parent directory.
    if (!DirsVisited.insert(Dir)) {
      Dir = 0;
      break;
    }

    StringRef ParentPath = llvm::sys::path::parent_path(Path);
    while (llvm::sys::path::stem(ParentPath) == "..") {
      ParentPath = llvm::sys::path::parent_path(ParentPath);
    }
    if (ParentPath.empty()) {
      Dir = nullptr;
    } else {
      auto DirEntry = FileMgr.getDirectory(ParentPath)
      Dir = DirEntry ? *DirEntry : nullptr;
    }
  } while (Dir);

  // Path compression for all of the directories we visited, redirecting
  // them to the directory we ended on. If no API notes were found, the
  // resulting directory will be NULL, indicating no API notes.
  for (const auto Visited : DirsVisited) {
    Readers[Visited] = Dir;
  }

  return Results;
}
<|endoftext|>


<commit_before>#include "kwm.h"

const std::string KwmCurrentVersion = "Kwm Version 1.0.7";
const std::string PlistFile = "com.koekeishiya.kwm.plist";

CFMachPortRef EventTap;

kwm_path KWMPath = {};
kwm_screen KWMScreen = {};
kwm_toggles KWMToggles = {};
kwm_focus KWMFocus = {};
kwm_mode KWMMode = {};
kwm_tiling KWMTiling = {};
kwm_cache KWMCache = {};
kwm_thread KWMThread = {};
kwm_hotkeys KWMHotkeys = {};
kwm_border KWMBorder = {};

CGEventRef CGEventCallback(CGEventTapProxy Proxy, CGEventType Type, CGEventRef Event, void *Refcon)
{
    pthread_mutex_lock(&KWMThread.Lock);

    switch(Type)
    {
        case kCGEventTapDisabledByTimeout:
        case kCGEventTapDisabledByUserInput:
        {
            DEBUG("Restarting Event Tap")
            CGEventTapEnable(EventTap, true);
        } break;
        case kCGEventKeyDown:
        {
            if(KWMToggles.UseBuiltinHotkeys && KwmMainHotkeyTrigger(&Event))
            {
                    pthread_mutex_unlock(&KWMThread.Lock);
                    return NULL;
            }

            if(KWMMode.Focus == FocusModeAutofocus)
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventKeyUp:
        {
            if(KWMMode.Focus == FocusModeAutofocus)
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventMouseMoved:
        {
            if(KWMMode.Focus != FocusModeDisabled)
                FocusWindowBelowCursor();
        } break;
        case kCGEventLeftMouseDown:
        {
            DEBUG("Left mouse button was pressed")
            FocusWindowBelowCursor();
            if(KWMToggles.EnableDragAndDrop && IsCursorInsideFocusedWindow())
               KWMToggles.WindowDragInProgress = true;
        } break;
        case kCGEventLeftMouseUp:
        {
            DEBUG("Left mouse button was released")
            if(KWMToggles.EnableDragAndDrop && KWMToggles.WindowDragInProgress)
            {
                if(!IsCursorInsideFocusedWindow())
                    ToggleFocusedWindowFloating();

                KWMToggles.WindowDragInProgress = false;
            }
        } break;
    }

    pthread_mutex_unlock(&KWMThread.Lock);
    return Event;
}

void KwmQuit()
{
    exit(0);
}

void * KwmWindowMonitor(void*)
{
    while(1)
    {
        pthread_mutex_lock(&KWMThread.Lock);
        UpdateWindowTree();
        pthread_mutex_unlock(&KWMThread.Lock);
        usleep(200000);
    }
}

bool IsPrefixOfString(std::string &Line, std::string Prefix)
{
    bool Result = false;

    if(Line.substr(0, Prefix.size()) == Prefix)
    {
        Line = Line.substr(Prefix.size()+1);
        Result = true;
    }

    return Result;
}

void KwmReloadConfig()
{
    KwmClearSettings();
    KwmExecuteConfig();
}

void KwmClearSettings()
{
    std::map<std::string, std::vector<CFTypeRef> >::iterator It;
    for(It = KWMTiling.AllowedWindowRoles.begin(); It != KWMTiling.AllowedWindowRoles.end(); ++It)
    {
        std::vector<CFTypeRef> &WindowRoles = It->second;
        for(std::size_t RoleIndex = 0; RoleIndex < WindowRoles.size(); ++RoleIndex)
            CFRelease(WindowRoles[RoleIndex]);

        WindowRoles.clear();
    }

    KWMTiling.FloatingAppLst.clear();
    KWMHotkeys.List.clear();
    KWMHotkeys.Prefix.Enabled = false;
}

void KwmExecuteConfig()
{
    char *HomeP = std::getenv("HOME");
    if(!HomeP)
    {
        DEBUG("Failed to get environment variable 'HOME'")
        return;
    }

    KWMPath.EnvHome = HomeP;
    KWMPath.ConfigFolder = ".kwm";
    std::ifstream ConfigFD(KWMPath.EnvHome + "/" + KWMPath.ConfigFolder + "/" + KWMPath.ConfigFile);
    if(ConfigFD.fail())
    {
        DEBUG("Could not open " << KWMPath.EnvHome << "/" << KWMPath.ConfigFolder << "/" << KWMPath.ConfigFile
              << ", make sure the file exists." << std::endl)
        return;
    }

    std::string Line;
    while(std::getline(ConfigFD, Line))
    {
        if(!Line.empty() && Line[0] != '#')
        {
            if(IsPrefixOfString(Line, "kwmc"))
                KwmInterpretCommand(Line, 0);
            else if(IsPrefixOfString(Line, "sys"))
                system(Line.c_str());
        }
    }

    ConfigFD.close();
}

bool IsKwmAlreadyAddedToLaunchd()
{
    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    struct stat attr;
    int Result = stat(SymlinkFullPath.c_str(), &attr);
    if(Result == -1)
        return false;

    return true;
}

void AddKwmToLaunchd()
{
    if(IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    std::ifstream TemplateFD(KWMPath.FilePath + "/kwm_template.plist");
    if(TemplateFD.fail())
        return;

    std::ofstream OutFD(SymlinkFullPath);
    if(OutFD.fail())
        return;

    std::string Line;
    std::vector<std::string> PlistContents;
    while(std::getline(TemplateFD, Line))
        PlistContents.push_back(Line);

    DEBUG("AddKwmToLaunchd() Creating file: " << SymlinkFullPath)
    for(std::size_t LineNumber = 0; LineNumber < PlistContents.size(); ++LineNumber)
    {
        if(LineNumber == 8)
            OutFD << "    <string>" + KWMPath.FilePath + "/kwm</string>" << std::endl;
        else
            OutFD << PlistContents[LineNumber] << std::endl;
    }

    TemplateFD.close();
    OutFD.close();
}

void RemoveKwmFromLaunchd()
{
    if(!IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;
    std::string RemoveSymlink = "rm " + SymlinkFullPath;

    system(RemoveSymlink.c_str());
    DEBUG("RemoveKwmFromLaunchd() Removing file: " << SymlinkFullPath)
}

bool GetKwmFilePath()
{
    bool Result = false;
    char PathBuf[PROC_PIDPATHINFO_MAXSIZE];
    pid_t Pid = getpid();
    int Ret = proc_pidpath(Pid, PathBuf, sizeof(PathBuf));
    if (Ret > 0)
    {
        KWMPath.FilePath = PathBuf;

        std::size_t Split = KWMPath.FilePath.find_last_of("/\\");
        KWMPath.FilePath = KWMPath.FilePath.substr(0, Split);
        KWMPath.HotkeySOFullPath = KWMPath.FilePath + "/hotkeys.so";
        Result = true;
    }

    return Result;
}

void KwmInit()
{
    if(!CheckPrivileges())
        Fatal("Could not access OSX Accessibility!");

    if (pthread_mutex_init(&KWMThread.Lock, NULL) != 0)
        Fatal("Could not create mutex!");

    if(KwmStartDaemon())
        pthread_create(&KWMThread.Daemon, NULL, &KwmDaemonHandleConnectionBG, NULL);
    else
        Fatal("Kwm: Could not start daemon..");

    signal(SIGSEGV, SignalHandler);

    KWMScreen.SplitRatio = 0.5;
    KWMScreen.SplitMode = -1;
    KWMScreen.MarkedWindow = -1;
    KWMScreen.OldScreenID = -1;
    KWMScreen.PrevSpace = -1;
    KWMScreen.DefaultOffset = CreateDefaultScreenOffset();
    KWMScreen.MaxCount = 5;
    KWMScreen.ActiveCount = 0;
    KWMScreen.UpdateSpace = true;

    KWMToggles.EnableTilingMode = true;
    KWMToggles.UseBuiltinHotkeys = true;
    KWMToggles.EnableDragAndDrop = true;
    KWMToggles.UseContextMenuFix = true;
    KWMToggles.UseMouseFollowsFocus = true;
    KWMToggles.WindowDragInProgress = false;

    KWMMode.Space = SpaceModeBSP;
    KWMMode.Focus = FocusModeAutoraise;
    KWMMode.Cycle = CycleModeScreen;

    KWMHotkeys.Prefix.Enabled = false;
    KWMHotkeys.Prefix.Global = false;
    KWMHotkeys.Prefix.Active = false;
    KWMHotkeys.Prefix.Timeout = 0.75;

    KWMPath.ConfigFile = "kwmrc";
    GetKwmFilePath();

    KwmExecuteConfig();
    GetActiveDisplays();

    pthread_create(&KWMThread.WindowMonitor, NULL, &KwmWindowMonitor, NULL);
}

bool CheckPrivileges()
{
    bool Result = false;
    const void * Keys[] = { kAXTrustedCheckOptionPrompt };
    const void * Values[] = { kCFBooleanTrue };

    CFDictionaryRef Options;
    Options = CFDictionaryCreate(kCFAllocatorDefault,
            Keys, Values, sizeof(Keys) / sizeof(*Keys),
            &kCFCopyStringDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

    Result = AXIsProcessTrustedWithOptions(Options);
    CFRelease(Options);

    return Result;
}

bool CheckArguments(int argc, char **argv)
{
    bool Result = false;

    if(argc == 2)
    {
        std::string Arg = argv[1];
        if(Arg == "--version")
        {
            std::cout << KwmCurrentVersion << std::endl;
            Result = true;
        }
    }

    return Result;
}

void SignalHandler(int Signum)
{
    std::string Terminate = "quit";
    if(KWMBorder.FHandle)
    {
        fwrite(Terminate.c_str(), Terminate.size(), 1, KWMBorder.FHandle);
        fflush(KWMBorder.FHandle);
    }

    if(KWMBorder.MHandle)
    {
        fwrite(Terminate.c_str(), Terminate.size(), 1, KWMBorder.MHandle);
        fflush(KWMBorder.MHandle);
    }

    signal(Signum, SIG_DFL);
    kill(getpid(), Signum);
}

void Fatal(const std::string &Err)
{
    std::cout << Err << std::endl;
    exit(1);
}

int main(int argc, char **argv)
{
    if(CheckArguments(argc, argv))
        return 0;

    KwmInit();
    CGEventMask EventMask;
    EventMask = ((1 << kCGEventKeyDown) |
                 (1 << kCGEventKeyUp) |
                 (1 << kCGEventMouseMoved) |
                 (1 << kCGEventLeftMouseDown) |
                 (1 << kCGEventLeftMouseUp));

    EventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, EventMask, CGEventCallback, NULL);
    if(!EventTap || !CGEventTapIsEnabled(EventTap))
        Fatal("ERROR: Could not create event-tap!");

    CFRunLoopSourceRef RunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, EventTap, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), RunLoopSource, kCFRunLoopCommonModes);
    CGEventTapEnable(EventTap, true);
    NSApplicationLoad();
    CFRunLoopRun();

    return 0;
}
<commit_msg>version 1.0.8<commit_after>#include "kwm.h"

const std::string KwmCurrentVersion = "Kwm Version 1.0.8";
const std::string PlistFile = "com.koekeishiya.kwm.plist";

CFMachPortRef EventTap;

kwm_path KWMPath = {};
kwm_screen KWMScreen = {};
kwm_toggles KWMToggles = {};
kwm_focus KWMFocus = {};
kwm_mode KWMMode = {};
kwm_tiling KWMTiling = {};
kwm_cache KWMCache = {};
kwm_thread KWMThread = {};
kwm_hotkeys KWMHotkeys = {};
kwm_border KWMBorder = {};

CGEventRef CGEventCallback(CGEventTapProxy Proxy, CGEventType Type, CGEventRef Event, void *Refcon)
{
    pthread_mutex_lock(&KWMThread.Lock);

    switch(Type)
    {
        case kCGEventTapDisabledByTimeout:
        case kCGEventTapDisabledByUserInput:
        {
            DEBUG("Restarting Event Tap")
            CGEventTapEnable(EventTap, true);
        } break;
        case kCGEventKeyDown:
        {
            if(KWMToggles.UseBuiltinHotkeys && KwmMainHotkeyTrigger(&Event))
            {
                    pthread_mutex_unlock(&KWMThread.Lock);
                    return NULL;
            }

            if(KWMMode.Focus == FocusModeAutofocus)
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventKeyUp:
        {
            if(KWMMode.Focus == FocusModeAutofocus)
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventMouseMoved:
        {
            if(KWMMode.Focus != FocusModeDisabled)
                FocusWindowBelowCursor();
        } break;
        case kCGEventLeftMouseDown:
        {
            DEBUG("Left mouse button was pressed")
            FocusWindowBelowCursor();
            if(KWMToggles.EnableDragAndDrop && IsCursorInsideFocusedWindow())
               KWMToggles.WindowDragInProgress = true;
        } break;
        case kCGEventLeftMouseUp:
        {
            DEBUG("Left mouse button was released")
            if(KWMToggles.EnableDragAndDrop && KWMToggles.WindowDragInProgress)
            {
                if(!IsCursorInsideFocusedWindow())
                    ToggleFocusedWindowFloating();

                KWMToggles.WindowDragInProgress = false;
            }
        } break;
    }

    pthread_mutex_unlock(&KWMThread.Lock);
    return Event;
}

void KwmQuit()
{
    exit(0);
}

void * KwmWindowMonitor(void*)
{
    while(1)
    {
        pthread_mutex_lock(&KWMThread.Lock);
        UpdateWindowTree();
        pthread_mutex_unlock(&KWMThread.Lock);
        usleep(200000);
    }
}

bool IsPrefixOfString(std::string &Line, std::string Prefix)
{
    bool Result = false;

    if(Line.substr(0, Prefix.size()) == Prefix)
    {
        Line = Line.substr(Prefix.size()+1);
        Result = true;
    }

    return Result;
}

void KwmReloadConfig()
{
    KwmClearSettings();
    KwmExecuteConfig();
}

void KwmClearSettings()
{
    std::map<std::string, std::vector<CFTypeRef> >::iterator It;
    for(It = KWMTiling.AllowedWindowRoles.begin(); It != KWMTiling.AllowedWindowRoles.end(); ++It)
    {
        std::vector<CFTypeRef> &WindowRoles = It->second;
        for(std::size_t RoleIndex = 0; RoleIndex < WindowRoles.size(); ++RoleIndex)
            CFRelease(WindowRoles[RoleIndex]);

        WindowRoles.clear();
    }

    KWMTiling.FloatingAppLst.clear();
    KWMHotkeys.List.clear();
    KWMHotkeys.Prefix.Enabled = false;
}

void KwmExecuteConfig()
{
    char *HomeP = std::getenv("HOME");
    if(!HomeP)
    {
        DEBUG("Failed to get environment variable 'HOME'")
        return;
    }

    KWMPath.EnvHome = HomeP;
    KWMPath.ConfigFolder = ".kwm";
    std::ifstream ConfigFD(KWMPath.EnvHome + "/" + KWMPath.ConfigFolder + "/" + KWMPath.ConfigFile);
    if(ConfigFD.fail())
    {
        DEBUG("Could not open " << KWMPath.EnvHome << "/" << KWMPath.ConfigFolder << "/" << KWMPath.ConfigFile
              << ", make sure the file exists." << std::endl)
        return;
    }

    std::string Line;
    while(std::getline(ConfigFD, Line))
    {
        if(!Line.empty() && Line[0] != '#')
        {
            if(IsPrefixOfString(Line, "kwmc"))
                KwmInterpretCommand(Line, 0);
            else if(IsPrefixOfString(Line, "sys"))
                system(Line.c_str());
        }
    }

    ConfigFD.close();
}

bool IsKwmAlreadyAddedToLaunchd()
{
    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    struct stat attr;
    int Result = stat(SymlinkFullPath.c_str(), &attr);
    if(Result == -1)
        return false;

    return true;
}

void AddKwmToLaunchd()
{
    if(IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    std::ifstream TemplateFD(KWMPath.FilePath + "/kwm_template.plist");
    if(TemplateFD.fail())
        return;

    std::ofstream OutFD(SymlinkFullPath);
    if(OutFD.fail())
        return;

    std::string Line;
    std::vector<std::string> PlistContents;
    while(std::getline(TemplateFD, Line))
        PlistContents.push_back(Line);

    DEBUG("AddKwmToLaunchd() Creating file: " << SymlinkFullPath)
    for(std::size_t LineNumber = 0; LineNumber < PlistContents.size(); ++LineNumber)
    {
        if(LineNumber == 8)
            OutFD << "    <string>" + KWMPath.FilePath + "/kwm</string>" << std::endl;
        else
            OutFD << PlistContents[LineNumber] << std::endl;
    }

    TemplateFD.close();
    OutFD.close();
}

void RemoveKwmFromLaunchd()
{
    if(!IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;
    std::string RemoveSymlink = "rm " + SymlinkFullPath;

    system(RemoveSymlink.c_str());
    DEBUG("RemoveKwmFromLaunchd() Removing file: " << SymlinkFullPath)
}

bool GetKwmFilePath()
{
    bool Result = false;
    char PathBuf[PROC_PIDPATHINFO_MAXSIZE];
    pid_t Pid = getpid();
    int Ret = proc_pidpath(Pid, PathBuf, sizeof(PathBuf));
    if (Ret > 0)
    {
        KWMPath.FilePath = PathBuf;

        std::size_t Split = KWMPath.FilePath.find_last_of("/\\");
        KWMPath.FilePath = KWMPath.FilePath.substr(0, Split);
        KWMPath.HotkeySOFullPath = KWMPath.FilePath + "/hotkeys.so";
        Result = true;
    }

    return Result;
}

void KwmInit()
{
    if(!CheckPrivileges())
        Fatal("Could not access OSX Accessibility!");

    if (pthread_mutex_init(&KWMThread.Lock, NULL) != 0)
        Fatal("Could not create mutex!");

    if(KwmStartDaemon())
        pthread_create(&KWMThread.Daemon, NULL, &KwmDaemonHandleConnectionBG, NULL);
    else
        Fatal("Kwm: Could not start daemon..");

    signal(SIGSEGV, SignalHandler);

    KWMScreen.SplitRatio = 0.5;
    KWMScreen.SplitMode = -1;
    KWMScreen.MarkedWindow = -1;
    KWMScreen.OldScreenID = -1;
    KWMScreen.PrevSpace = -1;
    KWMScreen.DefaultOffset = CreateDefaultScreenOffset();
    KWMScreen.MaxCount = 5;
    KWMScreen.ActiveCount = 0;
    KWMScreen.UpdateSpace = true;

    KWMToggles.EnableTilingMode = true;
    KWMToggles.UseBuiltinHotkeys = true;
    KWMToggles.EnableDragAndDrop = true;
    KWMToggles.UseContextMenuFix = true;
    KWMToggles.UseMouseFollowsFocus = true;
    KWMToggles.WindowDragInProgress = false;

    KWMMode.Space = SpaceModeBSP;
    KWMMode.Focus = FocusModeAutoraise;
    KWMMode.Cycle = CycleModeScreen;

    KWMHotkeys.Prefix.Enabled = false;
    KWMHotkeys.Prefix.Global = false;
    KWMHotkeys.Prefix.Active = false;
    KWMHotkeys.Prefix.Timeout = 0.75;

    KWMPath.ConfigFile = "kwmrc";
    GetKwmFilePath();

    KwmExecuteConfig();
    GetActiveDisplays();

    pthread_create(&KWMThread.WindowMonitor, NULL, &KwmWindowMonitor, NULL);
}

bool CheckPrivileges()
{
    bool Result = false;
    const void * Keys[] = { kAXTrustedCheckOptionPrompt };
    const void * Values[] = { kCFBooleanTrue };

    CFDictionaryRef Options;
    Options = CFDictionaryCreate(kCFAllocatorDefault,
            Keys, Values, sizeof(Keys) / sizeof(*Keys),
            &kCFCopyStringDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

    Result = AXIsProcessTrustedWithOptions(Options);
    CFRelease(Options);

    return Result;
}

bool CheckArguments(int argc, char **argv)
{
    bool Result = false;

    if(argc == 2)
    {
        std::string Arg = argv[1];
        if(Arg == "--version")
        {
            std::cout << KwmCurrentVersion << std::endl;
            Result = true;
        }
    }

    return Result;
}

void SignalHandler(int Signum)
{
    std::string Terminate = "quit";
    if(KWMBorder.FHandle)
    {
        fwrite(Terminate.c_str(), Terminate.size(), 1, KWMBorder.FHandle);
        fflush(KWMBorder.FHandle);
    }

    if(KWMBorder.MHandle)
    {
        fwrite(Terminate.c_str(), Terminate.size(), 1, KWMBorder.MHandle);
        fflush(KWMBorder.MHandle);
    }

    signal(Signum, SIG_DFL);
    kill(getpid(), Signum);
}

void Fatal(const std::string &Err)
{
    std::cout << Err << std::endl;
    exit(1);
}

int main(int argc, char **argv)
{
    if(CheckArguments(argc, argv))
        return 0;

    KwmInit();
    CGEventMask EventMask;
    EventMask = ((1 << kCGEventKeyDown) |
                 (1 << kCGEventKeyUp) |
                 (1 << kCGEventMouseMoved) |
                 (1 << kCGEventLeftMouseDown) |
                 (1 << kCGEventLeftMouseUp));

    EventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, EventMask, CGEventCallback, NULL);
    if(!EventTap || !CGEventTapIsEnabled(EventTap))
        Fatal("ERROR: Could not create event-tap!");

    CFRunLoopSourceRef RunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, EventTap, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), RunLoopSource, kCFRunLoopCommonModes);
    CGEventTapEnable(EventTap, true);
    NSApplicationLoad();
    CFRunLoopRun();

    return 0;
}
<|endoftext|>


<commit_before>#include "catch.hpp"
#include <iostream>
#include <string>

#include "audio/Sound.h"

const std::string sound_file = "sound.wav";

TEST_CASE("Initial value(s) in Sound", "[Sound]")
{
	Sound* sound = new Sound();

	// volume should be at max
	CHECK(sound->get_volume() == 100);

	delete sound;
}

TEST_CASE("Opening sound chunk", "[Sound]")
{
	REQUIRE(SDL_Init(SDL_INIT_EVERYTHING) == 0);
	Sound* sound = new Sound();
	REQUIRE(sound != nullptr);

	SECTION("once w/o close")
	{
		sound->open(sound_file);
		CHECK(sound->get_sound() != nullptr);
	}
	SECTION("once")
	{
		sound->open(sound_file);
		REQUIRE(sound->get_sound() != nullptr);

		sound->close();
		CHECK(sound->get_sound() == nullptr);
	}
	SECTION("twice")
	{
		for(unsigned int i = 0; i < 2; i++)
		{
			sound->open(sound_file);
			REQUIRE(sound->get_sound() != nullptr);

			sound->close();
			CHECK(sound->get_sound() == nullptr);
		}
	}
	SECTION("thrice")
	{
		for(unsigned int i = 0; i < 3; i++)
		{
			sound->open(sound_file);
			REQUIRE(sound->get_sound() != nullptr);

			sound->close();
			CHECK(sound->get_sound() == nullptr);
		}
	}

	delete sound;
	SDL_Quit();
}
<commit_msg>Make SDL_Init result visible.<commit_after>#include "catch.hpp"
#include <iostream>
#include <string>

#include "audio/Sound.h"

const std::string sound_file = "sound.wav";

TEST_CASE("Initial value(s) in Sound", "[Sound]")
{
	Sound* sound = new Sound();

	// volume should be at max
	CHECK(sound->get_volume() == 100);

	delete sound;
}

TEST_CASE("Opening sound chunk", "[Sound]")
{
	const int SDL_Init_result = SDL_Init(SDL_INIT_EVERYTHING);
	REQUIRE(SDL_Init_result == 0);
	Sound* sound = new Sound();
	REQUIRE(sound != nullptr);

	SECTION("once w/o close")
	{
		sound->open(sound_file);
		CHECK(sound->get_sound() != nullptr);
	}
	SECTION("once")
	{
		sound->open(sound_file);
		REQUIRE(sound->get_sound() != nullptr);

		sound->close();
		CHECK(sound->get_sound() == nullptr);
	}
	SECTION("twice")
	{
		for(unsigned int i = 0; i < 2; i++)
		{
			sound->open(sound_file);
			REQUIRE(sound->get_sound() != nullptr);

			sound->close();
			CHECK(sound->get_sound() == nullptr);
		}
	}
	SECTION("thrice")
	{
		for(unsigned int i = 0; i < 3; i++)
		{
			sound->open(sound_file);
			REQUIRE(sound->get_sound() != nullptr);

			sound->close();
			CHECK(sound->get_sound() == nullptr);
		}
	}

	delete sound;
	SDL_Quit();
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////
// Copyright (c) 2012 Polytech Montpellier
// 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 copyright holder 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 "Badger.hpp"

#include "ConsoleViewBadger.hpp"

#include <cstdlib>
#include <iostream>
#include <sstream>

namespace badger
{

    namespace
    {
        void sendCommand(plt::Console &c, const std::string &command)
        {
            for(auto it = command.begin(); it!=command.end(); ++it)
                c.sendChar(*it);

            c.sendChar('\n');
        }
    }

    Badger::Badger
    (
    ) :
    m_continuer(true),
    m_logged(false)
    {
        // Important : A console need a view, otherwise seg fault :)
        m_console.setView( std::make_shared<ConsoleViewBadger>() );

        m_console.enable(true);

        std::function<void(const std::string&)> openFunction = std::bind(&Badger::open, this, std::placeholders::_1);
        addCommand("open", openFunction, false);

        std::function<void()> closeFunction = std::bind(&Badger::close, this);
        addCommand("close", closeFunction, false);

        std::function<std::string(const std::string&)> loginFunction = std::bind(&Badger::login, this, std::placeholders::_1);
        addCommand("login", loginFunction, false);

        std::function<std::string()> logoutFunction = std::bind(&Badger::logout, this);
        addCommand("logout", logoutFunction, false);

        std::function<std::string()> nextFunction = std::bind(&Badger::next, this);
        addCommand("next", nextFunction, true);

        std::function<std::string()> rollbackFunction = std::bind(&Badger::rollback, this);
        addCommand("rollback", rollbackFunction, true);

        std::function<std::string()> eraseFunction = std::bind(&Badger::erase, this);
        addCommand("erase", eraseFunction, true);

        std::function<std::string()> countFunction = std::bind(&Badger::count, this);
        addCommand("count", countFunction, false);

        std::function<std::string(const Date&, const Time&, const Time&)> getFunction = std::bind(&Badger::get, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
        addCommand("get", getFunction, true);

        std::function<void()> quitFunction = std::bind(&Badger::quit, this);
        addCommand("quit", quitFunction, false);

        std::cout << "==================================" << std::endl;
        std::cout << "    Welcome to badger program    " << std::endl;
        std::cout << std::endl;
        std::cout << "To get available options, type '?'" << std::endl;
        std::cout << "==================================" << std::endl;
    }

    Badger::~Badger
    (
    )
    {
        close();
    }

    void Badger::open
    (
        const std::string &port
    )
    {
        m_serial.open(port);
    }

    void Badger::close
    (
    )
    {
        if(m_logged)
            sendCommand(m_console, "logout");

        m_serial.close();
    }

    std::string Badger::login
    (
        const std::string &password
    )
    {
        std::vector<serial::byte> command;
        command.push_back('L');

        for(unsigned int i(0); i<password.size(); ++i)
            command.push_back(password[i]);

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        m_logged = (result == "A") ? true : false;

        return (result == "A") ? "You are logged now" : "Error during login";
    }

    std::string Badger::logout
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('O');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        if(result == "A")
            m_logged = false;

        return (result == "A") ? "" : "Error during logout";
    }

    std::string Badger::next
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('G');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        return (result == "E") ? "Database is empty or finish (try rollback maybe)" : result.erase(0,1);
    }

    std::string Badger::rollback
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('R');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        return (result == "A") ? "" : "Rollback impossible";
    }

    std::string Badger::erase
    (
    )
    {
        std::string answer;
        std::string result;

        std::cout << "Do you really want to erase all database (yes)? ";
        std::getline(std::cin, answer);

        if(answer == "yes")
        {
            std::cout << "Information: erase duration < 60 seconds" << std::endl;

            std::vector<serial::byte> command;
            command.push_back('E');

            sendCommandOnSerial(command);

            result = getReturnCommand();
        }

        return (result == "A") ? "" : "Erase impossible";

    }

    std::string Badger::count
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('N');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();
        result.erase(result.begin());

        return result;
    }

    std::string Badger::get
    (
        const Date &date,
        const Time &begin,
        const Time &end
    )
    {
        unsigned int numberOfRecords;

        std::string countStr = count();
        std::istringstream iss(countStr);

        if(!(iss >> numberOfRecords))
            throw std::runtime_error("Unable to parse number of records");

        rollback();

        for(unsigned int i(0); i<numberOfRecords; ++i)
        {
            Record rcd = parseRecord(next());

            if(rcd.date == date && rcd.time >= begin && rcd.time <= end)
                std::cout << rcd.date << ' ' << rcd.time << ' ' << rcd.data << std::endl;
        }

        return "";
    }

    void Badger::quit
    (
    )
    {
        m_continuer = false;
    }

    void Badger::needLogin
    (
        const std::string &command
    )
    {
        bool continueToLogin = true;

        std::string nameFunction;
        std::istringstream iss(command);

        iss >> nameFunction;

        auto it = m_access.find(nameFunction);

        // Don't change order of this if :)
        if(it != m_access.end() && m_access[nameFunction] && !m_logged)
        {
            std::cout << "Information: You must be logged to use the function: " << nameFunction << std::endl;

            while(continueToLogin)
            {
                std::string password;

                std::cout << "Password : ";
                std::getline(std::cin, password);

                login(password);

                if(m_logged)
                {
                    sendCommand(m_console, command);
                    continueToLogin = false;
                }

                else
                {
                    std::string answer;

                    std::cout << "Bad password! Retry (yes)?: ";
                    std::getline(std::cin, answer);

                    if(answer != "yes")
                        continueToLogin = false;
                }
            }
        }

        else
            sendCommand(m_console, command);
    }

    std::string Badger::getReturnCommand
    (
    )
    {
        std::vector<serial::byte> result;
        m_serial.readUntil(result, '\r');

        if(result.front() != serial::byte(2) || result.back() != serial::byte(13))
            throw std::runtime_error("The return command don't begin by STX or don't end by CR");

        result.erase( result.begin() );
        result.erase( --result.end() );
        result.push_back('\0');

        return std::string(&result[0]);
    }

    void Badger::sendCommandOnSerial
    (
        const std::vector<serial::byte> &command
    )
    {
        std::vector<serial::byte> tmp = command;

        // Add STX to begin
        tmp.insert(tmp.begin(), 2);

        // Add CR to end
        tmp.push_back(13);

        m_serial.write(&tmp[0], tmp.size());
    }

    Badger::Record Badger::parseRecord
    (
        const std::string &str
    ) const
    {

        std::string day(str, 0, 2);
        std::string month(str, 2, 2);
        std::string year(str, 4, 4);

        std::string hour(str, 8, 2);
        std::string minute(str, 10, 2);
        std::string seconde(str, 12, 2);

        Record rcd;

        rcd.date = Date(std::atoi(&day[0]), std::atoi(&month[0]), std::atoi(&year[0]));
        rcd.time = Time(std::atoi(&hour[0]), std::atoi(&minute[0]), std::atoi(&seconde[0]));
        rcd.data = std::string(str, 18);

        return rcd;
    }

    void Badger::run
    (
    )
    {
        while(m_continuer)
        {
            std::string command;

            std::cout << "> ";
            std::getline(std::cin, command);

            needLogin(command);
        }
    }

} // namespace badger

<commit_msg>La fonction close de l'interpréteur fonctionne désormais correctement<commit_after>////////////////////////////////////////////////////////////
// Copyright (c) 2012 Polytech Montpellier
// 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 copyright holder 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 "Badger.hpp"

#include "ConsoleViewBadger.hpp"

#include <cstdlib>
#include <iostream>
#include <sstream>

namespace badger
{

    namespace
    {
        void sendCommand(plt::Console &c, const std::string &command)
        {
            for(auto it = command.begin(); it!=command.end(); ++it)
                c.sendChar(*it);

            c.sendChar('\n');
        }
    }

    Badger::Badger
    (
    ) :
    m_continuer(true),
    m_logged(false)
    {
        // Important : A console need a view, otherwise seg fault :)
        m_console.setView( std::make_shared<ConsoleViewBadger>() );

        m_console.enable(true);

        std::function<void(const std::string&)> openFunction = std::bind(&Badger::open, this, std::placeholders::_1);
        addCommand("open", openFunction, false);

        std::function<void()> closeFunction = std::bind(&Badger::close, this);
        addCommand("close", closeFunction, false);

        std::function<std::string(const std::string&)> loginFunction = std::bind(&Badger::login, this, std::placeholders::_1);
        addCommand("login", loginFunction, false);

        std::function<std::string()> logoutFunction = std::bind(&Badger::logout, this);
        addCommand("logout", logoutFunction, false);

        std::function<std::string()> nextFunction = std::bind(&Badger::next, this);
        addCommand("next", nextFunction, true);

        std::function<std::string()> rollbackFunction = std::bind(&Badger::rollback, this);
        addCommand("rollback", rollbackFunction, true);

        std::function<std::string()> eraseFunction = std::bind(&Badger::erase, this);
        addCommand("erase", eraseFunction, true);

        std::function<std::string()> countFunction = std::bind(&Badger::count, this);
        addCommand("count", countFunction, false);

        std::function<std::string(const Date&, const Time&, const Time&)> getFunction = std::bind(&Badger::get, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
        addCommand("get", getFunction, true);

        std::function<void()> quitFunction = std::bind(&Badger::quit, this);
        addCommand("quit", quitFunction, false);

        std::cout << "==================================" << std::endl;
        std::cout << "    Welcome to badger program    " << std::endl;
        std::cout << std::endl;
        std::cout << "To get available options, type '?'" << std::endl;
        std::cout << "==================================" << std::endl;
    }

    Badger::~Badger
    (
    )
    {
        close();
    }

    void Badger::open
    (
        const std::string &port
    )
    {
        m_serial.open(port);
    }

    void Badger::close
    (
    )
    {
        if(m_serial.isOpen())
        {
            logout();

            m_serial.close();
        }
    }

    std::string Badger::login
    (
        const std::string &password
    )
    {
        std::vector<serial::byte> command;
        command.push_back('L');

        for(unsigned int i(0); i<password.size(); ++i)
            command.push_back(password[i]);

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        m_logged = (result == "A") ? true : false;

        return (result == "A") ? "You are logged now" : "Error during login";
    }

    std::string Badger::logout
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('O');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        if(result == "A")
            m_logged = false;

        return (result == "A") ? "" : "Error during logout";
    }

    std::string Badger::next
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('G');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        return (result == "E") ? "Database is empty or finish (try rollback maybe)" : result.erase(0,1);
    }

    std::string Badger::rollback
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('R');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();

        return (result == "A") ? "" : "Rollback impossible";
    }

    std::string Badger::erase
    (
    )
    {
        std::string answer;
        std::string result;

        std::cout << "Do you really want to erase all database (yes)? ";
        std::getline(std::cin, answer);

        if(answer == "yes")
        {
            std::cout << "Information: erase duration < 60 seconds" << std::endl;

            std::vector<serial::byte> command;
            command.push_back('E');

            sendCommandOnSerial(command);

            result = getReturnCommand();
        }

        return (result == "A") ? "" : "Erase impossible";

    }

    std::string Badger::count
    (
    )
    {
        std::vector<serial::byte> command;
        command.push_back('N');

        sendCommandOnSerial(command);
        std::string result = getReturnCommand();
        result.erase(result.begin());

        return result;
    }

    std::string Badger::get
    (
        const Date &date,
        const Time &begin,
        const Time &end
    )
    {
        unsigned int numberOfRecords;

        std::string countStr = count();
        std::istringstream iss(countStr);

        if(!(iss >> numberOfRecords))
            throw std::runtime_error("Unable to parse number of records");

        rollback();

        for(unsigned int i(0); i<numberOfRecords; ++i)
        {
            Record rcd = parseRecord(next());

            if(rcd.date == date && rcd.time >= begin && rcd.time <= end)
                std::cout << rcd.date << ' ' << rcd.time << ' ' << rcd.data << std::endl;
        }

        return "";
    }

    void Badger::quit
    (
    )
    {
        m_continuer = false;
    }

    void Badger::needLogin
    (
        const std::string &command
    )
    {
        bool continueToLogin = true;

        std::string nameFunction;
        std::istringstream iss(command);

        iss >> nameFunction;

        auto it = m_access.find(nameFunction);

        // Don't change order of this if :)
        if(it != m_access.end() && m_access[nameFunction] && !m_logged)
        {
            std::cout << "Information: You must be logged to use the function: " << nameFunction << std::endl;

            while(continueToLogin)
            {
                std::string password;

                std::cout << "Password : ";
                std::getline(std::cin, password);

                login(password);

                if(m_logged)
                {
                    sendCommand(m_console, command);
                    continueToLogin = false;
                }

                else
                {
                    std::string answer;

                    std::cout << "Bad password! Retry (yes)?: ";
                    std::getline(std::cin, answer);

                    if(answer != "yes")
                        continueToLogin = false;
                }
            }
        }

        else
            sendCommand(m_console, command);
    }

    std::string Badger::getReturnCommand
    (
    )
    {
        std::vector<serial::byte> result;
        m_serial.readUntil(result, '\r');

        if(result.front() != serial::byte(2) || result.back() != serial::byte(13))
            throw std::runtime_error("The return command don't begin by STX or don't end by CR");

        result.erase( result.begin() );
        result.erase( --result.end() );
        result.push_back('\0');

        return std::string(&result[0]);
    }

    void Badger::sendCommandOnSerial
    (
        const std::vector<serial::byte> &command
    )
    {
        std::vector<serial::byte> tmp = command;

        // Add STX to begin
        tmp.insert(tmp.begin(), 2);

        // Add CR to end
        tmp.push_back(13);

        m_serial.write(&tmp[0], tmp.size());
    }

    Badger::Record Badger::parseRecord
    (
        const std::string &str
    ) const
    {

        std::string day(str, 0, 2);
        std::string month(str, 2, 2);
        std::string year(str, 4, 4);

        std::string hour(str, 8, 2);
        std::string minute(str, 10, 2);
        std::string seconde(str, 12, 2);

        Record rcd;

        rcd.date = Date(std::atoi(&day[0]), std::atoi(&month[0]), std::atoi(&year[0]));
        rcd.time = Time(std::atoi(&hour[0]), std::atoi(&minute[0]), std::atoi(&seconde[0]));
        rcd.data = std::string(str, 18);

        return rcd;
    }

    void Badger::run
    (
    )
    {
        while(m_continuer)
        {
            std::string command;

            std::cout << "> ";
            std::getline(std::cin, command);

            needLogin(command);
        }
    }

} // namespace badger

<|endoftext|>