<commit_before>#include <termox/system/system.hpp>

#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iterator>
#include <map>
#include <memory>
#include <shared_mutex>
#include <stdexcept>
#include <thread>
#include <utility>
#include <vector>

#include <signals_light/signal.hpp>

#include <termox/system/animation_engine.hpp>
#include <termox/system/detail/filter_send.hpp>
#include <termox/system/detail/focus.hpp>
#include <termox/system/detail/is_sendable.hpp>
#include <termox/system/detail/send.hpp>
#include <termox/system/detail/send_shortcut.hpp>
#include <termox/system/detail/user_input_event_loop.hpp>
#include <termox/system/event.hpp>
#include <termox/system/event_loop.hpp>
#include <termox/system/event_queue.hpp>
#include <termox/system/system.hpp>
#include <termox/terminal/terminal.hpp>
#include <termox/widget/area.hpp>
#include <termox/widget/widget.hpp>

namespace ox {

auto System::focus_widget() -> Widget* { return detail::Focus::focus_widget(); }

void System::set_focus(Widget& w) { detail::Focus::set(w); }

void System::clear_focus() { detail::Focus::clear(); }

void System::enable_tab_focus() { detail::Focus::enable_tab_focus(); }

void System::disable_tab_focus() { detail::Focus::disable_tab_focus(); }

void System::post_event(Event e) { current_queue_.get().append(std::move(e)); }

void System::exit()
{
    user_input_loop_.exit(0);
    Terminal::uninitialize();
    std::quick_exit(0);
}

void System::set_head(Widget* new_head)
{
    if (auto* const head = head_.load(); head != nullptr)
        head->disable();
    if (new_head != nullptr) {
        new_head->enable();
        System::post_event(Resize_event{*new_head, Terminal::area()});
        detail::Focus::set(*new_head);
    }
    head_ = new_head;
}

auto System::run() -> int
{
    auto* const head = head_.load();
    if (head == nullptr)
        return -1;
    auto const result = user_input_loop_.run();
    // user_input_loop_ is already stopped if you are here.
    animation_engine_.stop();
    Terminal::stop_dynamic_color_engine();
    return result;
}

auto System::send_event(Event e) -> bool
{
    auto handled =
        std::visit([](auto const& e) { return detail::send_shortcut(e); }, e);
    if (!std::visit([](auto const& e) { return detail::is_sendable(e); }, e))
        return false;
    if (!handled) {
        handled =
            std::visit([](auto const& e) { return detail::filter_send(e); }, e);
    }
    if (!handled)
        std::visit([](auto e) { detail::send(std::move(e)); }, std::move(e));
    return true;
}

auto System::send_event(Paint_event e) -> bool
{
    if (!detail::is_sendable(e))
        return false;
    auto const handled = detail::filter_send(e);
    if (!handled)
        detail::send(std::move(e));
    return true;
}

auto System::send_event(Delete_event e) -> bool
{
    auto const handled = detail::filter_send(e);
    if (!handled)
        detail::send(std::move(e));
    return true;
}

sl::Slot<void()> System::quit = [] { System::exit(); };

detail::User_input_event_loop System::user_input_loop_;
Animation_engine System::animation_engine_;
std::reference_wrapper<Event_queue> System::current_queue_ =
    user_input_loop_.event_queue();

}  // namespace ox
<commit_msg>change std::quick_exit to std::_Exit to see if works on MacOS<commit_after>#include <termox/system/system.hpp>

#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iterator>
#include <map>
#include <memory>
#include <shared_mutex>
#include <stdexcept>
#include <thread>
#include <utility>
#include <vector>

#include <signals_light/signal.hpp>

#include <termox/system/animation_engine.hpp>
#include <termox/system/detail/filter_send.hpp>
#include <termox/system/detail/focus.hpp>
#include <termox/system/detail/is_sendable.hpp>
#include <termox/system/detail/send.hpp>
#include <termox/system/detail/send_shortcut.hpp>
#include <termox/system/detail/user_input_event_loop.hpp>
#include <termox/system/event.hpp>
#include <termox/system/event_loop.hpp>
#include <termox/system/event_queue.hpp>
#include <termox/system/system.hpp>
#include <termox/terminal/terminal.hpp>
#include <termox/widget/area.hpp>
#include <termox/widget/widget.hpp>

namespace ox {

auto System::focus_widget() -> Widget* { return detail::Focus::focus_widget(); }

void System::set_focus(Widget& w) { detail::Focus::set(w); }

void System::clear_focus() { detail::Focus::clear(); }

void System::enable_tab_focus() { detail::Focus::enable_tab_focus(); }

void System::disable_tab_focus() { detail::Focus::disable_tab_focus(); }

void System::post_event(Event e) { current_queue_.get().append(std::move(e)); }

void System::exit()
{
    user_input_loop_.exit(0);
    Terminal::uninitialize();
    std::_Exit(0);
}

void System::set_head(Widget* new_head)
{
    if (auto* const head = head_.load(); head != nullptr)
        head->disable();
    if (new_head != nullptr) {
        new_head->enable();
        System::post_event(Resize_event{*new_head, Terminal::area()});
        detail::Focus::set(*new_head);
    }
    head_ = new_head;
}

auto System::run() -> int
{
    auto* const head = head_.load();
    if (head == nullptr)
        return -1;
    auto const result = user_input_loop_.run();
    // user_input_loop_ is already stopped if you are here.
    animation_engine_.stop();
    Terminal::stop_dynamic_color_engine();
    return result;
}

auto System::send_event(Event e) -> bool
{
    auto handled =
        std::visit([](auto const& e) { return detail::send_shortcut(e); }, e);
    if (!std::visit([](auto const& e) { return detail::is_sendable(e); }, e))
        return false;
    if (!handled) {
        handled =
            std::visit([](auto const& e) { return detail::filter_send(e); }, e);
    }
    if (!handled)
        std::visit([](auto e) { detail::send(std::move(e)); }, std::move(e));
    return true;
}

auto System::send_event(Paint_event e) -> bool
{
    if (!detail::is_sendable(e))
        return false;
    auto const handled = detail::filter_send(e);
    if (!handled)
        detail::send(std::move(e));
    return true;
}

auto System::send_event(Delete_event e) -> bool
{
    auto const handled = detail::filter_send(e);
    if (!handled)
        detail::send(std::move(e));
    return true;
}

sl::Slot<void()> System::quit = [] { System::exit(); };

detail::User_input_event_loop System::user_input_loop_;
Animation_engine System::animation_engine_;
std::reference_wrapper<Event_queue> System::current_queue_ =
    user_input_loop_.event_queue();

}  // namespace ox
<|endoftext|>


<commit_before>#ifndef _SPATIAL_MATH_RECT_INL_
#define _SPATIAL_MATH_RECT_INL_

#include <limits>
#include <algorithm>

namespace sm
{

template <typename T>
Rect<T>::Rect()
{
	MakeEmpty();
}

template <typename T>
Rect<T>::Rect(T width, T height)
{
	Build(width, height);
}

template <typename T>
Rect<T>::Rect(const Vector2<T>& center, T width, T height)
{
	Build(width, height);
	Translate(center);
}

template <typename T>
Rect<T>::Rect(const Vector2<T>& v0, const Vector2<T>& v1)
{
	xmin = (std::min)(v0.x, v1.x);
	ymin = (std::min)(v0.y, v1.y);
	xmax = (std::max)(v0.x, v1.x);
	ymax = (std::max)(v0.y, v1.y);
}

template <typename T>
bool Rect<T>::operator == (const Rect<T>& r) const
{
	return xmin == r.xmin
		&& xmax == r.xmax
		&& ymin == r.ymin
		&& ymax == r.ymax;
}

template <typename T>
void Rect<T>::Build(T width, T height)
{
	T hw = width * 0.5f,
	  hh = height * 0.5f;
	xmin = -hw;
	xmax =  hw;
	ymin = -hh;
	ymax =  hh;
}

template <typename T>
void Rect<T>::MakeEmpty()
{
	xmin = ymin = std::numeric_limits<T>::max();
	xmax = ymax = -std::numeric_limits<T>::max();
}

template <typename T>
bool Rect<T>::IsValid() const
{
	return xmin != std::numeric_limits<T>::max() && ymin != std::numeric_limits<T>::max()
		&& xmax != -std::numeric_limits<T>::max() && ymax != -std::numeric_limits<T>::max()
		&& xmin <= xmax && ymin <= ymax;
}

template <typename T>
void Rect<T>::Combine(const Vector2<T>& v)
{
	if (v.x < xmin) xmin = v.x;
	if (v.x > xmax) xmax = v.x;
	if (v.y < ymin) ymin = v.y;
	if (v.y > ymax) ymax = v.y;
}

template <typename T>
void Rect<T>::Combine(const Rect<T>& r)
{
	if (r.xmin < xmin) xmin = r.xmin;
	if (r.xmax > xmax) xmax = r.xmax;
	if (r.ymin < ymin) ymin = r.ymin;
	if (r.ymax > ymax) ymax = r.ymax;
}

template <typename T>
Vector2<T> Rect<T>::Size() const
{
	return Vector2<T>(xmax - xmin, ymax - ymin);
}

template <typename T>
Vector2<T> Rect<T>::Center() const
{
	return Vector2<T>((xmin + xmax) * 0.5f, (ymin + ymax) * 0.5f);
}

template <typename T>
void Rect<T>::Translate(const Vector2<T>& offset)
{
	xmin += offset.x;
	xmax += offset.x;
	ymin += offset.y;
	ymax += offset.y;
}

template <typename T>
void Rect<T>::Scale(T sx, T sy)
{
	xmin *= sx;
	xmax *= sx;
	ymin *= sy;
	ymax *= sy;
}

template <typename T>
void Rect<T>::Shear(T kx, T ky)
{
	MakeEmpty();
	// x' = x + y*kx
	// y' = x*ky + y
	sm ::vec2 v[4];
	v[0].x = xmin + ymin * kx;
	v[0].y = xmin * ky + ymin;
	v[1].x = xmax + ymin * kx;
	v[1].y = xmax * ky + ymin;
	v[2].x = xmax + ymax * kx;
	v[2].y = xmax * ky + ymax;
	v[3].x = xmin + ymax * kx;
	v[3].y = xmin * ky + ymax;
	for (int i = 0; i < 4; ++i) {
		Combine(v[i]);
	}
}

}

