<commit_before>/*
 * Copyright (C) 2018 ScyllaDB
 */

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

#include "auth/passwords.hh"

#include <cerrno>
#include <optional>
#include <random>

extern "C" {
#include <crypt.h>
#include <unistd.h>
}

// TODO: blowfish
// Origin uses Java bcrypt library, i.e. blowfish salt
// generation and hashing, which is arguably a "better"
// password hash than sha/md5 versions usually available in
// crypt_r. Otoh, glibc 2.7+ uses a modified sha512 algo
// which should be the same order of safe, so the only
// real issue should be salted hash compatibility with
// origin if importing system tables from there.
//
// Since bcrypt/blowfish is _not_ (afaict) not available
// as a dev package/lib on most linux distros, we'd have to
// copy and compile for example OWL  crypto
// (http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/glibc/crypt_blowfish/)
// to be fully bit-compatible.
//
// Until we decide this is needed, let's just use crypt_r,
// and some old-fashioned random salt generation.

namespace auth::passwords {

static constexpr size_t rand_bytes = 16;
static thread_local crypt_data tlcrypt = { 0, };

no_supported_schemes::no_supported_schemes()
        : std::runtime_error("No allowed hashing schemes are supported on this system") {
}

static sstring hash(const sstring& pass, const sstring& salt) {
    auto res = crypt_r(pass.c_str(), salt.c_str(), &tlcrypt);
    if (!res || (res[0] == '*')) {
        throw std::system_error(errno, std::system_category());
    }
    return res;
}

static const char* prefix_for_scheme(scheme c) noexcept {
    switch (c) {
    case scheme::bcrypt_y: return "$2y$";
    case scheme::bcrypt_a: return "$2a$";
    case scheme::sha_512: return "$6$";
    case scheme::sha_256: return "$5$";
    case scheme::md5: return "$1$";
    default: return nullptr;
    }
}

///
/// Test each allowed hashing scheme and report the best supported one on the current system.
///
/// \throws \ref no_supported_schemes when none of the known schemes is supported.
///
static scheme identify_best_supported_scheme() {
    const auto all_schemes = { scheme::bcrypt_y, scheme::bcrypt_a, scheme::sha_512, scheme::sha_256, scheme::md5 };
    // "Random", for testing schemes.
    const sstring random_part_of_salt = "aaaabbbbccccdddd";

    for (scheme c : all_schemes) {
        const sstring salt = sstring(prefix_for_scheme(c)) + random_part_of_salt;
        const char* e = crypt_r("fisk", salt.c_str(), &tlcrypt);

        if (e && (e[0] != '*')) {
            return c;
        }
    }

    throw no_supported_schemes();
}

bool check(const sstring& pass, const sstring& salted_hash) {
    auto tmp = hash(pass, salted_hash);
    return tmp == salted_hash;
}

///
/// Generate a implementation-specific salt string for hashing passwords.
///
/// The \ref std::default_random_engine is used to generate the string, which is an implementation-specific length.
///
/// \throws \ref no_supported_schemes when no known hashing schemes are supported on the system.
///
static sstring generate_salt() {
    static thread_local std::random_device rd{};
    static const scheme scheme = identify_best_supported_scheme();
    static const sstring prefix = sstring(prefix_for_scheme(scheme));

    std::default_random_engine e1{rd()};
    std::uniform_int_distribution<char> dist;

    sstring valid_salt = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
    sstring input(rand_bytes, 0);

    for (char&c : input) {
        c = valid_salt[dist(e1) % valid_salt.size()];
    }

    return prefix + input;
}

sstring hash(const sstring& pass) {
    return hash(pass, generate_salt());
}

}
<commit_msg>auth: Extract random byte generation for salt<commit_after>/*
 * Copyright (C) 2018 ScyllaDB
 */

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

#include "auth/passwords.hh"

#include <cerrno>
#include <optional>
#include <random>

extern "C" {
#include <crypt.h>
#include <unistd.h>
}

// TODO: blowfish
// Origin uses Java bcrypt library, i.e. blowfish salt
// generation and hashing, which is arguably a "better"
// password hash than sha/md5 versions usually available in
// crypt_r. Otoh, glibc 2.7+ uses a modified sha512 algo
// which should be the same order of safe, so the only
// real issue should be salted hash compatibility with
// origin if importing system tables from there.
//
// Since bcrypt/blowfish is _not_ (afaict) not available
// as a dev package/lib on most linux distros, we'd have to
// copy and compile for example OWL  crypto
// (http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/glibc/crypt_blowfish/)
// to be fully bit-compatible.
//
// Until we decide this is needed, let's just use crypt_r,
// and some old-fashioned random salt generation.

namespace auth::passwords {

static thread_local crypt_data tlcrypt = { 0, };

no_supported_schemes::no_supported_schemes()
        : std::runtime_error("No allowed hashing schemes are supported on this system") {
}

static sstring hash(const sstring& pass, const sstring& salt) {
    auto res = crypt_r(pass.c_str(), salt.c_str(), &tlcrypt);
    if (!res || (res[0] == '*')) {
        throw std::system_error(errno, std::system_category());
    }
    return res;
}

static const char* prefix_for_scheme(scheme c) noexcept {
    switch (c) {
    case scheme::bcrypt_y: return "$2y$";
    case scheme::bcrypt_a: return "$2a$";
    case scheme::sha_512: return "$6$";
    case scheme::sha_256: return "$5$";
    case scheme::md5: return "$1$";
    default: return nullptr;
    }
}

///
/// Test each allowed hashing scheme and report the best supported one on the current system.
///
/// \throws \ref no_supported_schemes when none of the known schemes is supported.
///
static scheme identify_best_supported_scheme() {
    const auto all_schemes = { scheme::bcrypt_y, scheme::bcrypt_a, scheme::sha_512, scheme::sha_256, scheme::md5 };
    // "Random", for testing schemes.
    const sstring random_part_of_salt = "aaaabbbbccccdddd";

    for (scheme c : all_schemes) {
        const sstring salt = sstring(prefix_for_scheme(c)) + random_part_of_salt;
        const char* e = crypt_r("fisk", salt.c_str(), &tlcrypt);

        if (e && (e[0] != '*')) {
            return c;
        }
    }

    throw no_supported_schemes();
}

static sstring generate_random_salt_bytes() {
    static thread_local std::random_device rd{};
    static const sstring valid_bytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
    static constexpr std::size_t num_bytes = 16;
    std::default_random_engine rng{rd()};
    std::uniform_int_distribution<char> dist;
    sstring result(num_bytes, 0);

    for (char& c : result) {
        c = valid_bytes[dist(rng) % valid_bytes.size()];
    }

    return result;
}

bool check(const sstring& pass, const sstring& salted_hash) {
    auto tmp = hash(pass, salted_hash);
    return tmp == salted_hash;
}

///
/// Generate a implementation-specific salt string for hashing passwords.
///
/// The \ref std::default_random_engine is used to generate the string, which is an implementation-specific length.
///
/// \throws \ref no_supported_schemes when no known hashing schemes are supported on the system.
///
static sstring generate_salt() {
    static const scheme scheme = identify_best_supported_scheme();
    static const sstring prefix = sstring(prefix_for_scheme(scheme));
    return prefix + generate_random_salt_bytes();
}

sstring hash(const sstring& pass) {
    return hash(pass, generate_salt());
}

}
<|endoftext|>


<commit_before>/*
 * measure_tool.cpp
 *
 *  Created on: Aug 8, 2012
 *      Author: gossow
 */

#include "measure_tool.h"

#include "rviz/ogre_helpers/shape.h"
#include "rviz/viewport_mouse_event.h"
#include "rviz/display_context.h"
#include "rviz/selection/selection_manager.h"

#include <OgreSceneNode.h>

namespace rviz
{

MeasureTool::MeasureTool()
{
}

MeasureTool::~MeasureTool()
{
}

void MeasureTool::onInitialize()
{
  sphere_ = new Shape(Shape::Sphere,
      scene_manager_, 0 );
  sphere_->setColor( 1,1,1,0.3 );
  const float s = 0.1;
  sphere_->setScale( Ogre::Vector3(s) );
  sphere_->getRootNode()->setVisible(false);
}

void MeasureTool::activate()
{
  setStatus( "Click on two points to measure their distance." );
}

void MeasureTool::deactivate()
{
  sphere_->getRootNode()->setVisible(false);
}

int MeasureTool::processMouseEvent( ViewportMouseEvent& event )
{
  int flags = 0;

  Ogre::Vector3 pos;

  bool success = context_->getSelectionManager()->get3DPoint( event.viewport, event.x, event.y, pos );
  sphere_->getRootNode()->setVisible(success);

  if ( !success )
  {
    return flags;
  }

  sphere_->setPosition( pos );

  if( event.leftUp() )
  {
    //flags |= (Finished|Render);
  }

  return flags;
}

} /* namespace rviz */

#include <pluginlib/class_list_macros.h>
PLUGINLIB_DECLARE_CLASS( rviz, Measure, rviz::MeasureTool, rviz::Tool )
<commit_msg>small change to measure tool. Will move this thing somewhere else next, as it is only partly implemented.<commit_after>/*
 * measure_tool.cpp
 *
 *  Created on: Aug 8, 2012
 *      Author: gossow
 */

#include "measure_tool.h"

#include "rviz/ogre_helpers/shape.h"
#include "rviz/viewport_mouse_event.h"
#include "rviz/display_context.h"
#include "rviz/selection/selection_manager.h"

#include <OgreSceneNode.h>