#endif // _SPATIAL_MATH_RECT_INL_<commit_msg>[FIXED] rect shear<commit_after>#ifndef _SPATIAL_MATH_RECT_INL_
#define _SPATIAL_MATH_RECT_INL_

#include <limits>
#include <algorithm>

namespace sm
{

template <typename T>
Rect<T>::Rect()
{
	MakeEmpty();
}

template <typename T>
Rect<T>::Rect(T width, T height)
{
	Build(width, height);
}

template <typename T>
Rect<T>::Rect(const Vector2<T>& center, T width, T height)
{
	Build(width, height);
	Translate(center);
}

template <typename T>
Rect<T>::Rect(const Vector2<T>& v0, const Vector2<T>& v1)
{
	xmin = (std::min)(v0.x, v1.x);
	ymin = (std::min)(v0.y, v1.y);
	xmax = (std::max)(v0.x, v1.x);
	ymax = (std::max)(v0.y, v1.y);
}

template <typename T>
bool Rect<T>::operator == (const Rect<T>& r) const
{
	return xmin == r.xmin
		&& xmax == r.xmax
		&& ymin == r.ymin
		&& ymax == r.ymax;
}

template <typename T>
void Rect<T>::Build(T width, T height)
{
	T hw = width * 0.5f,
	  hh = height * 0.5f;
	xmin = -hw;
	xmax =  hw;
	ymin = -hh;
	ymax =  hh;
}

template <typename T>
void Rect<T>::MakeEmpty()
{
	xmin = ymin = std::numeric_limits<T>::max();
	xmax = ymax = -std::numeric_limits<T>::max();
}

template <typename T>
bool Rect<T>::IsValid() const
{
	return xmin != std::numeric_limits<T>::max() && ymin != std::numeric_limits<T>::max()
		&& xmax != -std::numeric_limits<T>::max() && ymax != -std::numeric_limits<T>::max()
		&& xmin <= xmax && ymin <= ymax;
}

template <typename T>
void Rect<T>::Combine(const Vector2<T>& v)
{
	if (v.x < xmin) xmin = v.x;
	if (v.x > xmax) xmax = v.x;
	if (v.y < ymin) ymin = v.y;
	if (v.y > ymax) ymax = v.y;
}

template <typename T>
void Rect<T>::Combine(const Rect<T>& r)
{
	if (r.xmin < xmin) xmin = r.xmin;
	if (r.xmax > xmax) xmax = r.xmax;
	if (r.ymin < ymin) ymin = r.ymin;
	if (r.ymax > ymax) ymax = r.ymax;
}

template <typename T>
Vector2<T> Rect<T>::Size() const
{
	return Vector2<T>(xmax - xmin, ymax - ymin);
}

template <typename T>
Vector2<T> Rect<T>::Center() const
{
	return Vector2<T>((xmin + xmax) * 0.5f, (ymin + ymax) * 0.5f);
}

template <typename T>
void Rect<T>::Translate(const Vector2<T>& offset)
{
	xmin += offset.x;
	xmax += offset.x;
	ymin += offset.y;
	ymax += offset.y;
}

template <typename T>
void Rect<T>::Scale(T sx, T sy)
{
	xmin *= sx;
	xmax *= sx;
	ymin *= sy;
	ymax *= sy;
}

template <typename T>
void Rect<T>::Shear(T kx, T ky)
{
	// x' = x + y*kx
	// y' = x*ky + y
	sm ::vec2 v[4];
	v[0].x = xmin + ymin * kx;
	v[0].y = xmin * ky + ymin;
	v[1].x = xmax + ymin * kx;
	v[1].y = xmax * ky + ymin;
	v[2].x = xmax + ymax * kx;
	v[2].y = xmax * ky + ymax;
	v[3].x = xmin + ymax * kx;
	v[3].y = xmin * ky + ymax;

	MakeEmpty();
	for (int i = 0; i < 4; ++i) {
		Combine(v[i]);
	}
}

}

#endif // _SPATIAL_MATH_RECT_INL_<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "dispatcher_p.h"

#include "common.h"
#include "context.h"
#include "controller.h"
#include "action.h"
#include "request_p.h"
#include "dispatchtypepath.h"

#include <QUrl>
#include <QMetaMethod>
#include <QStringBuilder>
#include <QDebug>

using namespace Cutelyst;

Dispatcher::Dispatcher(QObject *parent) :
    QObject(parent),
    d_ptr(new DispatcherPrivate)
{
}

Dispatcher::~Dispatcher()
{
    delete d_ptr;
}

void Dispatcher::setupActions(const QList<Controller*> &controllers)
{
    Q_D(Dispatcher);

    if (d->dispatchers.isEmpty()) {
        registerDispatchType(new DispatchTypePath(this));
    }

    Q_FOREACH (Controller *controller, controllers) {
        // App controller
//        qDebug() << "Found a controller:" << controller << meta->className();
        const QMetaObject *meta = controller->metaObject();
        controller->setObjectName(meta->className());
        bool instanceUsed = false;
        for (int i = 0; i < meta->methodCount(); ++i) {
            QMetaMethod method = meta->method(i);
            if (method.methodType() == QMetaMethod::Method ||
                    method.methodType() == QMetaMethod::Slot) {
                bool registered = false;
                Action *action = new Action(method, controller, this);
                if (action->isValid() && !d->actionHash.contains(action->privateName())) {
                    if (!action->attributes().contains("Private")) {
                        // Register the action with each dispatcher
                        Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
                            if (dispatch->registerAction(action)) {
                                registered = true;
                            }
                        }
                    } else {
                        // We register private actions
                        registered = true;
                    }
                }

                // The Begin, Auto, End actions are not
                // registered by Dispatchers but we need them
                // as private actions anyway
                if (registered || action->isValid()) {
                    d->actionHash.insert(action->privateName(), action);
                    d->containerHash[action->ns()] << action;
                    instanceUsed = true;
                } else {
                    delete action;
                }
            }
        }

        if (instanceUsed) {
            d->constrollerHash.insert(meta->className(), controller);
        }
    }

    printActions();
}

bool Dispatcher::dispatch(Context *ctx)
{
    if (ctx->action()) {
        QByteArray action = ctx->ns();
        action.reserve(action.size() + 12);
        action.prepend('/');
        action.append("/_DISPATCH", 10);
        return forward(ctx, action);
    } else {
        QString error;
        const QString &path = ctx->req()->path();
        if (path.isEmpty()) {
            error = QLatin1String("No default action defined");
        } else {
            error = QLatin1String("Unknown resource \"") % path % QLatin1Char('"');
        }
        qCDebug(CUTELYST_DISPATCHER) << error;
        ctx->error(error);
    }
    return false;
}

bool Dispatcher::forward(Context *ctx, const QByteArray &opname, const QStringList &arguments)
{
    Action *action = command2Action(ctx, opname);
    if (action) {
        return action->dispatch(ctx);
    }

    qCCritical(CUTELYST_DISPATCHER) << "Action not found" << action;
    return false;
}

void Dispatcher::prepareAction(Context *ctx)
{
    Q_D(Dispatcher);

    QByteArray path = ctx->req()->path().toLatin1();
    QList<QByteArray> pathParts = path.split('/');
    QList<QByteArray> args;

    // Root action
    pathParts.prepend(QByteArrayLiteral(""));

    int pos = path.size();
    while (pos != -1) {
        Q_FOREACH (DispatchType *type, d->dispatchers) {
            QByteArray actionPath = path.mid(1, pos);
            if (type->match(ctx, actionPath, args)) {

                ctx->req()->d_ptr->args = unexcapedArgs(args);

                if (!path.isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Path is" << actionPath;
                }

                if (!ctx->args().isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Arguments are" << ctx->args().join(QLatin1Char('/'));
                }

                return;
            }
        }

        pos = path.lastIndexOf('/', pos - 1);

        args.prepend(pathParts.takeLast());
    }
}

Action *Dispatcher::getAction(const QByteArray &name, const QByteArray &ns) const
{
    Q_D(const Dispatcher);

    if (name.isEmpty()) {
        return 0;
    }

    QByteArray action = cleanNamespace(ns);
    action.reserve(action.size() + name.size() + 1);
    action.append('/');
    action.append(name);

    return d->actionHash.value(action);
}

ActionList Dispatcher::getActions(const QByteArray &name, const QByteArray &ns) const
{
    Q_D(const Dispatcher);

    ActionList ret;
    if (name.isEmpty()) {
        return ret;
    }

    QByteArray _ns = cleanNamespace(ns);

    ActionList containers = d->getContainers(_ns);
    Q_FOREACH (Action *action, containers) {
        if (action->name() == name) {
            ret.prepend(action);
        }
    }

    return ret;
}

QHash<QByteArray, Controller *> Dispatcher::controllers() const
{
    Q_D(const Dispatcher);
    return d->constrollerHash;
}

QByteArray Dispatcher::uriForAction(Action *action, const QStringList &captures)
{
    Q_D(const Dispatcher);
    Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
        QByteArray uri = dispatch->uriForAction(action, captures);
        if (!uri.isNull()) {
            return uri.isEmpty() ? QByteArray("/", 1) : uri;
        }
    }
    return QByteArray();
}

void Dispatcher::registerDispatchType(DispatchType *dispatchType)
{
    Q_D(Dispatcher);
    d->dispatchers.append(dispatchType);
}

void Dispatcher::printActions()
{
    Q_D(Dispatcher);

    QString buffer;
    QTextStream out(&buffer, QIODevice::WriteOnly);
    bool showInternalActions = false;

    out << "Loaded Private actions:" << endl;
    QString privateTitle("Private");
    QString classTitle("Class");
    QString methodTitle("Method");
    int privateLength = privateTitle.length();
    int classLength = classTitle.length();
    int actionLength = methodTitle.length();
    QHash<QByteArray, Action*>::ConstIterator it = d->actionHash.constBegin();
    while (it != d->actionHash.constEnd()) {
        Action *action = it.value();
        QByteArray path = it.key();
        if (!path.startsWith('/')) {
            path.prepend('/');
        }
        privateLength = qMax(privateLength, path.length());
        classLength = qMax(classLength, action->className().length());
        actionLength = qMax(actionLength, action->name().length());
        ++it;
    }

    out << "." << QString().fill(QLatin1Char('-'), privateLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), classLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), actionLength).toUtf8().data()
        << "." << endl;
    out << "|" << privateTitle.leftJustified(privateLength).toUtf8().data()
        << "|" << classTitle.leftJustified(classLength).toUtf8().data()
        << "|" << methodTitle.leftJustified(actionLength).toUtf8().data()
        << "|" << endl;
    out << "." << QString().fill(QLatin1Char('-'), privateLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), classLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), actionLength).toUtf8().data()
        << "." << endl;

    QList<QByteArray> keys = d->actionHash.keys();
    qSort(keys.begin(), keys.end());
    Q_FOREACH (const QByteArray &key, keys) {
        Action *action = d->actionHash.value(key);
        if (showInternalActions || !action->name().startsWith('_')) {
            QString path = key;
            if (!path.startsWith(QLatin1String("/"))) {
                path.prepend(QLatin1String("/"));
            }
            out << "|" << path.leftJustified(privateLength).toUtf8().data()
                << "|" << QString(action->className()).leftJustified(classLength).toUtf8().data()
                << "|" << QString(action->name()).leftJustified(actionLength).toUtf8().data()
                << "|" << endl;
        }
    }

    out << "." << QString().fill(QLatin1Char('-'), privateLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), classLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), actionLength).toUtf8().data()
        << ".";
    qCDebug(CUTELYST_DISPATCHER) << buffer.toUtf8().data();

    // List all public actions
    Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
        dispatch->list();
    }
}

Action *Dispatcher::command2Action(Context *ctx, const QByteArray &command, const QStringList &extraParams)
{
    Q_D(Dispatcher);
//    qDebug() << Q_FUNC_INFO << "Command" << command;

    Action *ret = d->actionHash.value(command);
    if (!ret) {
        ret = invokeAsPath(ctx, command, ctx->args());
    }

    return ret;
}

QStringList Dispatcher::unexcapedArgs(const QList<QByteArray> &args)
{
    QStringList ret;
    Q_FOREACH (const QByteArray &arg, args) {
        ret.append(QUrl::fromPercentEncoding(arg));
    }
    return ret;
}

QByteArray Dispatcher::actionRel2Abs(Context *ctx, const QByteArray &path)
{
    QByteArray ret = path;
    if (!ret.startsWith('/')) {
        // TODO at Catalyst it uses
        // c->stack->last()->namespace
        ret.prepend('/');
        ret.prepend(ctx->action()->ns());
    }

    if (ret.startsWith('/')) {
        ret.remove(0, 1);
    }

    return ret;
}

Action *Dispatcher::invokeAsPath(Context *ctx, const QByteArray &relativePath, const QStringList &args)
{
    Q_D(Dispatcher);

    Action *ret;
    QByteArray path = actionRel2Abs(ctx, relativePath);

    int pos = path.lastIndexOf('/');
    int lastPos = path.size();
    do {
        if (pos == -1) {
            ret = getAction(path, QByteArray());
            if (ret) {
                return ret;
            }
        } else {
            QByteArray name = path.mid(pos + 1, lastPos);
            path = path.mid(0, pos);
            ret = getAction(name, path);
            if (ret) {
                return ret;
            }
        }

        lastPos = pos;
        pos = path.indexOf('/', pos - 1);
    } while (pos != -1);

    return 0;
}

QByteArray Dispatcher::cleanNamespace(const QByteArray &ns) const
{
    QByteArray ret = ns;
    bool lastWasSlash = true; // remove initial slash
    int nsSize = ns.size();
    const char * data = ret.constData();
    for (int i = 0; i < nsSize; ++i) {
        if (data[i] == '/') {
            if (lastWasSlash) {
                ret.remove(i, 1);
                data = ret.constData();
                --nsSize;
            } else {
                lastWasSlash = true;
            }
        } else {
            lastWasSlash = false;
        }
    }
    return ret;
}