namespace rviz
{

MeasureTool::MeasureTool()
{
}

MeasureTool::~MeasureTool()
{
}

void MeasureTool::onInitialize()
{
  sphere_ = new Shape(Shape::Sphere,
      scene_manager_, 0 );
  sphere_->setColor( 1,1,0,1.0 );
  const float s = 0.1;
  sphere_->setScale( Ogre::Vector3(s) );
  sphere_->getRootNode()->setVisible(true);
}

void MeasureTool::activate()
{
  sphere_->getRootNode()->setVisible(true);
}

void MeasureTool::deactivate()
{
  sphere_->getRootNode()->setVisible(false);
}

int MeasureTool::processMouseEvent( ViewportMouseEvent& event )
{
  int flags = 0;

  Ogre::Vector3 pos;

  setStatus( "Click on two points to measure their distance." );

  if( event.leftUp() )
  {
    bool success = context_->getSelectionManager()->get3DPoint( event.viewport, event.x, event.y, pos );
    sphere_->getRootNode()->setVisible(success);

    if ( !success )
    {
      return flags;
    }

    sphere_->setPosition( pos );
    flags |= Render;
  }

  return flags;
}

} /* namespace rviz */

#include <pluginlib/class_list_macros.h>
PLUGINLIB_DECLARE_CLASS( rviz, Measure, rviz::MeasureTool, rviz::Tool )
<|endoftext|>


<commit_before>#include "SqliteConnector.h"
#include <CDebug.h>

namespace metacpp
{
namespace sql
{
namespace connectors
{
namespace sqlite
{

SqliteConnector::SqliteConnector(const String &databaseName)
    : m_databaseName(databaseName), m_dbHandle(nullptr)
{

}

SqliteConnector::~SqliteConnector()
{
    if (m_dbHandle)
        disconnect();
}

bool SqliteConnector::connect()
{
    if (m_dbHandle)
    {
        cwarning() << "SqliteConnector::connect(): database connection seems to be already opened";
        return true;
    }
    int error = sqlite3_open_v2(m_databaseName.c_str(), &m_dbHandle,
                             SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
    if (SQLITE_OK != error)
    {
        cerror() << "sqlite3_open_v2(): " << describeSqliteError(error);
        return false;
    }
    return true;
}

bool SqliteConnector::disconnect()
{
    if (!m_dbHandle)
    {
        cwarning() << "SqliteConnector::disconnect(): database connection was not previously successfully created";
        return true;
    }
    int error = sqlite3_close(m_dbHandle);
    if (SQLITE_OK != error)
    {
        cerror() << "sqlite3_close(): " << describeSqliteError(error);
        return false;
    }

    {
        std::lock_guard<std::mutex> _guard(m_transactionMutex);
        if (m_transactions.size())
            cwarning() << "SqliteConnector::disconnect(): there is still non-closed transaction connections left";
    }

    return true;
}

SqlTransactionImpl *SqliteConnector::beginTransaction()
{
    int error;
    sqlite3 *dbHandle;
    error = sqlite3_open_v2(m_databaseName.c_str(), &dbHandle,
                             SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
    if (SQLITE_OK != error)
    {
        cfatal() << "sqlite3_open_v2(): " << describeSqliteError(error);
        return nullptr;
    }
    char *errorMessage;
    error = sqlite3_exec(dbHandle, "BEGIN TRANSACTION", nullptr, nullptr, &errorMessage);
    if (SQLITE_OK != error)
    {
        cfatal() << "Failed to start transaction with sqlite3_exec(): " << errorMessage;
        return nullptr;
    }
    SqliteTransactionImpl *result = new SqliteTransactionImpl(dbHandle);
    {
        std::lock_guard<std::mutex> _guard(m_transactionMutex);
        m_transactions.push_back(result);
    }
    return result;
}

bool SqliteConnector::commitTransaction(SqlTransactionImpl *transaction)
{
    return closeTransaction(transaction, "COMMIT TRANSACTION");
}

bool SqliteConnector::rollbackTransaction(SqlTransactionImpl *transaction)
{
    return closeTransaction(transaction, "ROLLBACK TRANSACTION");
}

SqlSyntax SqliteConnector::sqlSyntax() const
{
    return SqlSyntaxSqlite;
}

bool SqliteConnector::closeTransaction(SqlTransactionImpl *transaction, const char *closeStmt)
{
    SqliteTransactionImpl *sqliteTransaction = reinterpret_cast<SqliteTransactionImpl *>(transaction);
    char *errorMessage;
    int error;
    error = sqlite3_exec(sqliteTransaction->dbHandle(), closeStmt, nullptr, nullptr, &errorMessage);
    if (SQLITE_OK != error)
    {
        cfatal() << "Failed to commit transaction: " << errorMessage;
        return false;
    }
    {
        std::lock_guard<std::mutex> _guard(m_transactionMutex);
        auto it = std::find(m_transactions.begin(), m_transactions.end(), sqliteTransaction);
        if (it != m_transactions.end())
        {
            m_transactions.erase(it);
            delete transaction;
            return true;
        }
        cerror() << "SqliteConnector::commitTransaction(): no such transaction";
        return false;
    }
}

const char *describeSqliteError(int errorCode)
{
    return sqlite3_errstr(errorCode);
    //switch (errorCode)
    //{
    //case SQLITE_OK:
    //    return "Successful result";
    //case SQLITE_ERROR:
    //    return "SQL error or missing database";
    //case SQLITE_INTERNAL:
    //    return "Internal logic error in SQLite";
    //case SQLITE_PERM:
    //    return "Access permission denied";
    //case SQLITE_ABORT:
    //    return "Callback routine requested an abort";
    //case SQLITE_BUSY:
    //    return "The database file is locked";
    //case SQLITE_LOCKED:
    //    return "A table in the database is locked";
    //case SQLITE_NOMEM:
    //    return "A malloc() failed";
    //case SQLITE_READONLY:
    //    return "Attempt to write a readonly database";
    //case SQLITE_INTERRUPT:
    //    return "Operation terminated by sqlite3_interrupt()";
    //case SQLITE_IOERR:
    //    return "Some kind of disk I/O error occurred";
    //case SQLITE_CORRUPT:
    //    return "The database disk image is malformed";
    //case SQLITE_NOTFOUND:
    //    return "Unknown opcode in sqlite3_file_control()";
    //case SQLITE_FULL:
    //    return "Insertion failed because database is full";
    //case SQLITE_CANTOPEN:
    //    return "Unable to open the database file";
    //case SQLITE_PROTOCOL:
    //    return "Database lock protocol error";
    //case SQLITE_EMPTY:
    //    return "Database is empty";
    //case SQLITE_SCHEMA:
    //    return "The database schema changed";
    //case SQLITE_TOOBIG:
    //    return "String or BLOB exceeds size limit";
    //case SQLITE_CONSTRAINT:
    //    return "Abort due to constraint violation";
    //case SQLITE_MISMATCH:
    //    return "Data type mismatch";
    //case SQLITE_MISUSE:
    //    return "Library used incorrectly";
    //case SQLITE_NOLFS:
    //    return "Uses OS features not supported on host";
    //case SQLITE_AUTH:
    //    return "Authorization denied";
    //case SQLITE_FORMAT:
    //    return "Auxiliary database format error";
    //case SQLITE_RANGE:
    //    return "2nd parameter to sqlite3_bind out of range";
    //case SQLITE_NOTADB:
    //    return "File opened that is not a database file";
    //case SQLITE_NOTICE:
    //    return "Notifications from sqlite3_log()";
    //case SQLITE_WARNING:
    //    return "Warnings from sqlite3_log()";
    //case SQLITE_ROW:
    //    return "sqlite3_step() has another row ready";
    //case SQLITE_DONE:
    //    return "sqlite3_step() has finished executing";
    //default:
    //    return "Unknown error";
    //}
}

} // namespace sqlite
} // namespace metacpp
} // namespace sql
} // namespace connectors
<commit_msg>sqlite_errstr<commit_after>#include "SqliteConnector.h"
#include <CDebug.h>

namespace metacpp
{
namespace sql
{
namespace connectors
{
namespace sqlite
{

SqliteConnector::SqliteConnector(const String &databaseName)
    : m_databaseName(databaseName), m_dbHandle(nullptr)
{

}

SqliteConnector::~SqliteConnector()
{
    if (m_dbHandle)
        disconnect();
}

bool SqliteConnector::connect()
{
    if (m_dbHandle)
    {
        cwarning() << "SqliteConnector::connect(): database connection seems to be already opened";
        return true;
    }
    int error = sqlite3_open_v2(m_databaseName.c_str(), &m_dbHandle,
                             SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
    if (SQLITE_OK != error)
    {
        cerror() << "sqlite3_open_v2(): " << describeSqliteError(error);
        return false;
    }
    return true;
}

bool SqliteConnector::disconnect()
{
    if (!m_dbHandle)
    {
        cwarning() << "SqliteConnector::disconnect(): database connection was not previously successfully created";
        return true;
    }
    int error = sqlite3_close(m_dbHandle);
    if (SQLITE_OK != error)
    {
        cerror() << "sqlite3_close(): " << describeSqliteError(error);
        return false;
    }

    {
        std::lock_guard<std::mutex> _guard(m_transactionMutex);
        if (m_transactions.size())
            cwarning() << "SqliteConnector::disconnect(): there is still non-closed transaction connections left";
    }

    return true;
}

SqlTransactionImpl *SqliteConnector::beginTransaction()
{
    int error;
    sqlite3 *dbHandle;
    error = sqlite3_open_v2(m_databaseName.c_str(), &dbHandle,
                             SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
    if (SQLITE_OK != error)
    {
        cfatal() << "sqlite3_open_v2(): " << describeSqliteError(error);
        return nullptr;
    }
    char *errorMessage;
    error = sqlite3_exec(dbHandle, "BEGIN TRANSACTION", nullptr, nullptr, &errorMessage);
    if (SQLITE_OK != error)
    {
        cfatal() << "Failed to start transaction with sqlite3_exec(): " << errorMessage;
        return nullptr;
    }
    SqliteTransactionImpl *result = new SqliteTransactionImpl(dbHandle);
    {
        std::lock_guard<std::mutex> _guard(m_transactionMutex);
        m_transactions.push_back(result);
    }
    return result;
}

bool SqliteConnector::commitTransaction(SqlTransactionImpl *transaction)
{
    return closeTransaction(transaction, "COMMIT TRANSACTION");
}

bool SqliteConnector::rollbackTransaction(SqlTransactionImpl *transaction)
{
    return closeTransaction(transaction, "ROLLBACK TRANSACTION");
}

SqlSyntax SqliteConnector::sqlSyntax() const
{
    return SqlSyntaxSqlite;
}

bool SqliteConnector::closeTransaction(SqlTransactionImpl *transaction, const char *closeStmt)
{
    SqliteTransactionImpl *sqliteTransaction = reinterpret_cast<SqliteTransactionImpl *>(transaction);
    char *errorMessage;
    int error;
    error = sqlite3_exec(sqliteTransaction->dbHandle(), closeStmt, nullptr, nullptr, &errorMessage);
    if (SQLITE_OK != error)
    {
        cfatal() << "Failed to commit transaction: " << errorMessage;
        return false;
    }
    {
        std::lock_guard<std::mutex> _guard(m_transactionMutex);
        auto it = std::find(m_transactions.begin(), m_transactions.end(), sqliteTransaction);
        if (it != m_transactions.end())
        {
            m_transactions.erase(it);
            delete transaction;
            return true;
        }
        cerror() << "SqliteConnector::commitTransaction(): no such transaction";
        return false;
    }
}

const char *describeSqliteError(int errorCode)
{
    //return sqlite3_errstr(errorCode);
    switch (errorCode)
    {
    case SQLITE_OK:
        return "Successful result";
    case SQLITE_ERROR:
        return "SQL error or missing database";
    case SQLITE_INTERNAL:
        return "Internal logic error in SQLite";
    case SQLITE_PERM:
        return "Access permission denied";
    case SQLITE_ABORT:
        return "Callback routine requested an abort";
    case SQLITE_BUSY:
        return "The database file is locked";
    case SQLITE_LOCKED:
        return "A table in the database is locked";
    case SQLITE_NOMEM:
        return "A malloc() failed";
    case SQLITE_READONLY:
        return "Attempt to write a readonly database";
    case SQLITE_INTERRUPT:
        return "Operation terminated by sqlite3_interrupt()";
    case SQLITE_IOERR:
        return "Some kind of disk I/O error occurred";
    case SQLITE_CORRUPT:
        return "The database disk image is malformed";
    case SQLITE_NOTFOUND:
        return "Unknown opcode in sqlite3_file_control()";
    case SQLITE_FULL:
        return "Insertion failed because database is full";
    case SQLITE_CANTOPEN:
        return "Unable to open the database file";
    case SQLITE_PROTOCOL:
        return "Database lock protocol error";
    case SQLITE_EMPTY:
        return "Database is empty";
    case SQLITE_SCHEMA:
        return "The database schema changed";
    case SQLITE_TOOBIG:
        return "String or BLOB exceeds size limit";
    case SQLITE_CONSTRAINT:
        return "Abort due to constraint violation";
    case SQLITE_MISMATCH:
        return "Data type mismatch";
    case SQLITE_MISUSE:
        return "Library used incorrectly";
    case SQLITE_NOLFS:
        return "Uses OS features not supported on host";
    case SQLITE_AUTH:
        return "Authorization denied";
    case SQLITE_FORMAT:
        return "Auxiliary database format error";
    case SQLITE_RANGE:
        return "2nd parameter to sqlite3_bind out of range";
    case SQLITE_NOTADB:
        return "File opened that is not a database file";
    case SQLITE_NOTICE:
        return "Notifications from sqlite3_log()";
    case SQLITE_WARNING:
        return "Warnings from sqlite3_log()";
    case SQLITE_ROW:
        return "sqlite3_step() has another row ready";
    case SQLITE_DONE:
        return "sqlite3_step() has finished executing";
    default:
        return "Unknown error";
    }
}

} // namespace sqlite
} // namespace metacpp
} // namespace sql
} // namespace connectors
<|endoftext|>


<commit_before>#include "halley/tools/assets/import_tool.h"
#include "halley/tools/codegen/codegen.h"
#include <iostream>
#include "halley/tools/project/project.h"
#include "halley/tools/tasks/editor_task_set.h"
#include "halley/tools/assets/check_assets_task.h"
#include <thread>
#include <chrono>
#include "halley/tools/file/filesystem.h"
#include "halley/os/os.h"
#include "halley/core/game/halley_statics.h"
#include "halley/support/logger.h"

using namespace Halley;
using namespace std::chrono_literals;

int ImportTool::run(Vector<std::string> args)
{
	if (args.size() == 2) {
		HalleyStatics statics;
		statics.resume(nullptr);
		StdOutSink logSink;
		Logger::addSink(logSink);

		Path projectPath = FileSystem::getAbsolute(Path(args[0]));
		Path halleyRootPath = FileSystem::getAbsolute(Path(args[1]));

		auto proj = std::make_unique<Project>(statics, "pc", projectPath, halleyRootPath);
		std::cout << "Importing project at " << projectPath << ", with Halley root at " << halleyRootPath << "" << std::endl;

		auto tasks = std::make_unique<EditorTaskSet>();
		tasks->setListener(*this);
		tasks->addTask(EditorTaskAnchor(std::make_unique<CheckAssetsTask>(*proj, true)));
		auto last = std::chrono::steady_clock::now();

		while (tasks->getTasks().size() > 0) {
			std::this_thread::sleep_for(50ms);

			auto now = std::chrono::steady_clock::now();
			float elapsed = std::chrono::duration<float>(now - last).count();
			last = now;

			tasks->update(elapsed);
		}

		if (hasError) {
			std::cout << "Import failed." << std::endl;
			return 1;
		} else {
			std::cout << "Import done." << std::endl;
			return 0;
		}
	}
	else {
		std::cout << "Usage: halley-cmd import projDir halleyDir" << std::endl;
		return 1;
	}
}

void ImportTool::onTaskAdded(const std::shared_ptr<EditorTaskAnchor>&)
{
}

void ImportTool::onTaskTerminated(const std::shared_ptr<EditorTaskAnchor>&)
{
}

void ImportTool::onTaskError(const std::shared_ptr<EditorTaskAnchor>&)
{
	hasError = true;
}
<commit_msg>halley-cmd.exe now takes --platform.<commit_after>#include "halley/tools/assets/import_tool.h"
#include "halley/tools/codegen/codegen.h"
#include <iostream>
#include "halley/tools/project/project.h"
#include "halley/tools/tasks/editor_task_set.h"
#include "halley/tools/assets/check_assets_task.h"
#include <thread>
#include <chrono>
#include "halley/tools/file/filesystem.h"
#include "halley/os/os.h"
#include "halley/core/game/halley_statics.h"
#include "halley/support/logger.h"

using namespace Halley;
using namespace std::chrono_literals;

int ImportTool::run(Vector<std::string> args)
{
	if (args.size() >= 2) {
		HalleyStatics statics;
		statics.resume(nullptr);
		StdOutSink logSink;
		Logger::addSink(logSink);

		Path projectPath = FileSystem::getAbsolute(Path(args[0]));
		Path halleyRootPath = FileSystem::getAbsolute(Path(args[1]));

		String platform = "pc";

		for (size_t i = 2; i < args.size(); ++i) {
			String arg = args[i];
			if (arg.startsWith("--platform=")) {
				platform = arg.mid(11);
			}
		}

		auto proj = std::make_unique<Project>(statics, platform, projectPath, halleyRootPath);
		std::cout << "Importing project at " << projectPath << ", with Halley root at " << halleyRootPath << "" << std::endl;

		auto tasks = std::make_unique<EditorTaskSet>();
		tasks->setListener(*this);
		tasks->addTask(EditorTaskAnchor(std::make_unique<CheckAssetsTask>(*proj, true)));
		auto last = std::chrono::steady_clock::now();

		while (tasks->getTasks().size() > 0) {
			std::this_thread::sleep_for(50ms);

			auto now = std::chrono::steady_clock::now();
			float elapsed = std::chrono::duration<float>(now - last).count();
			last = now;

			tasks->update(elapsed);
		}

		if (hasError) {
			std::cout << "Import failed." << std::endl;
			return 1;
		} else {
			std::cout << "Import done." << std::endl;
			return 0;
		}
	}
	else {
		std::cout << "Usage: halley-cmd import projDir halleyDir" << std::endl;
		return 1;
	}
}

void ImportTool::onTaskAdded(const std::shared_ptr<EditorTaskAnchor>&)
{
}

void ImportTool::onTaskTerminated(const std::shared_ptr<EditorTaskAnchor>&)
{
}

void ImportTool::onTaskError(const std::shared_ptr<EditorTaskAnchor>&)
{
	hasError = true;
}
<|endoftext|>


<commit_before>#pragma once

#include "boost_defs.hpp"

#include "apple_hid_usage_tables.hpp"
#include "userspace_types.hpp"
#include <IOKit/hid/IOHIDDevice.h>
#include <IOKit/hid/IOHIDElement.h>
#include <IOKit/hid/IOHIDQueue.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <IOKit/hid/IOHIDValue.h>
#include <boost/function.hpp>
#include <cstdint>
#include <list>
#include <spdlog/spdlog.h>
#include <unordered_map>

class human_interface_device final {
public:
  typedef boost::function<void(human_interface_device& device,
                               IOHIDValueRef _Nonnull value,
                               IOHIDElementRef _Nonnull element,
                               uint32_t usage_page,
                               uint32_t usage,
                               CFIndex integer_value)>
      value_callback;

  typedef boost::function<void(human_interface_device& device,
                               IOHIDReportType type,
                               uint32_t report_id,
                               uint8_t* _Nullable report,
                               CFIndex report_length)>
      report_callback;

  human_interface_device(spdlog::logger& logger,
                         IOHIDDeviceRef _Nonnull device) : logger_(logger),
                                                           device_(device),
                                                           queue_(nullptr),
                                                           grabbed_(false) {
    CFRetain(device_);

    auto elements = IOHIDDeviceCopyMatchingElements(device_, nullptr, kIOHIDOptionsTypeNone);
    if (elements) {
      for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i) {
        // add to elements_
        auto element = static_cast<IOHIDElementRef>(const_cast<void*>(CFArrayGetValueAtIndex(elements, i)));
        auto usage_page = IOHIDElementGetUsagePage(element);
        auto usage = IOHIDElementGetUsage(element);

        auto key = elements_key(usage_page, usage);
        if (elements_.find(key) == elements_.end()) {
          CFRetain(element);
          elements_[key] = element;
        }
      }
      CFRelease(elements);
    }
  }