ActionList DispatcherPrivate::getContainers(const QByteArray &ns) const
{
    ActionList ret;

    if (ns != "/") {
        int pos = ns.size();
//        qDebug() << pos << _ns.mid(0, pos);
        while (pos > 0) {
//            qDebug() << pos << _ns.mid(0, pos);
            ret.append(containerHash.value(ns.mid(0, pos)));
            pos = ns.lastIndexOf('/', pos - 1);
        }
    }
    ret.append(containerHash.value(""));

    return ret;
}
<commit_msg>Fix compilation<commit_after>/*
 * Copyright (C) 2013 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "dispatcher_p.h"

#include "common.h"
#include "context.h"
#include "controller.h"
#include "action.h"
#include "request_p.h"
#include "dispatchtypepath.h"

#include <QUrl>
#include <QMetaMethod>
#include <QStringBuilder>
#include <QDebug>

using namespace Cutelyst;

Dispatcher::Dispatcher(QObject *parent) :
    QObject(parent),
    d_ptr(new DispatcherPrivate)
{
}

Dispatcher::~Dispatcher()
{
    delete d_ptr;
}

void Dispatcher::setupActions(const QList<Controller*> &controllers)
{
    Q_D(Dispatcher);

    if (d->dispatchers.isEmpty()) {
        registerDispatchType(new DispatchTypePath(this));
    }

    Q_FOREACH (Controller *controller, controllers) {
        // App controller
//        qDebug() << "Found a controller:" << controller << meta->className();
        const QMetaObject *meta = controller->metaObject();
        controller->setObjectName(meta->className());
        bool instanceUsed = false;
        for (int i = 0; i < meta->methodCount(); ++i) {
            QMetaMethod method = meta->method(i);
            if (method.methodType() == QMetaMethod::Method ||
                    method.methodType() == QMetaMethod::Slot) {
                bool registered = false;
                Action *action = new Action(method, controller);
                if (action->isValid() && !d->actionHash.contains(action->privateName())) {
                    if (!action->attributes().contains("Private")) {
                        // Register the action with each dispatcher
                        Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
                            if (dispatch->registerAction(action)) {
                                registered = true;
                            }
                        }
                    } else {
                        // We register private actions
                        registered = true;
                    }
                }

                // The Begin, Auto, End actions are not
                // registered by Dispatchers but we need them
                // as private actions anyway
                if (registered || action->isValid()) {
                    d->actionHash.insert(action->privateName(), action);
                    d->containerHash[action->ns()] << action;
                    instanceUsed = true;
                } else {
                    delete action;
                }
            }
        }

        if (instanceUsed) {
            d->constrollerHash.insert(meta->className(), controller);
        }
    }

    printActions();
}

bool Dispatcher::dispatch(Context *ctx)
{
    if (ctx->action()) {
        QByteArray action = ctx->ns();
        action.reserve(action.size() + 12);
        action.prepend('/');
        action.append("/_DISPATCH", 10);
        return forward(ctx, action);
    } else {
        QString error;
        const QString &path = ctx->req()->path();
        if (path.isEmpty()) {
            error = QLatin1String("No default action defined");
        } else {
            error = QLatin1String("Unknown resource \"") % path % QLatin1Char('"');
        }
        qCDebug(CUTELYST_DISPATCHER) << error;
        ctx->error(error);
    }
    return false;
}

bool Dispatcher::forward(Context *ctx, const QByteArray &opname, const QStringList &arguments)
{
    Action *action = command2Action(ctx, opname);
    if (action) {
        return action->dispatch(ctx);
    }

    qCCritical(CUTELYST_DISPATCHER) << "Action not found" << action;
    return false;
}

void Dispatcher::prepareAction(Context *ctx)
{
    Q_D(Dispatcher);

    QByteArray path = ctx->req()->path().toLatin1();
    QList<QByteArray> pathParts = path.split('/');
    QList<QByteArray> args;

    // Root action
    pathParts.prepend(QByteArrayLiteral(""));

    int pos = path.size();
    while (pos != -1) {
        Q_FOREACH (DispatchType *type, d->dispatchers) {
            QByteArray actionPath = path.mid(1, pos);
            if (type->match(ctx, actionPath, args)) {

                ctx->req()->d_ptr->args = unexcapedArgs(args);

                if (!path.isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Path is" << actionPath;
                }

                if (!ctx->args().isEmpty()) {
                    qCDebug(CUTELYST_DISPATCHER) << "Arguments are" << ctx->args().join(QLatin1Char('/'));
                }

                return;
            }
        }

        pos = path.lastIndexOf('/', pos - 1);

        args.prepend(pathParts.takeLast());
    }
}

Action *Dispatcher::getAction(const QByteArray &name, const QByteArray &ns) const
{
    Q_D(const Dispatcher);

    if (name.isEmpty()) {
        return 0;
    }

    QByteArray action = cleanNamespace(ns);
    action.reserve(action.size() + name.size() + 1);
    action.append('/');
    action.append(name);

    return d->actionHash.value(action);
}

ActionList Dispatcher::getActions(const QByteArray &name, const QByteArray &ns) const
{
    Q_D(const Dispatcher);

    ActionList ret;
    if (name.isEmpty()) {
        return ret;
    }

    QByteArray _ns = cleanNamespace(ns);

    ActionList containers = d->getContainers(_ns);
    Q_FOREACH (Action *action, containers) {
        if (action->name() == name) {
            ret.prepend(action);
        }
    }

    return ret;
}

QHash<QByteArray, Controller *> Dispatcher::controllers() const
{
    Q_D(const Dispatcher);
    return d->constrollerHash;
}

QByteArray Dispatcher::uriForAction(Action *action, const QStringList &captures)
{
    Q_D(const Dispatcher);
    Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
        QByteArray uri = dispatch->uriForAction(action, captures);
        if (!uri.isNull()) {
            return uri.isEmpty() ? QByteArray("/", 1) : uri;
        }
    }
    return QByteArray();
}

void Dispatcher::registerDispatchType(DispatchType *dispatchType)
{
    Q_D(Dispatcher);
    d->dispatchers.append(dispatchType);
}

void Dispatcher::printActions()
{
    Q_D(Dispatcher);

    QString buffer;
    QTextStream out(&buffer, QIODevice::WriteOnly);
    bool showInternalActions = false;

    out << "Loaded Private actions:" << endl;
    QString privateTitle("Private");
    QString classTitle("Class");
    QString methodTitle("Method");
    int privateLength = privateTitle.length();
    int classLength = classTitle.length();
    int actionLength = methodTitle.length();
    QHash<QByteArray, Action*>::ConstIterator it = d->actionHash.constBegin();
    while (it != d->actionHash.constEnd()) {
        Action *action = it.value();
        QByteArray path = it.key();
        if (!path.startsWith('/')) {
            path.prepend('/');
        }
        privateLength = qMax(privateLength, path.length());
        classLength = qMax(classLength, action->className().length());
        actionLength = qMax(actionLength, action->name().length());
        ++it;
    }

    out << "." << QString().fill(QLatin1Char('-'), privateLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), classLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), actionLength).toUtf8().data()
        << "." << endl;
    out << "|" << privateTitle.leftJustified(privateLength).toUtf8().data()
        << "|" << classTitle.leftJustified(classLength).toUtf8().data()
        << "|" << methodTitle.leftJustified(actionLength).toUtf8().data()
        << "|" << endl;
    out << "." << QString().fill(QLatin1Char('-'), privateLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), classLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), actionLength).toUtf8().data()
        << "." << endl;

    QList<QByteArray> keys = d->actionHash.keys();
    qSort(keys.begin(), keys.end());
    Q_FOREACH (const QByteArray &key, keys) {
        Action *action = d->actionHash.value(key);
        if (showInternalActions || !action->name().startsWith('_')) {
            QString path = key;
            if (!path.startsWith(QLatin1String("/"))) {
                path.prepend(QLatin1String("/"));
            }
            out << "|" << path.leftJustified(privateLength).toUtf8().data()
                << "|" << QString(action->className()).leftJustified(classLength).toUtf8().data()
                << "|" << QString(action->name()).leftJustified(actionLength).toUtf8().data()
                << "|" << endl;
        }
    }

    out << "." << QString().fill(QLatin1Char('-'), privateLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), classLength).toUtf8().data()
        << "+" << QString().fill(QLatin1Char('-'), actionLength).toUtf8().data()
        << ".";
    qCDebug(CUTELYST_DISPATCHER) << buffer.toUtf8().data();

    // List all public actions
    Q_FOREACH (DispatchType *dispatch, d->dispatchers) {
        dispatch->list();
    }
}

Action *Dispatcher::command2Action(Context *ctx, const QByteArray &command, const QStringList &extraParams)
{
    Q_D(Dispatcher);
//    qDebug() << Q_FUNC_INFO << "Command" << command;

    Action *ret = d->actionHash.value(command);
    if (!ret) {
        ret = invokeAsPath(ctx, command, ctx->args());
    }

    return ret;
}

QStringList Dispatcher::unexcapedArgs(const QList<QByteArray> &args)
{
    QStringList ret;
    Q_FOREACH (const QByteArray &arg, args) {
        ret.append(QUrl::fromPercentEncoding(arg));
    }
    return ret;
}

QByteArray Dispatcher::actionRel2Abs(Context *ctx, const QByteArray &path)
{
    QByteArray ret = path;
    if (!ret.startsWith('/')) {
        // TODO at Catalyst it uses
        // c->stack->last()->namespace
        ret.prepend('/');
        ret.prepend(ctx->action()->ns());
    }

    if (ret.startsWith('/')) {
        ret.remove(0, 1);
    }

    return ret;
}

Action *Dispatcher::invokeAsPath(Context *ctx, const QByteArray &relativePath, const QStringList &args)
{
    Q_D(Dispatcher);

    Action *ret;
    QByteArray path = actionRel2Abs(ctx, relativePath);

    int pos = path.lastIndexOf('/');
    int lastPos = path.size();
    do {
        if (pos == -1) {
            ret = getAction(path, QByteArray());
            if (ret) {
                return ret;
            }
        } else {
            QByteArray name = path.mid(pos + 1, lastPos);
            path = path.mid(0, pos);
            ret = getAction(name, path);
            if (ret) {
                return ret;
            }
        }

        lastPos = pos;
        pos = path.indexOf('/', pos - 1);
    } while (pos != -1);

    return 0;
}

QByteArray Dispatcher::cleanNamespace(const QByteArray &ns) const
{
    QByteArray ret = ns;
    bool lastWasSlash = true; // remove initial slash
    int nsSize = ns.size();
    const char * data = ret.constData();
    for (int i = 0; i < nsSize; ++i) {
        if (data[i] == '/') {
            if (lastWasSlash) {
                ret.remove(i, 1);
                data = ret.constData();
                --nsSize;
            } else {
                lastWasSlash = true;
            }
        } else {
            lastWasSlash = false;
        }
    }
    return ret;
}

ActionList DispatcherPrivate::getContainers(const QByteArray &ns) const
{
    ActionList ret;

    if (ns != "/") {
        int pos = ns.size();
//        qDebug() << pos << _ns.mid(0, pos);
        while (pos > 0) {
//            qDebug() << pos << _ns.mid(0, pos);
            ret.append(containerHash.value(ns.mid(0, pos)));
            pos = ns.lastIndexOf('/', pos - 1);
        }
    }
    ret.append(containerHash.value(""));

    return ret;
}
<|endoftext|>


<commit_before>/*
 *
 * RippleLikeWallet
 *
 * Created by El Khalil Bellakrid on 06/01/2019.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ledger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "RippleLikeWallet.h"

#include "RippleLikeAccount.h"

#include <algorithm>

#include <async/wait.h>
#include <api/ErrorCode.hpp>
#include <api/AccountCallback.hpp>
#include <api/ConfigurationDefaults.hpp>
#include <api/KeychainEngines.hpp>

#include <ripple/RippleLikeExtendedPublicKey.h>

#include <wallet/common/database/AccountDatabaseHelper.h>
#include <wallet/ripple/database/RippleLikeAccountDatabaseHelper.h>

namespace ledger {
    namespace core {

        const api::WalletType RippleLikeWallet::type = api::WalletType::ETHEREUM;

        RippleLikeWallet::RippleLikeWallet(const std::string &name,
                                           const std::shared_ptr<RippleLikeBlockchainExplorer> &explorer,
                                           const std::shared_ptr<RippleLikeBlockchainObserver> &observer,
                                           const std::shared_ptr<RippleLikeKeychainFactory> &keychainFactory,
                                           const RippleLikeAccountSynchronizerFactory &synchronizer,
                                           const std::shared_ptr<WalletPool> &pool, const api::Currency &network,
                                           const std::shared_ptr<DynamicObject> &configuration,
                                           const DerivationScheme &scheme
        )
                : AbstractWallet(name, network, pool, configuration, scheme) {
            _explorer = explorer;
            _observer = observer;
            _keychainFactory = keychainFactory;
            _synchronizerFactory = synchronizer;
        }

        bool RippleLikeWallet::isSynchronizing() {
            return false;
        }

        std::shared_ptr<api::EventBus> RippleLikeWallet::synchronize() {
            return nullptr;
        }

        FuturePtr<ledger::core::api::Account>
        RippleLikeWallet::newAccountWithInfo(const api::AccountCreationInfo &info) {
            if (info.chainCodes.size() != 1 || info.publicKeys.size() != 1 || info.owners.size() != 1)
                throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                     "Account creation info are inconsistent (only one public key is needed)");
            auto self = getSelf();
            return async<api::ExtendedKeyAccountCreationInfo>([self, info]() -> api::ExtendedKeyAccountCreationInfo {
                if (info.owners.size() != info.derivations.size() || info.owners.size() != info.chainCodes.size() ||
                    info.publicKeys.size() != info.owners.size())
                    throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                         "Account creation info are inconsistent (size of arrays differs)");
                api::ExtendedKeyAccountCreationInfo result;

                if (info.chainCodes[0].size() != 32 || info.publicKeys[0].size() != 33)
                    throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                         "Account creation info are inconsistent (contains invalid public key(s))");
                DerivationPath occurencePath(info.derivations[0]);

                auto xpub = RippleLikeExtendedPublicKey::fromRaw(
                        self->getCurrency(),
                        Option<std::vector<uint8_t>>(),
                        info.publicKeys[0],
                        info.chainCodes[0],
                        info.derivations[0]
                );
                result.owners.push_back(info.owners[0]);
                result.derivations.push_back(info.derivations[0]);
                result.extendedKeys.push_back(xpub->toBase58());
                result.index = info.index;
                return result;
            }).flatMap<std::shared_ptr<ledger::core::api::Account>>(getContext(),
                                                                    [self](const api::ExtendedKeyAccountCreationInfo &info) -> Future<std::shared_ptr<ledger::core::api::Account>> {
                                                                        return self->newAccountWithExtendedKeyInfo(
                                                                                info);
                                                                    });
        }

        static int32_t getAccountIndex(const DerivationScheme &dPath, int32_t index) {
            // Set account index only if account level not hardened,
            // otherwise keep the one defined in derivation scheme
            return dPath.getSchemeTo(DerivationSchemeLevel::ACCOUNT_INDEX).getPath().isLastChildHardened() ? dPath.getAccountIndex() : index;
        }

        FuturePtr<ledger::core::api::Account>
        RippleLikeWallet::newAccountWithExtendedKeyInfo(const api::ExtendedKeyAccountCreationInfo &info) {

            if (info.extendedKeys.empty()) {
                throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                     "Empty extended keys passed to newAccountWithExtendedKeyInfo");
            }

            auto self = getSelf();
            auto scheme = getDerivationScheme();
            auto accountIndex = getAccountIndex(scheme, info.index);
            scheme.setCoinType(getCurrency().bip44CoinType).setAccountIndex(accountIndex);
            auto xpubPath = scheme.getSchemeTo(DerivationSchemeLevel::ACCOUNT_INDEX).getPath();
            return async<std::shared_ptr<api::Account> >([=]() -> std::shared_ptr<api::Account> {
                auto keychain = self->_keychainFactory->build(
                        accountIndex,
                        xpubPath,
                        getConfig(),
                        info,
                        getAccountInternalPreferences(accountIndex),
                        getCurrency()
                );
                soci::session sql(self->getDatabase()->getPool());
                soci::transaction tr(sql);
                auto accountUid = AccountDatabaseHelper::createAccountUid(self->getWalletUid(), accountIndex);
                if (AccountDatabaseHelper::accountExists(sql, self->getWalletUid(), accountIndex))
                    throw make_exception(api::ErrorCode::ACCOUNT_ALREADY_EXISTS,
                                         "Account {}, for wallet '{}', already exists", accountIndex, self->getWalletUid());
                AccountDatabaseHelper::createAccount(sql, self->getWalletUid(), accountIndex);
                RippleLikeAccountDatabaseHelper::createAccount(sql, self->getWalletUid(), accountIndex,
                                                               info.extendedKeys[info.extendedKeys.size() - 1]);
                tr.commit();
                auto account = std::static_pointer_cast<api::Account>(std::make_shared<RippleLikeAccount>(
                        self->shared_from_this(),
                        accountIndex,
                        self->_explorer,
                        self->_observer,
                        self->_synchronizerFactory(),
                        keychain
                ));
                self->addAccountInstanceToInstanceCache(std::dynamic_pointer_cast<AbstractAccount>(account));
                return account;
            });
        }

        static DerivationScheme getAccountScheme(DerivationScheme &scheme) {
            auto accountScheme = scheme.getSchemeTo(DerivationSchemeLevel::ACCOUNT_INDEX);
            // To handle all exotic paths we should avoid private derivations
            // So if node or/and address level are hardened, then they are included in account's derivation path
            auto path = scheme.getPath();
            auto hardenedDepth = path.getDepth();
            while (hardenedDepth) {
                if (path.isHardened(hardenedDepth - 1)) {
                    break;
                }
                hardenedDepth--;
            }
            auto lastHardenedScheme = scheme.getSchemeToDepth(hardenedDepth);
            return accountScheme.getPath().getDepth() > lastHardenedScheme.getPath().getDepth() ? accountScheme : lastHardenedScheme;
        }

        Future<api::ExtendedKeyAccountCreationInfo>
        RippleLikeWallet::getExtendedKeyAccountCreationInfo(int32_t accountIndex) {
            auto self = std::dynamic_pointer_cast<RippleLikeWallet>(shared_from_this());
            return async<api::ExtendedKeyAccountCreationInfo>(
                    [self, accountIndex]() -> api::ExtendedKeyAccountCreationInfo {
                        api::ExtendedKeyAccountCreationInfo info;
                        auto scheme = self->getDerivationScheme();
                        info.index = getAccountIndex(scheme, accountIndex);
                        scheme.setCoinType(self->getCurrency().bip44CoinType).setAccountIndex(info.index);
                        auto keychainEngine = self->getConfiguration()->getString(
                                api::Configuration::KEYCHAIN_ENGINE).value_or(
                                api::ConfigurationDefaults::DEFAULT_KEYCHAIN);
                        if (keychainEngine == api::KeychainEngines::BIP32_P2PKH ||
                            keychainEngine == api::KeychainEngines::BIP49_P2SH) {
                            info.derivations.push_back(getAccountScheme(scheme).getPath().toString());
                            info.owners.push_back(std::string("main"));
                        } else {
                            throw make_exception(api::ErrorCode::IMPLEMENTATION_IS_MISSING,
                                                 "No implementation found found for keychain {}", keychainEngine);
                        }

                        return info;
                    });
        }

        Future<api::AccountCreationInfo> RippleLikeWallet::getAccountCreationInfo(int32_t accountIndex) {
            auto self = std::dynamic_pointer_cast<RippleLikeWallet>(shared_from_this());
            return getExtendedKeyAccountCreationInfo(accountIndex).map<api::AccountCreationInfo>(getContext(), [self, accountIndex](const api::ExtendedKeyAccountCreationInfo info) {
                api::AccountCreationInfo result;
                result.index = getAccountIndex(self->getDerivationScheme(), accountIndex);
                auto length = info.derivations.size();
                for (auto i = 0; i < length; i++) {
                    DerivationPath path(info.derivations[i]);
                    auto owner = info.owners[i];
                    result.derivations.push_back(path.toString());
                    result.owners.push_back(owner);
                 }
                 return result;
            });
        }

        std::shared_ptr<RippleLikeWallet> RippleLikeWallet::getSelf() {
            return std::dynamic_pointer_cast<RippleLikeWallet>(shared_from_this());
        }

        std::shared_ptr<AbstractAccount>
        RippleLikeWallet::createAccountInstance(soci::session &sql, const std::string &accountUid) {
            RippleLikeAccountDatabaseEntry entry;
            RippleLikeAccountDatabaseHelper::queryAccount(sql, accountUid, entry);
            auto scheme = getDerivationScheme();
            scheme.setCoinType(getCurrency().bip44CoinType).setAccountIndex(entry.index);
            auto xpubPath = getAccountScheme(scheme).getPath();
            auto keychain = _keychainFactory->restore(entry.index, xpubPath, getConfig(), entry.address,
                                                      getAccountInternalPreferences(entry.index), getCurrency());
            return std::make_shared<RippleLikeAccount>(shared_from_this(),
                                                       entry.index,
                                                       _explorer,
                                                       _observer,
                                                       _synchronizerFactory(),
                                                       keychain);
        }

        std::shared_ptr<RippleLikeBlockchainExplorer> RippleLikeWallet::getBlockchainExplorer() {
            return _explorer;
        }

    }
}
<commit_msg>Fix XRP accounts from being created with account index = 0.<commit_after>/*
 *
 * RippleLikeWallet
 *
 * Created by El Khalil Bellakrid on 06/01/2019.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ledger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "RippleLikeWallet.h"

#include "RippleLikeAccount.h"

#include <algorithm>

#include <async/wait.h>
#include <api/ErrorCode.hpp>
#include <api/AccountCallback.hpp>
#include <api/ConfigurationDefaults.hpp>
#include <api/KeychainEngines.hpp>

#include <ripple/RippleLikeExtendedPublicKey.h>

#include <wallet/common/database/AccountDatabaseHelper.h>
#include <wallet/ripple/database/RippleLikeAccountDatabaseHelper.h>

namespace ledger {
    namespace core {

        const api::WalletType RippleLikeWallet::type = api::WalletType::ETHEREUM;

        RippleLikeWallet::RippleLikeWallet(const std::string &name,
                                           const std::shared_ptr<RippleLikeBlockchainExplorer> &explorer,
                                           const std::shared_ptr<RippleLikeBlockchainObserver> &observer,
                                           const std::shared_ptr<RippleLikeKeychainFactory> &keychainFactory,
                                           const RippleLikeAccountSynchronizerFactory &synchronizer,
                                           const std::shared_ptr<WalletPool> &pool, const api::Currency &network,
                                           const std::shared_ptr<DynamicObject> &configuration,
                                           const DerivationScheme &scheme
        )
                : AbstractWallet(name, network, pool, configuration, scheme) {
            _explorer = explorer;
            _observer = observer;
            _keychainFactory = keychainFactory;
            _synchronizerFactory = synchronizer;
        }

        bool RippleLikeWallet::isSynchronizing() {
            return false;
        }

        std::shared_ptr<api::EventBus> RippleLikeWallet::synchronize() {
            return nullptr;
        }

        FuturePtr<ledger::core::api::Account>
        RippleLikeWallet::newAccountWithInfo(const api::AccountCreationInfo &info) {
            if (info.chainCodes.size() != 1 || info.publicKeys.size() != 1 || info.owners.size() != 1)
                throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                     "Account creation info are inconsistent (only one public key is needed)");
            auto self = getSelf();
            return async<api::ExtendedKeyAccountCreationInfo>([self, info]() -> api::ExtendedKeyAccountCreationInfo {
                if (info.owners.size() != info.derivations.size() || info.owners.size() != info.chainCodes.size() ||
                    info.publicKeys.size() != info.owners.size())
                    throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                         "Account creation info are inconsistent (size of arrays differs)");
                api::ExtendedKeyAccountCreationInfo result;

                if (info.chainCodes[0].size() != 32 || info.publicKeys[0].size() != 33)
                    throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                         "Account creation info are inconsistent (contains invalid public key(s))");
                DerivationPath occurencePath(info.derivations[0]);

                auto xpub = RippleLikeExtendedPublicKey::fromRaw(
                        self->getCurrency(),
                        Option<std::vector<uint8_t>>(),
                        info.publicKeys[0],
                        info.chainCodes[0],
                        info.derivations[0]
                );
                result.owners.push_back(info.owners[0]);
                result.derivations.push_back(info.derivations[0]);
                result.extendedKeys.push_back(xpub->toBase58());
                result.index = info.index;
                return result;
            }).flatMap<std::shared_ptr<ledger::core::api::Account>>(getContext(),
                                                                    [self](const api::ExtendedKeyAccountCreationInfo &info) -> Future<std::shared_ptr<ledger::core::api::Account>> {
                                                                        return self->newAccountWithExtendedKeyInfo(
                                                                                info);
                                                                    });
        }

        static int32_t getAccountIndex(const DerivationScheme &dPath, int32_t index) {
            // Set account index only if account level not hardened,
            // otherwise keep the one defined in derivation scheme
            return dPath.getSchemeTo(DerivationSchemeLevel::ACCOUNT_INDEX).getPath().isLastChildHardened() ? dPath.getAccountIndex() : index;
        }

        FuturePtr<ledger::core::api::Account>
        RippleLikeWallet::newAccountWithExtendedKeyInfo(const api::ExtendedKeyAccountCreationInfo &info) {
            logger()->debug("Creating new XRP account (index = {}) with extended key info", info.index);

            if (info.extendedKeys.empty()) {
                throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
                                     "Empty extended keys passed to newAccountWithExtendedKeyInfo");
            }

            auto self = getSelf();
            auto scheme = getDerivationScheme();
            auto accountIndex = info.index;
            scheme.setCoinType(getCurrency().bip44CoinType).setAccountIndex(accountIndex);
            auto xpubPath = scheme.getSchemeTo(DerivationSchemeLevel::ACCOUNT_INDEX).getPath();
            return async<std::shared_ptr<api::Account> >([=]() -> std::shared_ptr<api::Account> {
                auto keychain = self->_keychainFactory->build(
                        accountIndex,
                        xpubPath,
                        getConfig(),
                        info,
                        getAccountInternalPreferences(accountIndex),
                        getCurrency()
                );
                soci::session sql(self->getDatabase()->getPool());
                soci::transaction tr(sql);
                auto accountUid = AccountDatabaseHelper::createAccountUid(self->getWalletUid(), accountIndex);
                if (AccountDatabaseHelper::accountExists(sql, self->getWalletUid(), accountIndex))
                    throw make_exception(api::ErrorCode::ACCOUNT_ALREADY_EXISTS,
                                         "Account {}, for wallet '{}', already exists", accountIndex, self->getWalletUid());
                AccountDatabaseHelper::createAccount(sql, self->getWalletUid(), accountIndex);
                RippleLikeAccountDatabaseHelper::createAccount(sql, self->getWalletUid(), accountIndex,
                                                               info.extendedKeys[info.extendedKeys.size() - 1]);
                tr.commit();
                auto account = std::static_pointer_cast<api::Account>(std::make_shared<RippleLikeAccount>(
                        self->shared_from_this(),
                        accountIndex,
                        self->_explorer,
                        self->_observer,
                        self->_synchronizerFactory(),
                        keychain
                ));
                self->addAccountInstanceToInstanceCache(std::dynamic_pointer_cast<AbstractAccount>(account));
                return account;
            });
        }

        static DerivationScheme getAccountScheme(DerivationScheme &scheme) {
            auto accountScheme = scheme.getSchemeTo(DerivationSchemeLevel::ACCOUNT_INDEX);
            // To handle all exotic paths we should avoid private derivations
            // So if node or/and address level are hardened, then they are included in account's derivation path
            auto path = scheme.getPath();
            auto hardenedDepth = path.getDepth();
            while (hardenedDepth) {
                if (path.isHardened(hardenedDepth - 1)) {
                    break;
                }
                hardenedDepth--;
            }
            auto lastHardenedScheme = scheme.getSchemeToDepth(hardenedDepth);
            return accountScheme.getPath().getDepth() > lastHardenedScheme.getPath().getDepth() ? accountScheme : lastHardenedScheme;
        }

        Future<api::ExtendedKeyAccountCreationInfo>
        RippleLikeWallet::getExtendedKeyAccountCreationInfo(int32_t accountIndex) {
            auto self = std::dynamic_pointer_cast<RippleLikeWallet>(shared_from_this());
            return async<api::ExtendedKeyAccountCreationInfo>(
                    [self, accountIndex]() -> api::ExtendedKeyAccountCreationInfo {
                        api::ExtendedKeyAccountCreationInfo info;
                        auto scheme = self->getDerivationScheme();
                        info.index = getAccountIndex(scheme, accountIndex);
                        scheme.setCoinType(self->getCurrency().bip44CoinType).setAccountIndex(info.index);
                        auto keychainEngine = self->getConfiguration()->getString(
                                api::Configuration::KEYCHAIN_ENGINE).value_or(
                                api::ConfigurationDefaults::DEFAULT_KEYCHAIN);
                        if (keychainEngine == api::KeychainEngines::BIP32_P2PKH ||
                            keychainEngine == api::KeychainEngines::BIP49_P2SH) {
                            info.derivations.push_back(getAccountScheme(scheme).getPath().toString());
                            info.owners.push_back(std::string("main"));
                        } else {
                            throw make_exception(api::ErrorCode::IMPLEMENTATION_IS_MISSING,
                                                 "No implementation found found for keychain {}", keychainEngine);
                        }

                        return info;
                    });
        }

        Future<api::AccountCreationInfo> RippleLikeWallet::getAccountCreationInfo(int32_t accountIndex) {
            auto self = std::dynamic_pointer_cast<RippleLikeWallet>(shared_from_this());
            return getExtendedKeyAccountCreationInfo(accountIndex).map<api::AccountCreationInfo>(getContext(), [self, accountIndex](const api::ExtendedKeyAccountCreationInfo info) {
                api::AccountCreationInfo result;
                result.index = getAccountIndex(self->getDerivationScheme(), accountIndex);
                auto length = info.derivations.size();
                for (auto i = 0; i < length; i++) {
                    DerivationPath path(info.derivations[i]);
                    auto owner = info.owners[i];
                    result.derivations.push_back(path.toString());
                    result.owners.push_back(owner);
                 }
                 return result;
            });
        }

        std::shared_ptr<RippleLikeWallet> RippleLikeWallet::getSelf() {
            return std::dynamic_pointer_cast<RippleLikeWallet>(shared_from_this());
        }

        std::shared_ptr<AbstractAccount>
        RippleLikeWallet::createAccountInstance(soci::session &sql, const std::string &accountUid) {
            RippleLikeAccountDatabaseEntry entry;
            RippleLikeAccountDatabaseHelper::queryAccount(sql, accountUid, entry);
            auto scheme = getDerivationScheme();
            scheme.setCoinType(getCurrency().bip44CoinType).setAccountIndex(entry.index);
            auto xpubPath = getAccountScheme(scheme).getPath();
            auto keychain = _keychainFactory->restore(entry.index, xpubPath, getConfig(), entry.address,
                                                      getAccountInternalPreferences(entry.index), getCurrency());
            return std::make_shared<RippleLikeAccount>(shared_from_this(),
                                                       entry.index,
                                                       _explorer,
                                                       _observer,
                                                       _synchronizerFactory(),
                                                       keychain);
        }

        std::shared_ptr<RippleLikeBlockchainExplorer> RippleLikeWallet::getBlockchainExplorer() {
            return _explorer;
        }

    }
}
<|endoftext|>


<commit_before>#include "csparser.h"
#include "cutscenes_defaults.h"

#include <fstream>
#include <cstring> //strtok
#include <iterator> //istreambuf_iterator
#include <cstdlib>
#include <algorithm> //ererase
#include <sstream>

CsParser::CsParser(const std::vector<std::string> &imagePaths) :
    _image_paths(imagePaths),
    _version(0),
    _defaults_file_path(""),
    _error_message("")
{
}

bool CsParser::parse(const std::string &csPath)
{
    std::ifstream file;
    file.open(csPath.c_str(), std::ios_base::in);
    std::istreambuf_iterator<char> i(file.rdbuf());
    if (!parseHeader(i)){
        _error_message = "error parsing header";
        return false;
    }
    if (_defaults_file_path != ""){
        if (!parseDefaults()){
            _error_message = "error parsing "+_defaults_file_path;
            return false;
        }
    }
    if (!parseDeclarations(i)){
        _error_message = "error parsing declarations";
        return false;
    }
    if (!parseAnimations(i)){
        _error_message = "error parsing animations";
        return false;
    }
    return true;
}

std::string CsParser::getErrorMessage() const
{
    return _error_message;
}

std::vector<CsObject> CsParser::objects() const
{
    return _objects;
}

std::map<std::string, CsStep> CsParser::steps() const
{
    return _steps;
}

std::vector<std::string> CsParser::startingSteps() const
{
    return _starting_steps;
}

bool CsParser::parseInt(const std::string &in, int *out)
{
    std::string found;
    if (!regexMatchSingle("[+-]?[0-9]+", in, &found))
        return false;
    *out = atoi(found.c_str());
    return true;
}

bool CsParser::parseFloat(const std::string &in, float *out)
{
    std::string found;
    if (!regexMatchSingle("[-+]?[0-9]*\\.?[0-9]*", in, &found))
        return false;
    char *endptr;
    *out = strtof(found.c_str(), &endptr);
    if (endptr != (found.c_str() + found.length()))
        return false;
    return true;
}

bool CsParser::parseString(const std::string &in, std::string *out)
{
    *out = "";
    std::string::const_iterator i;
    for (i = in.begin(); i != in.end(); ++i){
        if ((*i) == ' '){
            if ((i+1) != in.end() &&
                    *(i+1) != ' ' &&
                    *out != "")
                return false;
            continue;
        }
        *out += (*i);
    }
    if (*out == "")
        return false;
    return true;
}

bool CsParser::parseQuotedString(const std::string &in, std::string *out)
{
    if (!regexMatchSingle("\"[^\"]*\"", in, out))
        return false;
    strip(*out, '"');
    return true;
}

bool CsParser::parseState(std::string *in, CsState *s)
{
    strip(*in, ' ');
    while (in->length() > 0) {
        std::string token = removeFirstSlice(in, ",");
        std::string var;
        if (token == ""){
            token = *in;
            in->clear();
        }
        var = removeFirstSlice(&token, ":");

        if (var == "")
            break;
        if (var != "x" &&
                var != "y" &&
                var != "width" &&
                var != "height" &&
                var != "alpha"){
            return false;
        }
        std::string stateName = var;
        float stateValue;
        if (!parseFloat(token, &stateValue))
            return false;
        (*s)[stateName] = stateValue;
    }
    return true;
}

std::string CsParser::readline(std::istreambuf_iterator<char> &i)
{
    std::istreambuf_iterator<char> eof;
    std::string line = "";
    while (i != eof && (*i) != '\n'){
        line += (*i);
        ++i;
    }
    ++i;
    return line;
}

void CsParser::strip(std::string &string, const char expr)
{
    string.erase(std::remove(string.begin(), string.end(), expr), string.end());
}

std::string CsParser::removeFirstSlice(std::string *in, const std::string &separator)
{
    std::string result;
    size_t c_pos = findUnquoted(*in, separator);
    if (c_pos == std::string::npos)
        return "";
    result = in->substr(0, c_pos);
    *in = in->substr(c_pos + separator.length());
    return result;
}

bool CsParser::regexMatchSingle(const std::string &expression, const std::string &input, std::string *output)
{
    TRexpp regex;
    const char *found = "";
    const char *last = "";

    regex.Compile(expression.c_str());
    regex.Search(input.c_str(), &found, &last);

    std::string last_str(last);
    if (last_str.length() > 0){
        strip(last_str, ' ');
        if (last_str.length() > 0){
            return false;
        }
    }

    output->assign(found);
    if (output->length() > 0)
        return true;
    return false;
}

bool CsParser::parseHeader(std::istreambuf_iterator<char> &i)
{
    std::istreambuf_iterator<char> eof;
    std::string line = "";

    while (true){
        if (i == eof)
            return false;
        line = readline(i);
        if (line.substr(0,3) == "###")
            break;
    }
    //getting version number and definitions file
    line = line.substr(3);
    strip(line, ' ');
    std::string version = removeFirstSlice(&line, ",");
    if(version == ""){
        return parseInt(line, &_version);
    }
    if (!parseInt(version, &_version))
        return false;
    if (!parseQuotedString(line, &_defaults_file_path))
        return false;
    return true;
}

bool CsParser::parseDeclaration(std::string d_string)
{
    CsObject obj;
    if (!parseString(removeFirstSlice(&d_string, ":"), &(obj.name))){
        strip(d_string, ' ');
        if (d_string.length() > 0)
            return false;
        return true;
    }
    if (!parseQuotedString(removeFirstSlice(&d_string, ","), &(obj.content)))
        return false;
    obj.isText = false;
    std::vector<std::string>::const_iterator i = find(_image_paths.begin(), _image_paths.end(), obj.content);
    if (i == _image_paths.end())
        obj.isText = true;
    strip(d_string, ' ');
    replaceWDefaults(d_string);
    if (!parseState(&d_string, &(obj.state)))
        return false;
    if(d_string.length() > 0)
        return false;
    _objects.push_back(obj);
    return true;
}

bool CsParser::parseDeclarations(std::istreambuf_iterator<char> &i)
{
    std::string line  = "";
    std::istreambuf_iterator<char> eof;
    while(i != eof){
        line = readline(i);
        if (line.find("---") != std::string::npos)
            break;
        if (!parseDeclaration(line))
            return false;
    }
    return true;
}

bool CsParser::parseStepContent(std::string &line, CsStep &step)
{
    std::string objectName;
    if (!parseString(removeFirstSlice(&line, ":"), &objectName)){
        strip(line, ' ');
        if (line != "")
            return false;
        return true;
    }

    strip(line, ' ');
    replaceWDefaults(line);

    CsState s;
    if (!isObject(objectName))
        return false;
    if (!parseState(&line, &s))
        return false;
    step.objStates[objectName] = s;
    return true;
}

bool CsParser::parseSubSteps(std::istreambuf_iterator<char> &i,
                             std::string &line,
                             const std::string &name,
                             int *n)
{
    std::istreambuf_iterator<char> eof;
    line = readline(i);
    while(i != eof){
        CsStep step;
        step.duration = -1;
        if (line.find("->") != std::string::npos)
            break;
        if (line.find(":") != std::string::npos)
            return false;
        strip(line, ' ');
        if (line == ""){
            line = readline(i);
            continue;
        }
        if (!parseInt(line, &(step.duration)))
            return false;
        line = readline(i);
        while(i != eof &&
              line.find(":") != std::string::npos &&
              line.find("->") == std::string::npos){
            if (!parseStepContent(line, step))
                return false;
            line = readline(i);
        }
        if (*n > 0){
            _steps[name+intToString((*n)-1)].next.push_back(name+intToString(*n));
        }
        _steps[name+intToString(*n)] = step;
        (*n)++;
    }
    (*n)--;
    return true;
}

bool CsParser::parseAnimations(std::istreambuf_iterator<char> &i)
{
    std::istreambuf_iterator<char> eof;
    std::map<std::string, int> steps_length;
    std::string line = readline(i);
    while (i != eof){
        if (line.substr(0, 2) != "->"){
            strip(line, ' ');
            if (line != "")
                return false;
            line = readline(i);
            continue;
        }
        line = line.substr(2);

        //parsing step header
        std::string name_str = removeFirstSlice(&line, ",");
        std::string prev = "";
        if (name_str != ""){
            if(!parseString(line, &prev))
                return false;
        } else {
            name_str = line;
        }
        std::string name;
        if (!parseString(name_str, &name))
            return false;

        int stepNumber = 0;
        if (!parseSubSteps(i, line, name, &stepNumber))
            return false;
        steps_length[name] = stepNumber;

        if (prev != ""){
            std::map<std::string, CsStep>::iterator i =_steps.find(prev+intToString(steps_length[prev]));
            if (i == _steps.end())
                return false;
            (*i).second.next.push_back(name+"0");
        } else {
            _starting_steps.push_back(name+"0");
        }
    }
    return true;
}

void CsParser::mapReplace(std::map<std::string, std::string> m, std::string &string)
{
    std::map<std::string, std::string>::iterator i;
    for (i = m.begin(); i != m.end(); ++i){
        size_t start_pos = 0;
        while((start_pos = string.find((*i).first, start_pos)) != std::string::npos)
            string.replace(start_pos, (*i).first.length(), (*i).second);
    }
}

void CsParser::replaceWDefaults(std::string &string)
{
    mapReplace(_defaults, string);
    mapReplace(Defaults::declarations, string);
}

bool CsParser::parseDefaults()
{
    std::ifstream file;
    file.open(_defaults_file_path.c_str(), std::ios_base::in);
    std::istreambuf_iterator<char> i(file.rdbuf());
    std::istreambuf_iterator<char> eof;

    std::string line = "";
    while (i != eof){
        line = readline(i);
        strip(line, ' ');
        size_t arrow_pos = line.find("=>");
        if (arrow_pos == std::string::npos){
            if (line.length() > 0)
                return false;
            continue;
        }

        std::string var = "";
        if (!parseString(line.substr(0, arrow_pos), &var))
            return false;
        _defaults[var] = line.substr(arrow_pos + 2);
    }
    return true;
}

bool CsParser::isObject(const std::string &name)
{
    std::vector<CsObject>::iterator i;
    for (i = _objects.begin(); i != _objects.end(); ++i){
        if ((*i).name == name)
            return true;
    }
    return false;
}

size_t findUnquoted(const std::string &input, const std::string &toFind)
{
    int n = input.length();
    int m = toFind.length();
    size_t i = 0;
    while (int(i) < n-m){
        if (input.at(i) == '"'){
            ++i;
            while (input.at(i) != '"' && int(i) < n-m)
                ++i;
        }
        int j = 0;
        while (j < m && input.at(i+j) == toFind.at(j))
            ++j;
        if (j == m)
            return i;
        i++;
    }
    return std::string::npos;
}

std::string intToString(int i)
{
    std::stringstream ss;
    std::string s;
    ss << i;
    s = ss.str();

    return s;
}
<commit_msg>parser sops after the last line<commit_after>#include "csparser.h"
#include "cutscenes_defaults.h"

#include <fstream>
#include <cstring> //strtok
#include <iterator> //istreambuf_iterator
#include <cstdlib>
#include <algorithm> //ererase
#include <sstream>

CsParser::CsParser(const std::vector<std::string> &imagePaths) :
    _image_paths(imagePaths),
    _version(0),
    _defaults_file_path(""),
    _error_message("")
{
}

bool CsParser::parse(const std::string &csPath)
{
    std::ifstream file;
    file.open(csPath.c_str(), std::ios_base::in);
    std::istreambuf_iterator<char> i(file.rdbuf());
    if (!parseHeader(i)){
        _error_message = "error parsing header";
        return false;
    }
    if (_defaults_file_path != ""){
        if (!parseDefaults()){
            _error_message = "error parsing "+_defaults_file_path;
            return false;
        }
    }
    if (!parseDeclarations(i)){
        _error_message = "error parsing declarations";
        return false;
    }
    if (!parseAnimations(i)){
        _error_message = "error parsing animations";
        return false;
    }
    return true;
}

std::string CsParser::getErrorMessage() const
{
    return _error_message;
}

std::vector<CsObject> CsParser::objects() const
{
    return _objects;
}

std::map<std::string, CsStep> CsParser::steps() const
{
    return _steps;
}

std::vector<std::string> CsParser::startingSteps() const
{
    return _starting_steps;
}

bool CsParser::parseInt(const std::string &in, int *out)
{
    std::string found;
    if (!regexMatchSingle("[+-]?[0-9]+", in, &found))
        return false;
    *out = atoi(found.c_str());
    return true;
}

bool CsParser::parseFloat(const std::string &in, float *out)
{
    std::string found;
    if (!regexMatchSingle("[-+]?[0-9]*\\.?[0-9]*", in, &found))
        return false;
    char *endptr;
    *out = strtof(found.c_str(), &endptr);
    if (endptr != (found.c_str() + found.length()))
        return false;
    return true;
}

bool CsParser::parseString(const std::string &in, std::string *out)
{
    *out = "";
    std::string::const_iterator i;
    for (i = in.begin(); i != in.end(); ++i){
        if ((*i) == ' '){
            if ((i+1) != in.end() &&
                    *(i+1) != ' ' &&
                    *out != "")
                return false;
            continue;
        }
        *out += (*i);
    }
    if (*out == "")
        return false;
    return true;
}

bool CsParser::parseQuotedString(const std::string &in, std::string *out)
{
    if (!regexMatchSingle("\"[^\"]*\"", in, out))
        return false;
    strip(*out, '"');
    return true;
}

bool CsParser::parseState(std::string *in, CsState *s)
{
    strip(*in, ' ');
    while (in->length() > 0) {
        std::string token = removeFirstSlice(in, ",");
        std::string var;
        if (token == ""){
            token = *in;
            in->clear();
        }
        var = removeFirstSlice(&token, ":");

        if (var == "")
            break;
        if (var != "x" &&
                var != "y" &&
                var != "width" &&
                var != "height" &&
                var != "alpha"){
            return false;
        }
        std::string stateName = var;
        float stateValue;
        if (!parseFloat(token, &stateValue))
            return false;
        (*s)[stateName] = stateValue;
    }
    return true;
}

std::string CsParser::readline(std::istreambuf_iterator<char> &i)
{
    std::istreambuf_iterator<char> eof;
    std::string line = "";
    while (i != eof && (*i) != '\n'){
        line += (*i);
        ++i;
    }
    ++i;
    return line;
}

void CsParser::strip(std::string &string, const char expr)
{
    string.erase(std::remove(string.begin(), string.end(), expr), string.end());
}

std::string CsParser::removeFirstSlice(std::string *in, const std::string &separator)
{
    std::string result;
    size_t c_pos = findUnquoted(*in, separator);
    if (c_pos == std::string::npos)
        return "";
    result = in->substr(0, c_pos);
    *in = in->substr(c_pos + separator.length());
    return result;
}

bool CsParser::regexMatchSingle(const std::string &expression, const std::string &input, std::string *output)
{
    TRexpp regex;
    const char *found = "";
    const char *last = "";

    regex.Compile(expression.c_str());
    regex.Search(input.c_str(), &found, &last);

    std::string last_str(last);
    if (last_str.length() > 0){
        strip(last_str, ' ');
        if (last_str.length() > 0){
            return false;
        }
    }

    output->assign(found);
    if (output->length() > 0)
        return true;
    return false;
}

bool CsParser::parseHeader(std::istreambuf_iterator<char> &i)
{
    std::istreambuf_iterator<char> eof;
    std::string line = "";

    while (true){
        if (i == eof)
            return false;
        line = readline(i);
        if (line.substr(0,3) == "###")
            break;
    }
    //getting version number and definitions file
    line = line.substr(3);
    strip(line, ' ');
    std::string version = removeFirstSlice(&line, ",");
    if(version == ""){
        return parseInt(line, &_version);
    }
    if (!parseInt(version, &_version))
        return false;
    if (!parseQuotedString(line, &_defaults_file_path))
        return false;
    return true;
}

bool CsParser::parseDeclaration(std::string d_string)
{
    CsObject obj;
    if (!parseString(removeFirstSlice(&d_string, ":"), &(obj.name))){
        strip(d_string, ' ');
        if (d_string.length() > 0)
            return false;
        return true;
    }
    if (!parseQuotedString(removeFirstSlice(&d_string, ","), &(obj.content)))
        return false;
    obj.isText = false;
    std::vector<std::string>::const_iterator i = find(_image_paths.begin(), _image_paths.end(), obj.content);
    if (i == _image_paths.end())
        obj.isText = true;
    strip(d_string, ' ');
    replaceWDefaults(d_string);
    if (!parseState(&d_string, &(obj.state)))
        return false;
    if(d_string.length() > 0)
        return false;
    _objects.push_back(obj);
    return true;
}

bool CsParser::parseDeclarations(std::istreambuf_iterator<char> &i)
{
    std::string line  = "";
    std::istreambuf_iterator<char> eof;
    while(i != eof){
        line = readline(i);
        if (line.find("---") != std::string::npos)
            break;
        if (!parseDeclaration(line))
            return false;
    }
    return true;
}

bool CsParser::parseStepContent(std::string &line, CsStep &step)
{
    std::string objectName;
    if (!parseString(removeFirstSlice(&line, ":"), &objectName)){
        strip(line, ' ');
        if (line != "")
            return false;
        return true;
    }

    strip(line, ' ');
    replaceWDefaults(line);

    CsState s;
    if (!isObject(objectName))
        return false;
    if (!parseState(&line, &s))
        return false;
    step.objStates[objectName] = s;
    return true;
}

bool CsParser::parseSubSteps(std::istreambuf_iterator<char> &i,
                             std::string &line,
                             const std::string &name,
                             int *n)
{
    std::istreambuf_iterator<char> eof;
    line = readline(i);
    while(i != eof){
        CsStep step;
        step.duration = -1;
        if (line.find("->") != std::string::npos)
            break;
        if (line.find(":") != std::string::npos)
            return false;
        strip(line, ' ');
        if (line == ""){
            line = readline(i);
            continue;
        }
        if (!parseInt(line, &(step.duration)))
            return false;
        line = readline(i);
        while(line.find(":") != std::string::npos &&
              line.find("->") == std::string::npos){
            if (!parseStepContent(line, step))
                return false;
            if (i == eof)
                break;
            line = readline(i);
        }
        if (*n > 0){
            _steps[name+intToString((*n)-1)].next.push_back(name+intToString(*n));
        }
        _steps[name+intToString(*n)] = step;
        (*n)++;
    }
    (*n)--;
    return true;
}

bool CsParser::parseAnimations(std::istreambuf_iterator<char> &i)
{
    std::istreambuf_iterator<char> eof;
    std::map<std::string, int> steps_length;
    std::string line = readline(i);
    while (i != eof){
        if (line.substr(0, 2) != "->"){
            strip(line, ' ');
            if (line != "")
                return false;
            line = readline(i);
            continue;
        }
        line = line.substr(2);

        //parsing step header
        std::string name_str = removeFirstSlice(&line, ",");
        std::string prev = "";
        if (name_str != ""){
            if(!parseString(line, &prev))
                return false;
        } else {
            name_str = line;
        }
        std::string name;
        if (!parseString(name_str, &name))
            return false;

        int stepNumber = 0;
        if (!parseSubSteps(i, line, name, &stepNumber))
            return false;
        steps_length[name] = stepNumber;

        if (prev != ""){
            std::map<std::string, CsStep>::iterator i =_steps.find(prev+intToString(steps_length[prev]));
            if (i == _steps.end())
                return false;
            (*i).second.next.push_back(name+"0");
        } else {
            _starting_steps.push_back(name+"0");
        }
    }
    return true;
}

void CsParser::mapReplace(std::map<std::string, std::string> m, std::string &string)
{
    std::map<std::string, std::string>::iterator i;
    for (i = m.begin(); i != m.end(); ++i){
        size_t start_pos = 0;
        while((start_pos = string.find((*i).first, start_pos)) != std::string::npos)
            string.replace(start_pos, (*i).first.length(), (*i).second);
    }
}

void CsParser::replaceWDefaults(std::string &string)
{
    mapReplace(_defaults, string);
    mapReplace(Defaults::declarations, string);
}

bool CsParser::parseDefaults()
{
    std::ifstream file;
    file.open(_defaults_file_path.c_str(), std::ios_base::in);
    std::istreambuf_iterator<char> i(file.rdbuf());
    std::istreambuf_iterator<char> eof;

    std::string line = "";
    while (i != eof){
        line = readline(i);
        strip(line, ' ');
        size_t arrow_pos = line.find("=>");
        if (arrow_pos == std::string::npos){
            if (line.length() > 0)
                return false;
            continue;
        }

        std::string var = "";
        if (!parseString(line.substr(0, arrow_pos), &var))
            return false;
        _defaults[var] = line.substr(arrow_pos + 2);
    }
    return true;
}

bool CsParser::isObject(const std::string &name)
{
    std::vector<CsObject>::iterator i;
    for (i = _objects.begin(); i != _objects.end(); ++i){
        if ((*i).name == name)
            return true;
    }
    return false;
}

size_t findUnquoted(const std::string &input, const std::string &toFind)
{
    int n = input.length();
    int m = toFind.length();
    size_t i = 0;
    while (int(i) < n-m){
        if (input.at(i) == '"'){
            ++i;
            while (input.at(i) != '"' && int(i) < n-m)
                ++i;
        }
        int j = 0;
        while (j < m && input.at(i+j) == toFind.at(j))
            ++j;
        if (j == m)
            return i;
        i++;
    }
    return std::string::npos;
}

std::string intToString(int i)
{
    std::stringstream ss;
    std::string s;
    ss << i;
    s = ss.str();

    return s;
}
<|endoftext|>