  ~human_interface_device(void) {
    // unregister all callbacks.
    unregister_report_callback();
    unregister_value_callback();

    unschedule();

    if (grabbed_) {
      ungrab();
    } else {
      close();
    }

    if (queue_) {
      CFRelease(queue_);
    }

    for (const auto& it : elements_) {
      CFRelease(it.second);
    }
    elements_.clear();

    CFRelease(device_);
  }

  IOReturn open(IOOptionBits options = kIOHIDOptionsTypeNone) {
    return IOHIDDeviceOpen(device_, options);
  }

  IOReturn close(void) {
    return IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
  }

  void schedule(void) {
    IOHIDDeviceScheduleWithRunLoop(device_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    if (queue_) {
      IOHIDQueueScheduleWithRunLoop(queue_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    }
  }

  void unschedule(void) {
    if (queue_) {
      IOHIDQueueUnscheduleFromRunLoop(queue_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    }
    IOHIDDeviceUnscheduleFromRunLoop(device_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
  }

  void register_report_callback(const report_callback& callback,
                                uint8_t* _Nonnull report,
                                CFIndex report_size) {
    report_callback_ = callback;
    IOHIDDeviceRegisterInputReportCallback(device_, report, report_size, static_input_report_callback, this);
  }

  void unregister_report_callback(void) {
    IOHIDDeviceRegisterInputReportCallback(device_, nullptr, 0, nullptr, nullptr);
    report_callback_.clear();
  }

  void register_value_callback(const value_callback& callback) {
    if (!queue_) {
      const CFIndex depth = 1024;
      queue_ = IOHIDQueueCreate(kCFAllocatorDefault, device_, depth, kIOHIDOptionsTypeNone);
      if (!queue_) {
        logger_.error("IOHIDQueueCreate error @ {0}", __PRETTY_FUNCTION__);
        return;
      }

      // add elements into queue_.
      for (const auto& it : elements_) {
        IOHIDQueueAddElement(queue_, it.second);
      }
    }

    value_callback_ = callback;
    IOHIDQueueRegisterValueAvailableCallback(queue_, static_queue_value_available_callback, this);

    IOHIDQueueStart(queue_);
  }

  void unregister_value_callback(void) {
    if (queue_) {
      IOHIDQueueStop(queue_);
      IOHIDQueueRegisterValueAvailableCallback(queue_, nullptr, nullptr);
    }
    value_callback_.clear();
  }

  // high-level utility method
  void grab(const value_callback& callback) {
    if (grabbed_) {
      ungrab();
    }

    IOReturn r = open(kIOHIDOptionsTypeSeizeDevice);
    if (r != kIOReturnSuccess) {
      logger_.error("IOHIDDeviceOpen error: 0x{1:x} @ {0}", __PRETTY_FUNCTION__, r);
      return;
    }

    register_value_callback(callback);

    schedule();

    grabbed_ = true;
  }

  // high-level utility method
  void ungrab(void) {
    if (!grabbed_) {
      return;
    }

    unschedule();

    unregister_value_callback();

    if (queue_) {
      // Remove all elements
      for (const auto& it : elements_) {
        IOHIDQueueRemoveElement(queue_, it.second);
      }

      CFRelease(queue_);
      queue_ = nullptr;
    }

    IOReturn r = close();
    if (r != kIOReturnSuccess) {
      logger_.error("IOHIDDeviceClose error: 0x{1:x} @ {0}", __PRETTY_FUNCTION__, r);
      return;
    }

    grabbed_ = false;
  }

  long get_max_input_report_size(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDMaxInputReportSizeKey), value);
    return value;
  }

  long get_vendor_id(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDVendorIDKey), value);
    return value;
  }

  long get_product_id(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDProductIDKey), value);
    return value;
  }

  long get_location_id(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDLocationIDKey), value);
    return value;
  }

  std::string get_manufacturer(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDManufacturerKey), value);
    return value;
  }

  std::string get_product(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDProductKey), value);
    return value;
  }

  std::string get_serial_number_string(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDSerialNumberKey), value);
    return value;
  }

  std::string get_transport(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDTransportKey), value);
    return value;
  }

  bool get_long_property(const CFStringRef _Nonnull key, long& value) const {
    auto property = IOHIDDeviceGetProperty(device_, key);
    if (!property) {
      return false;
    }

    if (CFNumberGetTypeID() != CFGetTypeID(property)) {
      return false;
    }

    return CFNumberGetValue(static_cast<CFNumberRef>(property), kCFNumberLongType, &value);
  }

  bool get_string_property(const CFStringRef _Nonnull key, std::string& value) const {
    auto property = IOHIDDeviceGetProperty(device_, key);
    if (!property) {
      return false;
    }

    if (CFStringGetTypeID() != CFGetTypeID(property)) {
      return false;
    }

    auto p = CFStringGetCStringPtr(static_cast<CFStringRef>(property), kCFStringEncodingUTF8);
    if (!p) {
      value.clear();
    } else {
      value = p;
    }
    return true;
  }

  std::unordered_map<krbn::key_code, krbn::key_code>& get_simple_changed_keys(void) {
    return simple_changed_keys_;
  }

  std::unordered_map<krbn::key_code, krbn::key_code>& get_fn_changed_keys(void) {
    return fn_changed_keys_;
  }

  size_t get_pressed_keys_count(void) const {
    return pressed_key_usages_.size();
  }

private:
  static void static_queue_value_available_callback(void* _Nullable context, IOReturn result, void* _Nullable sender) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<human_interface_device*>(context);
    if (!self) {
      return;
    }

    auto queue = static_cast<IOHIDQueueRef>(sender);
    if (!queue) {
      return;
    }

    self->queue_value_available_callback(queue);
  }

  void queue_value_available_callback(IOHIDQueueRef _Nonnull queue) {
    while (true) {
      auto value = IOHIDQueueCopyNextValueWithTimeout(queue, 0.);
      if (!value) {
        break;
      }

      auto element = IOHIDValueGetElement(value);
      if (element) {
        auto usage_page = IOHIDElementGetUsagePage(element);
        auto usage = IOHIDElementGetUsage(element);
        auto integer_value = IOHIDValueGetIntegerValue(value);

        // update pressed_key_usages_
        if ((usage_page == kHIDPage_KeyboardOrKeypad) ||
            (usage_page == kHIDPage_AppleVendorTopCase && usage == kHIDUsage_AV_TopCase_KeyboardFn)) {
          bool pressed = integer_value;
          uint64_t u = (static_cast<uint64_t>(usage_page) << 32) | usage;
          if (pressed) {
            pressed_key_usages_.push_back(u);
          } else {
            pressed_key_usages_.remove(u);
          }
        }

        // call value_callback_
        if (value_callback_) {
          value_callback_(*this, value, element, usage_page, usage, integer_value);
        }
      }

      CFRelease(value);
    }
  }

  static void static_input_report_callback(void* _Nullable context,
                                           IOReturn result,
                                           void* _Nullable sender,
                                           IOHIDReportType type,
                                           uint32_t report_id,
                                           uint8_t* _Nullable report,
                                           CFIndex report_length) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<human_interface_device*>(context);
    if (!self) {
      return;
    }

    self->input_report_callback(type, report_id, report, report_length);
  }

  void input_report_callback(IOHIDReportType type,
                             uint32_t report_id,
                             uint8_t* _Nullable report,
                             CFIndex report_length) {
    if (report_callback_) {
      report_callback_(*this, type, report_id, report, report_length);
    }
  }

  uint64_t elements_key(uint32_t usage_page, uint32_t usage) {
    return (static_cast<uint64_t>(usage_page) << 32 | usage);
  }

  spdlog::logger& logger_;

  IOHIDDeviceRef _Nonnull device_;
  IOHIDQueueRef _Nullable queue_;
  std::unordered_map<uint64_t, IOHIDElementRef> elements_;

  bool grabbed_;
  std::list<uint64_t> pressed_key_usages_;
  value_callback value_callback_;
  report_callback report_callback_;

  std::unordered_map<krbn::key_code, krbn::key_code> simple_changed_keys_;
  std::unordered_map<krbn::key_code, krbn::key_code> fn_changed_keys_;
};
<commit_msg>add human_interface_device::get_element<commit_after>#pragma once

#include "boost_defs.hpp"

#include "apple_hid_usage_tables.hpp"
#include "userspace_types.hpp"
#include <IOKit/hid/IOHIDDevice.h>
#include <IOKit/hid/IOHIDElement.h>
#include <IOKit/hid/IOHIDQueue.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <IOKit/hid/IOHIDValue.h>
#include <boost/function.hpp>
#include <cstdint>
#include <list>
#include <spdlog/spdlog.h>
#include <unordered_map>

class human_interface_device final {
public:
  typedef boost::function<void(human_interface_device& device,
                               IOHIDValueRef _Nonnull value,
                               IOHIDElementRef _Nonnull element,
                               uint32_t usage_page,
                               uint32_t usage,
                               CFIndex integer_value)>
      value_callback;

  typedef boost::function<void(human_interface_device& device,
                               IOHIDReportType type,
                               uint32_t report_id,
                               uint8_t* _Nullable report,
                               CFIndex report_length)>
      report_callback;

  human_interface_device(spdlog::logger& logger,
                         IOHIDDeviceRef _Nonnull device) : logger_(logger),
                                                           device_(device),
                                                           queue_(nullptr),
                                                           grabbed_(false) {
    CFRetain(device_);

    auto elements = IOHIDDeviceCopyMatchingElements(device_, nullptr, kIOHIDOptionsTypeNone);
    if (elements) {
      for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i) {
        // add to elements_
        auto element = static_cast<IOHIDElementRef>(const_cast<void*>(CFArrayGetValueAtIndex(elements, i)));
        auto usage_page = IOHIDElementGetUsagePage(element);
        auto usage = IOHIDElementGetUsage(element);

        auto key = elements_key(usage_page, usage);
        if (elements_.find(key) == elements_.end()) {
          CFRetain(element);
          elements_[key] = element;
        }
      }
      CFRelease(elements);
    }
  }

  ~human_interface_device(void) {
    // unregister all callbacks.
    unregister_report_callback();
    unregister_value_callback();

    unschedule();

    if (grabbed_) {
      ungrab();
    } else {
      close();
    }

    if (queue_) {
      CFRelease(queue_);
    }

    for (const auto& it : elements_) {
      CFRelease(it.second);
    }
    elements_.clear();

    CFRelease(device_);
  }

  IOReturn open(IOOptionBits options = kIOHIDOptionsTypeNone) {
    return IOHIDDeviceOpen(device_, options);
  }

  IOReturn close(void) {
    return IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
  }

  void schedule(void) {
    IOHIDDeviceScheduleWithRunLoop(device_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    if (queue_) {
      IOHIDQueueScheduleWithRunLoop(queue_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    }
  }

  void unschedule(void) {
    if (queue_) {
      IOHIDQueueUnscheduleFromRunLoop(queue_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    }
    IOHIDDeviceUnscheduleFromRunLoop(device_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
  }

  void register_report_callback(const report_callback& callback,
                                uint8_t* _Nonnull report,
                                CFIndex report_size) {
    report_callback_ = callback;
    IOHIDDeviceRegisterInputReportCallback(device_, report, report_size, static_input_report_callback, this);
  }

  void unregister_report_callback(void) {
    IOHIDDeviceRegisterInputReportCallback(device_, nullptr, 0, nullptr, nullptr);
    report_callback_.clear();
  }

  void register_value_callback(const value_callback& callback) {
    if (!queue_) {
      const CFIndex depth = 1024;
      queue_ = IOHIDQueueCreate(kCFAllocatorDefault, device_, depth, kIOHIDOptionsTypeNone);
      if (!queue_) {
        logger_.error("IOHIDQueueCreate error @ {0}", __PRETTY_FUNCTION__);
        return;
      }

      // add elements into queue_.
      for (const auto& it : elements_) {
        IOHIDQueueAddElement(queue_, it.second);
      }
    }

    value_callback_ = callback;
    IOHIDQueueRegisterValueAvailableCallback(queue_, static_queue_value_available_callback, this);

    IOHIDQueueStart(queue_);
  }

  void unregister_value_callback(void) {
    if (queue_) {
      IOHIDQueueStop(queue_);
      IOHIDQueueRegisterValueAvailableCallback(queue_, nullptr, nullptr);
    }
    value_callback_.clear();
  }

  // high-level utility method
  void grab(const value_callback& callback) {
    if (grabbed_) {
      ungrab();
    }

    IOReturn r = open(kIOHIDOptionsTypeSeizeDevice);
    if (r != kIOReturnSuccess) {
      logger_.error("IOHIDDeviceOpen error: 0x{1:x} @ {0}", __PRETTY_FUNCTION__, r);
      return;
    }

    register_value_callback(callback);

    schedule();

    grabbed_ = true;
  }

  // high-level utility method
  void ungrab(void) {
    if (!grabbed_) {
      return;
    }

    unschedule();

    unregister_value_callback();

    if (queue_) {
      // Remove all elements
      for (const auto& it : elements_) {
        IOHIDQueueRemoveElement(queue_, it.second);
      }

      CFRelease(queue_);
      queue_ = nullptr;
    }

    IOReturn r = close();
    if (r != kIOReturnSuccess) {
      logger_.error("IOHIDDeviceClose error: 0x{1:x} @ {0}", __PRETTY_FUNCTION__, r);
      return;
    }

    grabbed_ = false;
  }

  long get_max_input_report_size(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDMaxInputReportSizeKey), value);
    return value;
  }

  long get_vendor_id(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDVendorIDKey), value);
    return value;
  }

  long get_product_id(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDProductIDKey), value);
    return value;
  }

  long get_location_id(void) const {
    long value = 0;
    get_long_property(CFSTR(kIOHIDLocationIDKey), value);
    return value;
  }

  std::string get_manufacturer(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDManufacturerKey), value);
    return value;
  }

  std::string get_product(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDProductKey), value);
    return value;
  }

  std::string get_serial_number_string(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDSerialNumberKey), value);
    return value;
  }

  std::string get_transport(void) const {
    std::string value;
    get_string_property(CFSTR(kIOHIDTransportKey), value);
    return value;
  }

  bool get_long_property(const CFStringRef _Nonnull key, long& value) const {
    auto property = IOHIDDeviceGetProperty(device_, key);
    if (!property) {
      return false;
    }

    if (CFNumberGetTypeID() != CFGetTypeID(property)) {
      return false;
    }

    return CFNumberGetValue(static_cast<CFNumberRef>(property), kCFNumberLongType, &value);
  }

  bool get_string_property(const CFStringRef _Nonnull key, std::string& value) const {
    auto property = IOHIDDeviceGetProperty(device_, key);
    if (!property) {
      return false;
    }

    if (CFStringGetTypeID() != CFGetTypeID(property)) {
      return false;
    }

    auto p = CFStringGetCStringPtr(static_cast<CFStringRef>(property), kCFStringEncodingUTF8);
    if (!p) {
      value.clear();
    } else {
      value = p;
    }
    return true;
  }

  IOHIDElementRef _Nullable get_element(uint32_t usage_page, uint32_t usage) {
    auto key = elements_key(usage_page, usage);
    auto it = elements_.find(key);
    if (it == elements_.end()) {
      return nullptr;
    } else {
      return it->second;
    }
  }

  std::unordered_map<krbn::key_code, krbn::key_code>& get_simple_changed_keys(void) {
    return simple_changed_keys_;
  }

  std::unordered_map<krbn::key_code, krbn::key_code>& get_fn_changed_keys(void) {
    return fn_changed_keys_;
  }

  size_t get_pressed_keys_count(void) const {
    return pressed_key_usages_.size();
  }

private:
  static void static_queue_value_available_callback(void* _Nullable context, IOReturn result, void* _Nullable sender) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<human_interface_device*>(context);
    if (!self) {
      return;
    }

    auto queue = static_cast<IOHIDQueueRef>(sender);
    if (!queue) {
      return;
    }

    self->queue_value_available_callback(queue);
  }

  void queue_value_available_callback(IOHIDQueueRef _Nonnull queue) {
    while (true) {
      auto value = IOHIDQueueCopyNextValueWithTimeout(queue, 0.);
      if (!value) {
        break;
      }

      auto element = IOHIDValueGetElement(value);
      if (element) {
        auto usage_page = IOHIDElementGetUsagePage(element);
        auto usage = IOHIDElementGetUsage(element);
        auto integer_value = IOHIDValueGetIntegerValue(value);

        // update pressed_key_usages_
        if ((usage_page == kHIDPage_KeyboardOrKeypad) ||
            (usage_page == kHIDPage_AppleVendorTopCase && usage == kHIDUsage_AV_TopCase_KeyboardFn)) {
          bool pressed = integer_value;
          uint64_t u = (static_cast<uint64_t>(usage_page) << 32) | usage;
          if (pressed) {
            pressed_key_usages_.push_back(u);
          } else {
            pressed_key_usages_.remove(u);
          }
        }

        // call value_callback_
        if (value_callback_) {
          value_callback_(*this, value, element, usage_page, usage, integer_value);
        }
      }

      CFRelease(value);
    }
  }

  static void static_input_report_callback(void* _Nullable context,
                                           IOReturn result,
                                           void* _Nullable sender,
                                           IOHIDReportType type,
                                           uint32_t report_id,
                                           uint8_t* _Nullable report,
                                           CFIndex report_length) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<human_interface_device*>(context);
    if (!self) {
      return;
    }

    self->input_report_callback(type, report_id, report, report_length);
  }

  void input_report_callback(IOHIDReportType type,
                             uint32_t report_id,
                             uint8_t* _Nullable report,
                             CFIndex report_length) {
    if (report_callback_) {
      report_callback_(*this, type, report_id, report, report_length);
    }
  }

  uint64_t elements_key(uint32_t usage_page, uint32_t usage) {
    return (static_cast<uint64_t>(usage_page) << 32 | usage);
  }

  spdlog::logger& logger_;

  IOHIDDeviceRef _Nonnull device_;
  IOHIDQueueRef _Nullable queue_;
  std::unordered_map<uint64_t, IOHIDElementRef> elements_;

  bool grabbed_;
  std::list<uint64_t> pressed_key_usages_;
  value_callback value_callback_;
  report_callback report_callback_;

  std::unordered_map<krbn::key_code, krbn::key_code> simple_changed_keys_;
  std::unordered_map<krbn::key_code, krbn::key_code> fn_changed_keys_;
};
<|endoftext|>


<commit_before>#include <Arduino.h>
#include "SoftwareSerial.h"
#include "A6lib.h"

#ifdef DEBUG
#define log(msg) Serial.print(msg)
#define logln(msg) Serial.println(msg)
#else
#define log(msg)
#define logln(msg)
#endif

#define countof(a) (sizeof(a) / sizeof(a[0]))

#define OK 1
#define NOTOK 2
#define TIMEOUT 3

#define A6_CMD_TIMEOUT 2000

/////////////////////////////////////////////
// Public methods.
//

A6::A6(int transmitPin, int receivePin) {
    A6conn = new SoftwareSerial(receivePin, transmitPin, false, 1024);
    A6conn->setTimeout(100);
}

A6::~A6() {
    delete A6conn;
}

// Initialize the software serial connection and change the baud rate from the
// default (autodetected) to the desired speed.
void A6::begin(long baudRate) {
    // Give the module some time to settle.
    logln("Waiting for the module to initialize...");
    delay(20000);
    logln("Done.");

    A6conn->flush();

    setRate(baudRate);

    // Factory reset.
    A6command("AT&F", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Echo off.
    A6command("ATE0", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Set caller ID on.
    A6command("AT+CLIP=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Set SMS to text mode.
    A6command("AT+CMGF=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Switch audio to headset.
    enableSpeaker(0);
}

// Reboot the module by setting the specified pin HIGH, then LOW. The pin should
// be connected to a P-MOSFET, not the A6's POWER pin.
void A6::powerCycle(int pin) {
    logln("Power-cycling module...");

    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);

    delay(1000);

    digitalWrite(pin, HIGH);

    // Give the module some time to settle.
    logln("Power-cycle done.");

    A6conn->flush();
}

// Dial a number.
void A6::dial(String number) {
    char buffer[50];

    logln("Dialing number...");

    sprintf(buffer, "ATD%s;", number.c_str());
    A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Redial the last number.
void A6::redial() {
    logln("Redialing last number...");
    A6command("AT+DLST", "OK", "CONNECT", A6_CMD_TIMEOUT, 2, NULL);
}

// Answer a call.
void A6::answer() {
    A6command("ATA", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Hang up the phone.
void A6::hangUp() {
    A6command("ATH", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Check whether there is an active call.
callInfo A6::checkCallStatus() {
    char number[50];
    String response = "";
    int respStart = 0, matched = 0;
    callInfo cinfo = (const struct callInfo){ 0 };

    // Issue the command and wait for the response.
    A6command("AT+CLCC", "OK", "+CLCC", A6_CMD_TIMEOUT, 2, &response);

    // Parse the response if it contains a valid +CLCC.
    respStart = response.indexOf("+CLCC");
    if (respStart >= 0) {
        matched = sscanf(response.substring(respStart).c_str(), "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d", &cinfo.index, &cinfo.direction, &cinfo.state, &cinfo.mode, &cinfo.multiparty, number, &cinfo.type);
        cinfo.number = String(number);
    }

    return cinfo;
}

// Send an SMS.
byte A6::sendSMS(String number, String text) {
    char ctrlZ[2] = { 0x1a, 0x00 };
    char buffer[100];

    if (text.length() > 159) {
        // We can't send messages longer than 160 characters.
        return NOTOK;
    }

    log("Sending SMS to ");
    log(number);
    logln("...");

    sprintf(buffer, "AT+CMGS=\"%s\"", number.c_str());
    A6command(buffer, ">", "yy", A6_CMD_TIMEOUT, 2, NULL);
    delay(100);
    A6conn->println(text.c_str());
    A6conn->println(ctrlZ);
    A6conn->println();

    return OK;
}

// Set the volume for the speaker. level should be a number between 5 and
// 8 inclusive.
void A6::setVol(byte level) {
    char buffer[30];

    // level should be between 5 and 8.
    level = min(max(level, 5), 8);
    sprintf(buffer, "AT+CLVL=%d", level);
    A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Enable the speaker, rather than the headphones. Pass 0 to route audio through
// headphones, 1 through speaker.
void A6::enableSpeaker(byte enable) {
    char buffer[30];

    // enable should be between 0 and 1.
    enable = min(max(enable, 0), 1);
    sprintf(buffer, "AT+SNFS=%d", enable);
    A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

/////////////////////////////////////////////
// Private methods.
//

// Autodetect the connection rate.
int A6::detectRate() {
    int rate = 0;
    int rates[] = {115200, 9600, 1200, 2400, 4800, 19200, 38400, 57600};

    // Try to autodetect the rate.
    logln("Autodetecting connection rate...");
    for (int i = 0; i < countof(rates); i++) {
        rate = rates[i];

        A6conn->begin(rate);
        log("Trying rate ");
        log(rate);
        logln("...");

        delay(100);
        if (A6command("\r\r\rAT", "OK", "+CME", 2000, 2, NULL) == OK) {
            return rate;
        }
    }

    // Return a default rate.
    return 9600;
}

// Set the A6 baud rate.
void A6::setRate(long baudRate) {
    int rate = detectRate();

    // The rate is already the desired rate, return.
    if (rate == baudRate) return;

    logln("Setting baud rate on the module...");
    // Change the rate to the requested.
    char buffer[30];
    sprintf(buffer, "AT+IPR=%d", baudRate);
    A6command(buffer, "OK", "+IPR=", A6_CMD_TIMEOUT, 3, NULL);

    logln("Switching to the new rate...");
    // Begin the connection again at the requested rate.
    A6conn->begin(baudRate);
    logln("Rate set.");
}

// Read some data from the A6 in a non-blocking manner.
String A6::read() {
    String reply = "";
    if (A6conn->available()) reply = A6conn->readString();
    return reply;
}

// Issue a command.
byte A6::A6command(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) {
    byte returnValue = NOTOK;
    byte count = 0;

    // Get rid of any buffered output.
    A6conn->flush();

    while (count < repetitions && returnValue != OK) {
        log("Issuing command: ");
        logln(command);

        A6conn->print(command);
        A6conn->print("\r");

        if (A6waitFor(resp1, resp2, timeout, response) == OK) {
            returnValue = OK;
        } else {
            returnValue = NOTOK;
        }
        count++;
    }
    return returnValue;
}

// Wait for responses.
byte A6::A6waitFor(const char *resp1, const char *resp2, int timeout, String *response) {
    unsigned long entry = millis();
    int count = 0;
    String reply = "";
    byte retVal = 99;
    do {
        reply += read();
        yield();
    } while ((reply.indexOf(resp1) + reply.indexOf(resp2) == -2) && millis() - entry < timeout);

    if (reply != "") {
        log("Reply in ");
        log((millis() - entry));
        log(" ms: ");
        logln(reply);
    }

    if (response != NULL) *response = reply;

    if ((millis() - entry) >= timeout) {
        retVal = TIMEOUT;
        logln("Timed out.");
    } else {
        if (reply.indexOf(resp1) + reply.indexOf(resp2) > -2) {
            logln("Reply OK.");
            retVal = OK;
        } else {
            logln("Reply NOT OK.");
            retVal = NOTOK;
        }
    }
    return retVal;
}
<commit_msg>Fix parenthesis bug.<commit_after>#include <Arduino.h>
#include "SoftwareSerial.h"
#include "A6lib.h"

#ifdef DEBUG
#define log(msg) Serial.print(msg)
#define logln(msg) Serial.println(msg)
#else
#define log(msg)
#define logln(msg)
#endif

#define countof(a) (sizeof(a) / sizeof(a[0]))

#define OK 1
#define NOTOK 2
#define TIMEOUT 3

#define A6_CMD_TIMEOUT 2000

/////////////////////////////////////////////
// Public methods.
//

A6::A6(int transmitPin, int receivePin) {
    A6conn = new SoftwareSerial(receivePin, transmitPin, false, 1024);
    A6conn->setTimeout(100);
}

A6::~A6() {
    delete A6conn;
}

// Initialize the software serial connection and change the baud rate from the
// default (autodetected) to the desired speed.
void A6::begin(long baudRate) {
    // Give the module some time to settle.
    logln("Waiting for the module to initialize...");
    delay(12000);
    logln("Done.");

    A6conn->flush();

    setRate(baudRate);

    // Factory reset.
    A6command("AT&F", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Echo off.
    A6command("ATE0", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Set caller ID on.
    A6command("AT+CLIP=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Set SMS to text mode.
    A6command("AT+CMGF=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);

    // Switch audio to headset.
    enableSpeaker(0);
}

// Reboot the module by setting the specified pin HIGH, then LOW. The pin should
// be connected to a P-MOSFET, not the A6's POWER pin.
void A6::powerCycle(int pin) {
    logln("Power-cycling module...");

    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);

    delay(1000);

    digitalWrite(pin, HIGH);

    // Give the module some time to settle.
    logln("Power-cycle done.");

    A6conn->flush();
}

// Dial a number.
void A6::dial(String number) {
    char buffer[50];

    logln("Dialing number...");

    sprintf(buffer, "ATD%s;", number.c_str());
    A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Redial the last number.
void A6::redial() {
    logln("Redialing last number...");
    A6command("AT+DLST", "OK", "CONNECT", A6_CMD_TIMEOUT, 2, NULL);
}

// Answer a call.
void A6::answer() {
    A6command("ATA", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Hang up the phone.
void A6::hangUp() {
    A6command("ATH", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Check whether there is an active call.
callInfo A6::checkCallStatus() {
    char number[50];
    String response = "";
    int respStart = 0, matched = 0;
    callInfo cinfo = (const struct callInfo){ 0 };

    // Issue the command and wait for the response.
    A6command("AT+CLCC", "OK", "+CLCC", A6_CMD_TIMEOUT, 2, &response);

    // Parse the response if it contains a valid +CLCC.
    respStart = response.indexOf("+CLCC");
    if (respStart >= 0) {
        matched = sscanf(response.substring(respStart).c_str(), "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d", &cinfo.index, &cinfo.direction, &cinfo.state, &cinfo.mode, &cinfo.multiparty, number, &cinfo.type);
        cinfo.number = String(number);
    }

    return cinfo;
}

// Send an SMS.
byte A6::sendSMS(String number, String text) {
    char ctrlZ[2] = { 0x1a, 0x00 };
    char buffer[100];

    if (text.length() > 159) {
        // We can't send messages longer than 160 characters.
        return NOTOK;
    }

    log("Sending SMS to ");
    log(number);
    logln("...");

    sprintf(buffer, "AT+CMGS=\"%s\"", number.c_str());
    A6command(buffer, ">", "yy", A6_CMD_TIMEOUT, 2, NULL);
    delay(100);
    A6conn->println(text.c_str());
    A6conn->println(ctrlZ);
    A6conn->println();

    return OK;
}

// Set the volume for the speaker. level should be a number between 5 and
// 8 inclusive.
void A6::setVol(byte level) {
    char buffer[30];

    // level should be between 5 and 8.
    level = min(max(level, 5), 8);
    sprintf(buffer, "AT+CLVL=%d", level);
    A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

// Enable the speaker, rather than the headphones. Pass 0 to route audio through
// headphones, 1 through speaker.
void A6::enableSpeaker(byte enable) {
    char buffer[30];

    // enable should be between 0 and 1.
    enable = min(max(enable, 0), 1);
    sprintf(buffer, "AT+SNFS=%d", enable);
    A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
}

/////////////////////////////////////////////
// Private methods.
//

// Autodetect the connection rate.
int A6::detectRate() {
    int rate = 0;
    int rates[] = {9600, 115200, 4800, 19200, 38400, 57600};

    // Try to autodetect the rate.
    logln("Autodetecting connection rate...");
    for (int i = 0; i < countof(rates); i++) {
        rate = rates[i];

        A6conn->begin(rate);
        log("Trying rate ");
        log(rate);
        logln("...");

        delay(100);
        if (A6command("\r\r\rAT", "OK", "+CME", 2000, 2, NULL) == OK) {
            return rate;
        }
    }

    // Return a default rate.
    return 9600;
}

// Set the A6 baud rate.
void A6::setRate(long baudRate) {
    int rate = detectRate();

    // The rate is already the desired rate, return.
    if (rate == baudRate) return;

    logln("Setting baud rate on the module...");
    // Change the rate to the requested.
    char buffer[30];
    sprintf(buffer, "AT+IPR=%d", baudRate);
    A6command(buffer, "OK", "+IPR=", A6_CMD_TIMEOUT, 3, NULL);

    logln("Switching to the new rate...");
    // Begin the connection again at the requested rate.
    A6conn->begin(baudRate);
    logln("Rate set.");
}

// Read some data from the A6 in a non-blocking manner.
String A6::read() {
    String reply = "";
    if (A6conn->available()) reply = A6conn->readString();
    return reply;
}

// Issue a command.
byte A6::A6command(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) {
    byte returnValue = NOTOK;
    byte count = 0;

    // Get rid of any buffered output.
    A6conn->flush();

    while (count < repetitions && returnValue != OK) {
        log("Issuing command: ");
        logln(command);

        A6conn->print(command);
        A6conn->print("\r");

        if (A6waitFor(resp1, resp2, timeout, response) == OK) {
            returnValue = OK;
        } else {
            returnValue = NOTOK;
        }
        count++;
    }
    return returnValue;
}

// Wait for responses.
byte A6::A6waitFor(const char *resp1, const char *resp2, int timeout, String *response) {
    unsigned long entry = millis();
    int count = 0;
    String reply = "";
    byte retVal = 99;
    do {
        reply += read();
        yield();
    } while ((reply.indexOf(resp1) + reply.indexOf(resp2) == -2) && ((millis() - entry) < timeout));

    if (reply != "") {
        log("Reply in ");
        log((millis() - entry));
        log(" ms: ");
        logln(reply);
    }

    if (response != NULL) *response = reply;

    if ((millis() - entry) >= timeout) {
        retVal = TIMEOUT;
        logln("Timed out.");
    } else {
        if (reply.indexOf(resp1) + reply.indexOf(resp2) > -2) {
            logln("Reply OK.");
            retVal = OK;
        } else {
            logln("Reply NOT OK.");
            retVal = NOTOK;
        }
    }
    return retVal;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
struct edge {
    char* start;
    char* end;
    int pathCost;
   // int directPath;
};
class graph {
private:
    int** path;
    int** directPath;
    char** names;
    char** shortNames;
    int noOfNodes;
public:
    graph(int noOfNodes, char* names[][2]);
    ~graph();
    bool isConnected(int, int);
    void addEdge(int n1, int n2, int pathcost, int directPathCost);
    void addEdge(int noOfEdges, edge e[]);
    void listNodes();
    void setDirectPath(const char* start, const char* end, int directPathCost);
    void findShortestpath(const char* start, const char* goal);
};
void graph::listNodes() {
    for(int i = 0; i < noOfNodes; i++){
        cout << i << "  " << names[i] << " - " << shortNames[i] << endl;
    }

}
graph::graph(int no, char* namesArg[][2] ) {
    noOfNodes = no;
    path = new int*[no];
    directPath = new int*[no];
    names = new char*[no];
    shortNames = new char*[no];
    for(int i = 0; i < no; i++){
        path[i] = new int[no];
        directPath[i] = new int[no];
        *(names+i) = namesArg[i][0];
        *(shortNames+i) = namesArg[i][1];
        for(int j = 0; j < no; j++)
            path[i][j] = directPath[i][j] = -1;
    }
}
graph::~graph() {
    for(int i = 0; i<noOfNodes; i++) {
       // delete[] names[i];
//        delete[] shortNames[i];
        delete[] path[i];
        delete[] directPath[i];
    }
    delete names;
    delete shortNames;
    delete path;
    delete directPath;
}
bool graph::isConnected(int node1, int node2) {
    return (path[node1][node2] >= 0);
}
void graph::addEdge(int n1, int n2, int pathcost, int directPathCost) {
    path[n1-1][n2-1] = path[n2-1][n1-1] = pathcost;
    directPath[n1-1][n2-1] = directPath[n2-1][n1-1] = directPathCost;
}

void graph::findShortestpath(const char* start, const char* goal) {
	int startNode, goalNode;
    for(int i=0; i < noOfNodes; i++) {
        if( _strcmpi( shortNames[i], start) == 0) {
			startNode = i; break;
		}
    }
    for(int j=0; j < noOfNodes; j++) {
        if( _strcmpi( shortNames[j], goal) == 0) {
			goalNode = j; break;
		}
    }

	int leastCostSoFar = 0;
	int* pathToGoal = new int[noOfNodes];
	int top = -1;
	pathToGoal[++top] = startNode;
	int selectedNode = startNode;
	int supposedSelectedNode;
	int supposedleastCost = -1 , tempLeastCost;
	int count = 0;
	bool triggerUpdate = false;
	while(selectedNode != goalNode) {
		for(int i = 0; i < noOfNodes; i++) {
			if(path[selectedNode][i] >= 0 && selectedNode != i) { //finding neighbour nodes
				tempLeastCost = leastCostSoFar + path[selectedNode][i] + directPath[i][goalNode];
				if( tempLeastCost < supposedleastCost || supposedleastCost < 0 ) {
					supposedleastCost = tempLeastCost;
					supposedSelectedNode = i;
					//triggerUpdate = true;
				}
			}
		}
   //     if(triggerUpdate){
            leastCostSoFar += path[selectedNode][supposedSelectedNode];
            selectedNode = supposedSelectedNode;
            pathToGoal[++top] = selectedNode;
		//}
		//triggerUpdate = false;
		supposedleastCost = -1;
		count++;
	}
	cout<<"Total looping through while loop or depth of search tree  is "<<count<<endl;
	cout<<"Shortest route is through ";
	for(int i = 0; i<= top; i++) {
		cout<<i<<". "<< names[pathToGoal[i]];
		if(i < top)
            cout<<" -->   ";
    }
	cout<<endl<<endl<<"Total path cost is "<<leastCostSoFar<<endl;

}
void graph::setDirectPath(const char* start, const char* end, int directPathCost) {
    int n1, n2;
    for(int i=0; i < noOfNodes; i++) {
        if( _strcmpi( shortNames[i], start) == 0) {
            n1 = i; break;
        }
    }
    for(int j=0; j < noOfNodes; j++) {
        if( _strcmpi( shortNames[j], end) == 0) {
            n2 = j; break;
        }
    }
    directPath[n1][n2] = directPath[n2][n1] = directPathCost;
	//cout<<names[n1]<<"   "<<names[n2]<<"   "<<directPathCost<<endl;
}
void graph::addEdge(int n, edge e[]) {
    int n1, n2;
    for(int i = 0; i < n; i++) {
        for(int j=0; j<noOfNodes; j++) {
            if( _strcmpi( shortNames[j], e[i].start) == 0) {
                n1 = j; break;
            }
        }
        for(int j=0; j<noOfNodes; j++) {
            if( _strcmpi( shortNames[j], e[i].end) == 0) {
                n2 = j; break;
            }
        }
        path[n1][n2] = path[n2][n1] = e[i].pathCost;
		//cout<<names[n1]<<"   "<<names[n2]<<"   "<<e[i].pathCost<<endl;
    }
	//cout<<endl;
}

int main() {
   // char* Nodes[2] = new char;
    char* nodes[20][2] = { "Oradea","or", "Zerind","ze", "Arad","ar", "Timisoara","ti",
                    "Lugoj","lu", "Mehadia","me", "Dobreta","do", "Craiova","cr",
                    "Rimnicu Vilcea","ri", "Sibiu","si", "Fagaras","fa", "Pitesti","pi",
                    "Bucharest","bu", "Giurgiu","gi", "Urziceni","ur", "Hirsova","hi",
                    "Eforie","ef", "Vaslui","va", "lasi","la", "Neamt","ne"
        };
    edge e[23] = {  "or", "ze", 71,
                    "ze", "ar", 75,
                    "ar", "ti", 118,
                    "ti", "lu", 111,
                    "lu", "me", 70,
                    "me", "do", 75,
                    "do", "cr", 120,
                    "cr", "ri", 146,
                    "ri", "si", 80,
                    "si", "or", 151,
                    "si", "ar", 140,
                    "si", "fa", 99,
                    "fa", "bu", 211,
                    "bu", "pi", 101,
                    "pi", "ri", 97,
                    "pi", "cr", 138,
					"bu", "gi", 90,
                    "bu", "ur", 85,
                    "ur", "hi", 98,
                    "hi", "ef", 86,
                    "ur", "va", 142,
                    "va", "la", 92,
                    "la", "ne", 87
                };

    graph g(20, nodes);
   // g.listNodes();
    g.addEdge(23, e);

	g.setDirectPath("bu", "ar", 366);
	g.setDirectPath("bu", "bu", 0);
	g.setDirectPath("bu", "cr", 160);
	g.setDirectPath("bu", "do", 242);
	g.setDirectPath("bu", "ef", 161);
	g.setDirectPath("bu", "fa", 178);
	g.setDirectPath("bu", "gi", 77);
	g.setDirectPath("bu", "hi", 151);
	g.setDirectPath("bu", "la", 226);
	g.setDirectPath("bu", "lu", 244);
	g.setDirectPath("bu", "me", 241);
	g.setDirectPath("bu", "ne", 234);
	g.setDirectPath("bu", "or", 380);
	g.setDirectPath("bu", "pi", 98);
	g.setDirectPath("bu", "ri", 193);
	g.setDirectPath("bu", "si", 253);
	g.setDirectPath("bu", "ti", 329);
	g.setDirectPath("bu", "ur", 80);
	g.setDirectPath("bu", "va", 199);
	g.setDirectPath("bu", "ze", 374);

    g.findShortestpath("ar", "bu");
    system("pause");
    return 0;
}
<commit_msg>Fixed returning to previous node. Added comments. Added input from user.<commit_after>#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;

struct edge {
    char* start;
    char* end;
    int pathCost;
   // int directPath;
};

class graph {
private:
    int** path; // two dimensional array works like adjacency matrix of graph but -1 => not adjacent, other value => path cost of edge
    int** directPath; //straight-line distance
    char** names;
    char** shortNames;
    int noOfNodes;
public:
    graph(int noOfNodes, char* names[][2]);
    ~graph();
    bool isConnected(int, int);
    void addEdge(int n1, int n2, int pathcost, int directPathCost);
    void addEdge(int noOfEdges, edge e[]);
    void listNodes();
    void setDirectPath(const char* start, const char* end, int directPathCost);
    void findShortestpath(const char* start, const char* goal);
};

void graph::listNodes() {
    for(int i = 0; i < noOfNodes; i++){
        cout << i << "  " << names[i] << " - " << shortNames[i] << endl;
    }

}

graph::graph(int no, char* namesArg[][2] ) {
    noOfNodes = no;
    path = new int*[no];
    directPath = new int*[no];
    names = new char*[no];
    shortNames = new char*[no];
    for(int i = 0; i < no; i++){
        path[i] = new int[no];
        directPath[i] = new int[no];
        *(names+i) = namesArg[i][0];
        *(shortNames+i) = namesArg[i][1];
        for(int j = 0; j < no; j++)
            path[i][j] = directPath[i][j] = -1;
    }
}

graph::~graph() {
    for(int i = 0; i<noOfNodes; i++) {
       // delete[] names[i];
//        delete[] shortNames[i];
        delete[] path[i];
        delete[] directPath[i];
    }
    delete names;
    delete shortNames;
    delete path;
    delete directPath;
}

bool graph::isConnected(int node1, int node2) {
    return (path[node1][node2] >= 0);
}

void graph::addEdge(int n1, int n2, int pathcost, int directPathCost) {
    path[n1-1][n2-1] = path[n2-1][n1-1] = pathcost;
    directPath[n1-1][n2-1] = directPath[n2-1][n1-1] = directPathCost;
}

void graph::findShortestpath(const char* start, const char* goal) {
	int startNode, goalNode;
    for(int i=0; i < noOfNodes; i++) {
        if( _strcmpi( shortNames[i], start) == 0) {
			startNode = i; break;
		}
    }
    for(int j=0; j < noOfNodes; j++) {
        if( _strcmpi( shortNames[j], goal) == 0) {
			goalNode = j; break;
		}
    }

	int leastCostSoFar = 0;
	int* pathToGoal = new int[noOfNodes]; // stores routing nodes
	int top = -1;
	pathToGoal[++top] = startNode;
	int selectedNode = startNode; //currently choosen node
	int supposedSelectedNode; // stores node with least estimated cost found so far
	int supposedleastCost = -1; //stores least estimated cost found so far
	int tempLeastCost;
	int count = 0;
	bool alreadyVisitedNode = false;

	while(selectedNode != goalNode) {
		for(int i = 0; i < noOfNodes; i++) {
			alreadyVisitedNode = false;
			for (int j = 0; j <= top; j++) {
				if (i == pathToGoal[j]) {	//makes sure not to return in previous node and prevents infinite loop
					alreadyVisitedNode = true;
					break;
				}
			}
			if(path[selectedNode][i] >= 0 && !alreadyVisitedNode) { //finding neighbour nodes by checking path(edge) cost
				tempLeastCost = leastCostSoFar + path[selectedNode][i] + directPath[i][goalNode];
				if( tempLeastCost < supposedleastCost || supposedleastCost < 0 ) {
					supposedleastCost = tempLeastCost;
					supposedSelectedNode = i;
					//triggerUpdate = true;
				}
			}
		}
        leastCostSoFar += path[selectedNode][supposedSelectedNode]; // adds path cost
        selectedNode = supposedSelectedNode; // chooses new node which as least estimated cost
        pathToGoal[++top] = selectedNode;

		cout << "Choosing \"" << names[pathToGoal[top]] << "\" for minimum estimated cost of " << supposedleastCost << endl;
		supposedleastCost = -1;
		count++;
	}
	cout << endl << "Total looping through while loop or depth of search tree  is "
        << count << endl;
	cout << endl << "Shortest route to " << names[goalNode] << " is through " << endl;
	for(int i = 0; i<= top; i++) {
		cout << names[pathToGoal[i]];
		if(i < top)
            cout <<" -> ";
    }
	cout << endl << endl <<"Total path cost is "<<leastCostSoFar<< endl << endl;
}

void graph::setDirectPath(const char* start, const char* end, int directPathCost) {
    int n1, n2;
    for(int i=0; i < noOfNodes; i++) {
        if( _strcmpi( shortNames[i], start) == 0) {
            n1 = i; break;
        }
    }
    for(int j=0; j < noOfNodes; j++) {
        if( _strcmpi( shortNames[j], end) == 0) {
            n2 = j; break;
        }
    }
    directPath[n1][n2] = directPath[n2][n1] = directPathCost;
	//cout <<names[n1]<<"   "<<names[n2]<<"   "<<directPathCost<< endl;
}

void graph::addEdge(int n, edge e[]) {
    int n1, n2;
    for(int i = 0; i < n; i++) {
        for(int j=0; j<noOfNodes; j++) {
            if( _strcmpi( shortNames[j], e[i].start) == 0) {
                n1 = j; break;
            }
        }
        for(int j=0; j<noOfNodes; j++) {
            if( _strcmpi( shortNames[j], e[i].end) == 0) {
                n2 = j; break;
            }
        }
        path[n1][n2] = path[n2][n1] = e[i].pathCost;
		//cout <<names[n1]<<"   "<<names[n2]<<"   "<<e[i].pathCost<< endl;
    }
	//cout << endl;
}

int main() {
   // char* Nodes[2] = new char;
    char* nodes[20][2] = { "Oradea","or", "Zerind","ze", "Arad","ar", "Timisoara","ti",
                    "Lugoj","lu", "Mehadia","me", "Dobreta","do", "Craiova","cr",
                    "Rimnicu Vilcea","ri", "Sibiu","si", "Fagaras","fa", "Pitesti","pi",
                    "Bucharest","bu", "Giurgiu","gi", "Urziceni","ur", "Hirsova","hi",
                    "Eforie","ef", "Vaslui","va", "lasi","la", "Neamt","ne"
        };
    edge e[23] = {  "or", "ze", 71,
                    "ze", "ar", 75,
                    "ar", "ti", 118,
                    "ti", "lu", 111,
                    "lu", "me", 70,
                    "me", "do", 75,
                    "do", "cr", 120,
                    "cr", "ri", 146,
                    "ri", "si", 80,
                    "si", "or", 151,
                    "si", "ar", 140,
                    "si", "fa", 99,
                    "fa", "bu", 211,
                    "bu", "pi", 101,
                    "pi", "ri", 97,
                    "pi", "cr", 138,
					"bu", "gi", 90,
                    "bu", "ur", 85,
                    "ur", "hi", 98,
                    "hi", "ef", 86,
                    "ur", "va", 142,
                    "va", "la", 92,
                    "la", "ne", 87
                };

    graph g(20, nodes);
   // g.listNodes();
    g.addEdge(23, e);

	g.setDirectPath("bu", "ar", 366);
	g.setDirectPath("bu", "bu", 0);
	g.setDirectPath("bu", "cr", 160);
	g.setDirectPath("bu", "do", 242);
	g.setDirectPath("bu", "ef", 161);
	g.setDirectPath("bu", "fa", 178);
	g.setDirectPath("bu", "gi", 77);
	g.setDirectPath("bu", "hi", 151);
	g.setDirectPath("bu", "la", 226);
	g.setDirectPath("bu", "lu", 244);
	g.setDirectPath("bu", "me", 241);
	g.setDirectPath("bu", "ne", 234);
	g.setDirectPath("bu", "or", 380);
	g.setDirectPath("bu", "pi", 98);
	g.setDirectPath("bu", "ri", 193);
	g.setDirectPath("bu", "si", 253);
	g.setDirectPath("bu", "ti", 329);
	g.setDirectPath("bu", "ur", 80);
	g.setDirectPath("bu", "va", 199);
	g.setDirectPath("bu", "ze", 374);

	//cout << "
	char initialNode[20];
	bool onNodeList = false;

	cout << "Following are places in Romania and corresponding key :" << endl;
	for (int i = 0; i < 20; i++) {
		cout << setw(14) << nodes[i][0] << " - " << nodes[i][1] << "\t    ";
		if (i % 3 == 0)
			cout << endl;
	}
	do {
		cout << "\nEnter the starting place's key to find shortest route. Bucharest will be destination by default:\n";
		cin >> initialNode;
		for (int i = 0; i < 20; i++) {
			if (_strcmpi(nodes[i][1], initialNode) == 0) {
				onNodeList = true; break;
			}
		}
		if (!onNodeList) {
			cout << "'" << initialNode << "' is not in the above list.\n";
		}
	} while (!onNodeList);

	cout << endl;

    g.findShortestpath(initialNode, "bu");
    system("pause");
    return 0;
}
<|endoftext|>