<commit_before>/*
   Copyright (C) 2009 Red Hat, Inc.

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

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

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#include "common.h"
#include "controller.h"
#include <spice/controller_prot.h>
#include "cmd_line_parser.h"
#include "menu.h"
#include "utils.h"
#include "debug.h"
#include "platform.h"

#define PIPE_NAME_MAX_LEN 50

#ifdef WIN32
#define PIPE_NAME "SpiceController-%lu"
#endif

Controller::Controller(ControllerInterface *handler)
    : _handler (handler)
    , _exclusive (false)
    , _refs (1)
{
    char pipe_name[PIPE_NAME_MAX_LEN];

    ASSERT(_handler);
#ifdef WIN32
    snprintf(pipe_name, PIPE_NAME_MAX_LEN, PIPE_NAME, Platform::get_process_id());
#else
    char *p_socket = getenv("SPICE_XPI_SOCKET");
    if (!p_socket) {
        LOG_ERROR("Failed to get a controller connection (SPICE_XPI_SOCKET)");
    }
    strncpy(pipe_name, p_socket, sizeof(pipe_name));
#endif
    LOG_INFO("Creating a controller connection %s", pipe_name);
    _pipe = NamedPipe::create(pipe_name, *this);
    if (!_pipe) {
        LOG_ERROR("Failed to create a controller connection");
    }
}

Controller::~Controller()
{
    std::map<NamedPipe::ConnectionRef, ControllerConnection*>::const_iterator conn;
    for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
        conn->second->reset_handler();
        delete conn->second;
    }
    if (_pipe) {
        NamedPipe::destroy(_pipe);
    }
}

NamedPipe::ConnectionInterface& Controller::create()
{
    ControllerConnection *conn = new ControllerConnection(_handler, *this);

    if (conn == NULL) {
        throw Exception("Error allocating a new controller connection");
    }
    return *conn;
}

bool Controller::set_exclusive(bool exclusive)
{
    if (_exclusive) {
        LOG_ERROR("Cannot init controller, an exclusive controller already connected");
        return false;
    }
    if (exclusive && _connections.size() > 1) {
        LOG_ERROR("Cannot init exclusive controller, other controllers already connected");
        return false;
    }
    _exclusive = exclusive;
    return true;
}

void Controller::add_connection(NamedPipe::ConnectionRef conn_ref, ControllerConnection *conn)
{
    _connections[conn_ref] = conn;
    conn->on_data();
}

void Controller::remove_connection(NamedPipe::ConnectionRef conn_ref)
{
    ControllerConnection *conn = _connections[conn_ref];
    _connections.erase(conn_ref);
    _exclusive = false;
    delete conn;
}

void Controller::on_command(NamedPipe::ConnectionRef conn_ref, int32_t id)
{
    ControllerConnection *conn = _connections[conn_ref];
    ControllerValue msg;

    ASSERT(conn);
    msg.base.id = CONTROLLER_MENU_ITEM_CLICK;
    msg.base.size = sizeof(msg);
    msg.value = id;
    conn->write_msg(&msg.base, msg.base.size);
}

ControllerConnection::ControllerConnection(ControllerInterface *handler, Controller& parent)
    : _handler (handler)
    , _parent (parent)
    , _initialized (false)
    , _write_pending (0)
    , _write_pos (_write_buf)
    , _read_pos (_read_buf)
    , _port (-1)
    , _sport (-1)
    , _full_screen (false)
    , _auto_display_res (false)
{
}

ControllerConnection::~ControllerConnection()
{
    if (_opaque != NamedPipe::INVALID_CONNECTION) {
        NamedPipe::destroy_connection(_opaque);
    }
    if (_handler) {
        _handler->clear_menu_items(_opaque);
        _handler->delete_menu();
    }
}

void ControllerConnection::bind(NamedPipe::ConnectionRef conn_ref)
{
    _opaque = conn_ref;
    _parent.add_connection(conn_ref, this);
}

void ControllerConnection::on_data()
{
    if (_write_pending) {
        LOG_INFO("Resume pending write %d", _write_pending);
        if (!write_msg(_write_pos, _write_pending)) {
            return;
        }
    }
    while (read_msgs());
}

bool ControllerConnection::read_msgs()
{
    uint8_t *pos = _read_buf;
    size_t nread = _read_pos - _read_buf;
    int32_t size;

    ASSERT(_handler);
    ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
    size = NamedPipe::read(_opaque, (uint8_t*)_read_pos, sizeof(_read_buf) - nread);
    if (size == 0) {
        return false;
    } else if (size < 0) {
        LOG_ERROR("Error reading from named pipe %d", size);
        _parent.remove_connection(_opaque);
        return false;
    }
    nread += size;
    while (nread > 0) {
        if (!_initialized && nread >= sizeof(ControllerInitHeader)) {
            ControllerInitHeader *init = (ControllerInitHeader *)pos;
            if (init->magic != CONTROLLER_MAGIC || init->version != CONTROLLER_VERSION) {
                LOG_ERROR("Bad controller init, magic=0x%x version=%u", init->magic,
                          init->version);
                _parent.remove_connection(_opaque);
                return false;
            }
            if (nread < init->size) {
                break;
            }
            if (!handle_init((ControllerInit*)init)) {
                _parent.remove_connection(_opaque);
                return false;
            }
            nread -= init->size;
            pos += init->size;
            _initialized = true;
        }
        if (!_initialized || nread < sizeof(ControllerMsg)) {
            break;
        }
        ControllerMsg *hdr = (ControllerMsg *)pos;
        if (hdr->size < sizeof(ControllerMsg)) {
            LOG_ERROR("Bad controller message, size=%u", hdr->size);
            _parent.remove_connection(_opaque);
            return false;
        }
        if (nread < hdr->size) {
            break;
        }
        handle_message(hdr);
        nread -= hdr->size;
        pos += hdr->size;
    }
    if (nread > 0 && pos != _read_buf) {
        memcpy(_read_buf, pos, nread);
    }
    _read_pos = _read_buf + nread;
    return true;
}

bool ControllerConnection::write_msg(const void *buf, int len)
{
    RecurciveLock lock(_write_lock);
    uint8_t *pos;
    int32_t written = 0;

    ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
    if (_write_pending && buf != _write_pos) {
        if ((_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) &&
                                              !write_msg(_write_pos, _write_pending)) {
            return false;
        }
        if (_write_pending) {
            if (_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) {
                DBG(0, "Dropping message, due to insufficient space in write buffer");
                return true;
            }
            memcpy(_write_pos + _write_pending, buf, len);
            _write_pending += len;
        }
    }
    pos = (uint8_t*)buf;
    while (len && (written = NamedPipe::write(_opaque, pos, len)) > 0) {
        pos += written;
        len -= written;
    }
    if (len && written == 0) {
        if (_write_pending) {
            _write_pos = pos;
        } else {
            _write_pos = _write_buf;
            memcpy(_write_buf, pos, len);
        }
        _write_pending = len;
    } else if (written < 0) {
        LOG_ERROR("Error writing to named pipe %d", written);
        _parent.remove_connection(_opaque);
        return false;
    } else {
        _write_pending = 0;
    }
    return true;
}

bool ControllerConnection::handle_init(ControllerInit *init)
{
    ASSERT(_handler);
    if (init->credentials != 0) {
        LOG_ERROR("Controller menu has wrong credentials 0x%x", init->credentials);
        return false;
    }
    if (!_parent.set_exclusive(init->flags & CONTROLLER_FLAG_EXCLUSIVE)) {
        return false;
    }
    return true;
}

bool ControllerConnection::handle_message(ControllerMsg *hdr)
{
    uint32_t value = ((ControllerValue*)hdr)->value;
    char *data = (char*)((ControllerData*)hdr)->data;

    ASSERT(_handler);
    switch (hdr->id) {
    case CONTROLLER_HOST:
        _host.assign(data);
        break;
    case CONTROLLER_PORT:
        _port = value;
        break;
    case CONTROLLER_SPORT:
        _sport = value;
        break;
    case CONTROLLER_PASSWORD:
        _password.assign(data);
        break;
    case CONTROLLER_SECURE_CHANNELS:
    case CONTROLLER_DISABLE_CHANNELS:
        return set_multi_val(hdr->id, data);
    case CONTROLLER_TLS_CIPHERS:
        return _handler->set_connection_ciphers(data, "Controller");
    case CONTROLLER_CA_FILE:
        return _handler->set_ca_file(data, "Controller");
    case CONTROLLER_HOST_SUBJECT:
        return _handler->set_host_cert_subject(data, "Controller");
    case CONTROLLER_FULL_SCREEN:
        _full_screen = !!(value & CONTROLLER_SET_FULL_SCREEN);
        _handler->set_auto_display_res(!!(value & CONTROLLER_AUTO_DISPLAY_RES));
        break;
    case CONTROLLER_SET_TITLE: {
        std::string str;
        string_printf(str, "%s", data);
        _handler->set_title(str);
        break;
    }
    case CONTROLLER_HOTKEYS:
        _handler->set_hotkeys(data);
        break;
    case CONTROLLER_CONNECT:
        _handler->connect(_host, _port, _sport, _password);
        break;
    case CONTROLLER_SHOW:
        _handler->show_me(_full_screen);
        break;
    case CONTROLLER_HIDE:
        _handler->hide_me();
        break;
    case CONTROLLER_CREATE_MENU:
        return create_menu((char*)data);
    case CONTROLLER_DELETE_MENU:
        _handler->delete_menu();
        break;
    case CONTROLLER_SEND_CAD:
    default:
        LOG_ERROR("Ignoring an unknown controller message %u", hdr->id);
        return false;
    }
    return true;
}

#ifdef WIN32
#define next_tok(str, delim, state) strtok(str, delim)
#else
#define next_tok(str, delim, state) strtok_r(str, delim, state)
#endif

bool ControllerConnection::create_menu(char* resource)
{
    bool ret = true;
#ifndef WIN32
    char* item_state = 0;
#endif
    char* next_item;
    const char* param;
    const char* text;
    int parent_id;
    int flags;
    int state;
    int id;

    ASSERT(_handler);
    AutoRef<Menu> app_menu(_handler->get_app_menu());
    AutoRef<Menu> menu(new Menu((*app_menu)->get_target(), ""));
    char* item = next_tok(resource, CONTROLLER_MENU_ITEM_DELIMITER, &item_state);
    while (item != NULL) {
        next_item = item + strlen(item) + 1;
        ret = ret && (param = next_tok(item, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
              sscanf(param, "%d", &parent_id);
        ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
              sscanf(param, "%d", &id);
        ret = ret && (text = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state));
        ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
              sscanf(param, "%d", &flags);

        if (!ret) {
            DBG(0, "item parsing failed %s", item);
            break;
        }
        DBG(0, "parent_id=%d, id=%d, text=%s, flags=%d", parent_id, id, text, flags);

        AutoRef<Menu> sub_menu((*menu)->find_sub(parent_id));
        if (!(ret = !!*sub_menu)) {
            DBG(0, "submenu not found %s", item);
            break;
        }

        if (flags & CONTROLLER_MENU_FLAGS_SEPARATOR) {
            (*sub_menu)->add_separator();
        } else if (flags & CONTROLLER_MENU_FLAGS_POPUP) {
            AutoRef<Menu> sub(new Menu((*app_menu)->get_target(), text, id));
            (*sub_menu)->add_sub(sub.release());
        } else {
            state = 0;
            if (flags & (CONTROLLER_MENU_FLAGS_DISABLED | CONTROLLER_MENU_FLAGS_GRAYED)) {
                state |= Menu::MENU_ITEM_STATE_DIM;
            }
            if (flags & CONTROLLER_MENU_FLAGS_CHECKED) {
                state |= Menu::MENU_ITEM_STATE_CHECKED;
            }
            if (id >= SPICE_MENU_INTERNAL_ID_BASE) {
                id = ((id - SPICE_MENU_INTERNAL_ID_BASE) >> SPICE_MENU_INTERNAL_ID_SHIFT) + 1;
            } else {
                id = _handler->get_controller_menu_item_id(_opaque, id);
            }
            (*sub_menu)->add_command(text, id, state);
        }
        item = next_tok(next_item, CONTROLLER_MENU_ITEM_DELIMITER, &item_state);
    }
    if (ret) {
        _handler->set_menu(*menu);
    }
    return ret;
}

bool ControllerConnection::set_multi_val(uint32_t op, char* multi_val)
{
    CmdLineParser parser("", false);
    int id = CmdLineParser::OPTION_FIRST_AVILABLE;
    char* argv[] = {NULL, (char*)"--set", multi_val};
    char* val;

    ASSERT(_handler);
    parser.add(id, "set", "none", "none", true);
    parser.set_multi(id, ',');
    parser.begin(3, argv);
    if (parser.get_option(&val) != id) {
        return false;
    }
    switch (op) {
    case CONTROLLER_SECURE_CHANNELS:
        _handler->set_channels_security(parser, true, val, "Controller");
        break;
    case CONTROLLER_DISABLE_CHANNELS:
        _handler->set_enable_channels(parser, false, val, "Controller");
        break;
    default:
        DBG(0, "unsupported op %u", op);
        return false;
    }
    return true;
}
<commit_msg>client: exit nicely for --controller with no SPICE_XPI_SOCKET (rhbz#644292)<commit_after>/*
   Copyright (C) 2009 Red Hat, Inc.

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

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

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/

#include "common.h"
#include "controller.h"
#include <spice/controller_prot.h>
#include "cmd_line_parser.h"
#include "menu.h"
#include "utils.h"
#include "debug.h"
#include "platform.h"

#define PIPE_NAME_MAX_LEN 50

#ifdef WIN32
#define PIPE_NAME "SpiceController-%lu"
#endif

Controller::Controller(ControllerInterface *handler)
    : _handler (handler)
    , _exclusive (false)
    , _refs (1)
{
    char pipe_name[PIPE_NAME_MAX_LEN];

    ASSERT(_handler);
#ifdef WIN32
    snprintf(pipe_name, PIPE_NAME_MAX_LEN, PIPE_NAME, Platform::get_process_id());
#else
    char *p_socket = getenv("SPICE_XPI_SOCKET");
    if (!p_socket) {
        LOG_ERROR("Failed to get a controller connection (SPICE_XPI_SOCKET)");
        throw Exception("Failed to get a controller connection (SPICE_XPI_SOCKET)");
    }
    strncpy(pipe_name, p_socket, sizeof(pipe_name));
#endif
    LOG_INFO("Creating a controller connection %s", pipe_name);
    _pipe = NamedPipe::create(pipe_name, *this);
    if (!_pipe) {
        LOG_ERROR("Failed to create a controller connection");
    }
}

Controller::~Controller()
{
    std::map<NamedPipe::ConnectionRef, ControllerConnection*>::const_iterator conn;
    for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
        conn->second->reset_handler();
        delete conn->second;
    }
    if (_pipe) {
        NamedPipe::destroy(_pipe);
    }
}

NamedPipe::ConnectionInterface& Controller::create()
{
    ControllerConnection *conn = new ControllerConnection(_handler, *this);

    if (conn == NULL) {
        throw Exception("Error allocating a new controller connection");
    }
    return *conn;
}

bool Controller::set_exclusive(bool exclusive)
{
    if (_exclusive) {
        LOG_ERROR("Cannot init controller, an exclusive controller already connected");
        return false;
    }
    if (exclusive && _connections.size() > 1) {
        LOG_ERROR("Cannot init exclusive controller, other controllers already connected");
        return false;
    }
    _exclusive = exclusive;
    return true;
}

void Controller::add_connection(NamedPipe::ConnectionRef conn_ref, ControllerConnection *conn)
{
    _connections[conn_ref] = conn;
    conn->on_data();
}

void Controller::remove_connection(NamedPipe::ConnectionRef conn_ref)
{
    ControllerConnection *conn = _connections[conn_ref];
    _connections.erase(conn_ref);
    _exclusive = false;
    delete conn;
}

void Controller::on_command(NamedPipe::ConnectionRef conn_ref, int32_t id)
{
    ControllerConnection *conn = _connections[conn_ref];
    ControllerValue msg;

    ASSERT(conn);
    msg.base.id = CONTROLLER_MENU_ITEM_CLICK;
    msg.base.size = sizeof(msg);
    msg.value = id;
    conn->write_msg(&msg.base, msg.base.size);
}

ControllerConnection::ControllerConnection(ControllerInterface *handler, Controller& parent)
    : _handler (handler)
    , _parent (parent)
    , _initialized (false)
    , _write_pending (0)
    , _write_pos (_write_buf)
    , _read_pos (_read_buf)
    , _port (-1)
    , _sport (-1)
    , _full_screen (false)
    , _auto_display_res (false)
{
}

ControllerConnection::~ControllerConnection()
{
    if (_opaque != NamedPipe::INVALID_CONNECTION) {
        NamedPipe::destroy_connection(_opaque);
    }
    if (_handler) {
        _handler->clear_menu_items(_opaque);
        _handler->delete_menu();
    }
}

void ControllerConnection::bind(NamedPipe::ConnectionRef conn_ref)
{
    _opaque = conn_ref;
    _parent.add_connection(conn_ref, this);
}

void ControllerConnection::on_data()
{
    if (_write_pending) {
        LOG_INFO("Resume pending write %d", _write_pending);
        if (!write_msg(_write_pos, _write_pending)) {
            return;
        }
    }
    while (read_msgs());
}

bool ControllerConnection::read_msgs()
{
    uint8_t *pos = _read_buf;
    size_t nread = _read_pos - _read_buf;
    int32_t size;

    ASSERT(_handler);
    ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
    size = NamedPipe::read(_opaque, (uint8_t*)_read_pos, sizeof(_read_buf) - nread);
    if (size == 0) {
        return false;
    } else if (size < 0) {
        LOG_ERROR("Error reading from named pipe %d", size);
        _parent.remove_connection(_opaque);
        return false;
    }
    nread += size;
    while (nread > 0) {
        if (!_initialized && nread >= sizeof(ControllerInitHeader)) {
            ControllerInitHeader *init = (ControllerInitHeader *)pos;
            if (init->magic != CONTROLLER_MAGIC || init->version != CONTROLLER_VERSION) {
                LOG_ERROR("Bad controller init, magic=0x%x version=%u", init->magic,
                          init->version);
                _parent.remove_connection(_opaque);
                return false;
            }
            if (nread < init->size) {
                break;
            }
            if (!handle_init((ControllerInit*)init)) {
                _parent.remove_connection(_opaque);
                return false;
            }
            nread -= init->size;
            pos += init->size;
            _initialized = true;
        }
        if (!_initialized || nread < sizeof(ControllerMsg)) {
            break;
        }
        ControllerMsg *hdr = (ControllerMsg *)pos;
        if (hdr->size < sizeof(ControllerMsg)) {
            LOG_ERROR("Bad controller message, size=%u", hdr->size);
            _parent.remove_connection(_opaque);
            return false;
        }
        if (nread < hdr->size) {
            break;
        }
        handle_message(hdr);
        nread -= hdr->size;
        pos += hdr->size;
    }
    if (nread > 0 && pos != _read_buf) {
        memcpy(_read_buf, pos, nread);
    }
    _read_pos = _read_buf + nread;
    return true;
}

bool ControllerConnection::write_msg(const void *buf, int len)
{
    RecurciveLock lock(_write_lock);
    uint8_t *pos;
    int32_t written = 0;

    ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
    if (_write_pending && buf != _write_pos) {
        if ((_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) &&
                                              !write_msg(_write_pos, _write_pending)) {
            return false;
        }
        if (_write_pending) {
            if (_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) {
                DBG(0, "Dropping message, due to insufficient space in write buffer");
                return true;
            }
            memcpy(_write_pos + _write_pending, buf, len);
            _write_pending += len;
        }
    }
    pos = (uint8_t*)buf;
    while (len && (written = NamedPipe::write(_opaque, pos, len)) > 0) {
        pos += written;
        len -= written;
    }
    if (len && written == 0) {
        if (_write_pending) {
            _write_pos = pos;
        } else {
            _write_pos = _write_buf;
            memcpy(_write_buf, pos, len);
        }
        _write_pending = len;
    } else if (written < 0) {
        LOG_ERROR("Error writing to named pipe %d", written);
        _parent.remove_connection(_opaque);
        return false;
    } else {
        _write_pending = 0;
    }
    return true;
}

bool ControllerConnection::handle_init(ControllerInit *init)
{
    ASSERT(_handler);
    if (init->credentials != 0) {
        LOG_ERROR("Controller menu has wrong credentials 0x%x", init->credentials);
        return false;
    }
    if (!_parent.set_exclusive(init->flags & CONTROLLER_FLAG_EXCLUSIVE)) {
        return false;
    }
    return true;
}

bool ControllerConnection::handle_message(ControllerMsg *hdr)
{
    uint32_t value = ((ControllerValue*)hdr)->value;
    char *data = (char*)((ControllerData*)hdr)->data;

    ASSERT(_handler);
    switch (hdr->id) {
    case CONTROLLER_HOST:
        _host.assign(data);
        break;
    case CONTROLLER_PORT:
        _port = value;
        break;
    case CONTROLLER_SPORT:
        _sport = value;
        break;
    case CONTROLLER_PASSWORD:
        _password.assign(data);
        break;
    case CONTROLLER_SECURE_CHANNELS:
    case CONTROLLER_DISABLE_CHANNELS:
        return set_multi_val(hdr->id, data);
    case CONTROLLER_TLS_CIPHERS:
        return _handler->set_connection_ciphers(data, "Controller");
    case CONTROLLER_CA_FILE:
        return _handler->set_ca_file(data, "Controller");
    case CONTROLLER_HOST_SUBJECT:
        return _handler->set_host_cert_subject(data, "Controller");
    case CONTROLLER_FULL_SCREEN:
        _full_screen = !!(value & CONTROLLER_SET_FULL_SCREEN);
        _handler->set_auto_display_res(!!(value & CONTROLLER_AUTO_DISPLAY_RES));
        break;
    case CONTROLLER_SET_TITLE: {
        std::string str;
        string_printf(str, "%s", data);
        _handler->set_title(str);
        break;
    }
    case CONTROLLER_HOTKEYS:
        _handler->set_hotkeys(data);
        break;
    case CONTROLLER_CONNECT:
        _handler->connect(_host, _port, _sport, _password);
        break;
    case CONTROLLER_SHOW:
        _handler->show_me(_full_screen);
        break;
    case CONTROLLER_HIDE:
        _handler->hide_me();
        break;
    case CONTROLLER_CREATE_MENU:
        return create_menu((char*)data);
    case CONTROLLER_DELETE_MENU:
        _handler->delete_menu();
        break;
    case CONTROLLER_SEND_CAD:
    default:
        LOG_ERROR("Ignoring an unknown controller message %u", hdr->id);
        return false;
    }
    return true;
}

#ifdef WIN32
#define next_tok(str, delim, state) strtok(str, delim)
#else
#define next_tok(str, delim, state) strtok_r(str, delim, state)
#endif

bool ControllerConnection::create_menu(char* resource)
{
    bool ret = true;
#ifndef WIN32
    char* item_state = 0;
#endif
    char* next_item;
    const char* param;
    const char* text;
    int parent_id;
    int flags;
    int state;
    int id;

    ASSERT(_handler);
    AutoRef<Menu> app_menu(_handler->get_app_menu());
    AutoRef<Menu> menu(new Menu((*app_menu)->get_target(), ""));
    char* item = next_tok(resource, CONTROLLER_MENU_ITEM_DELIMITER, &item_state);
    while (item != NULL) {
        next_item = item + strlen(item) + 1;
        ret = ret && (param = next_tok(item, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
              sscanf(param, "%d", &parent_id);
        ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
              sscanf(param, "%d", &id);
        ret = ret && (text = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state));
        ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
              sscanf(param, "%d", &flags);

        if (!ret) {
            DBG(0, "item parsing failed %s", item);
            break;
        }
        DBG(0, "parent_id=%d, id=%d, text=%s, flags=%d", parent_id, id, text, flags);

        AutoRef<Menu> sub_menu((*menu)->find_sub(parent_id));
        if (!(ret = !!*sub_menu)) {
            DBG(0, "submenu not found %s", item);
            break;
        }

        if (flags & CONTROLLER_MENU_FLAGS_SEPARATOR) {
            (*sub_menu)->add_separator();
        } else if (flags & CONTROLLER_MENU_FLAGS_POPUP) {
            AutoRef<Menu> sub(new Menu((*app_menu)->get_target(), text, id));
            (*sub_menu)->add_sub(sub.release());
        } else {
            state = 0;
            if (flags & (CONTROLLER_MENU_FLAGS_DISABLED | CONTROLLER_MENU_FLAGS_GRAYED)) {
                state |= Menu::MENU_ITEM_STATE_DIM;
            }
            if (flags & CONTROLLER_MENU_FLAGS_CHECKED) {
                state |= Menu::MENU_ITEM_STATE_CHECKED;
            }
            if (id >= SPICE_MENU_INTERNAL_ID_BASE) {
                id = ((id - SPICE_MENU_INTERNAL_ID_BASE) >> SPICE_MENU_INTERNAL_ID_SHIFT) + 1;
            } else {
                id = _handler->get_controller_menu_item_id(_opaque, id);
            }
            (*sub_menu)->add_command(text, id, state);
        }
        item = next_tok(next_item, CONTROLLER_MENU_ITEM_DELIMITER, &item_state);
    }
    if (ret) {
        _handler->set_menu(*menu);
    }
    return ret;
}

bool ControllerConnection::set_multi_val(uint32_t op, char* multi_val)
{
    CmdLineParser parser("", false);
    int id = CmdLineParser::OPTION_FIRST_AVILABLE;
    char* argv[] = {NULL, (char*)"--set", multi_val};
    char* val;

    ASSERT(_handler);
    parser.add(id, "set", "none", "none", true);
    parser.set_multi(id, ',');
    parser.begin(3, argv);
    if (parser.get_option(&val) != id) {
        return false;
    }
    switch (op) {
    case CONTROLLER_SECURE_CHANNELS:
        _handler->set_channels_security(parser, true, val, "Controller");
        break;
    case CONTROLLER_DISABLE_CHANNELS:
        _handler->set_enable_channels(parser, false, val, "Controller");
        break;
    default:
        DBG(0, "unsupported op %u", op);
        return false;
    }
    return true;
}
<|endoftext|>


<commit_before>
#ifndef UTILITIES_STRING_HPP_
#define UTILITIES_STRING_HPP_

#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

namespace string {

//------------------------------------------------------------------------------
// Name: ends_with
//------------------------------------------------------------------------------
inline bool ends_with(const std::string &s, char ch) {
	return !s.empty() && s.back() == ch;
}

//------------------------------------------------------------------------------
// Name: ends_with
//------------------------------------------------------------------------------
inline bool ends_with(const std::string &s, const std::string &suffix) {
	return std::mismatch(suffix.rbegin(), suffix.rend(), s.rbegin()).first == suffix.rend();
}

//------------------------------------------------------------------------------
// Name: starts_with
//------------------------------------------------------------------------------
inline bool starts_with(const std::string &s, char ch) {
	return !s.empty() && s.front() == ch;
}

//------------------------------------------------------------------------------
// Name: starts_with
//------------------------------------------------------------------------------
inline bool starts_with(const std::string &s, const std::string &prefix) {
	return std::mismatch(prefix.begin(), prefix.end(), s.begin()).first == prefix.end();
}

//------------------------------------------------------------------------------
// Name: rtrim
//------------------------------------------------------------------------------
inline void rtrim(std::string &s) {
	s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

//------------------------------------------------------------------------------
// Name: ltrim
//------------------------------------------------------------------------------
inline void ltrim(std::string &s) {
	s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
}

//------------------------------------------------------------------------------
// Name: trim
//------------------------------------------------------------------------------
inline void trim(std::string &s) {
	ltrim(s);
	rtrim(s);
}

//------------------------------------------------------------------------------
// Name: rtrim_copy
//------------------------------------------------------------------------------
inline std::string rtrim_copy(std::string s) {
	rtrim(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: ltrim_copy
//------------------------------------------------------------------------------
inline std::string ltrim_copy(std::string s) {
	ltrim(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: trim_copy
//------------------------------------------------------------------------------
inline std::string trim_copy(std::string &s) {
	trim(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: toupper
//------------------------------------------------------------------------------
inline void toupper(std::string &s) {
	std::transform(s.begin(), s.end(), s.begin(), std::ptr_fun<int, int>(std::toupper));
}

//------------------------------------------------------------------------------
// Name: tolower
//------------------------------------------------------------------------------
inline void tolower(std::string &s) {
	std::transform(s.begin(), s.end(), s.begin(), std::ptr_fun<int, int>(std::tolower));
}

//------------------------------------------------------------------------------
// Name: toupper_copy
//------------------------------------------------------------------------------
inline std::string toupper_copy(std::string s) {
	toupper(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: tolower_copy
//------------------------------------------------------------------------------
inline std::string tolower_copy(std::string s) {
	tolower(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: implode
// Desc: join elements with a char
//------------------------------------------------------------------------------
inline std::string implode(char glue, const std::vector<std::string> &pieces) {
	std::string s;
	if(!pieces.empty()) {
		s.append(pieces[0]);
		for(size_t i = 1; i < pieces.size(); ++i) {
			s.push_back(glue);
			s.append(pieces[i]);
		}
	}
	return s;
}

//------------------------------------------------------------------------------
// Name: implode
// Desc: join elements with a string
//------------------------------------------------------------------------------
inline std::string implode(const std::string &glue, const std::vector<std::string> &pieces) {
	std::string s;
	if(!pieces.empty()) {
		s.append(pieces[0]);
		for(size_t i = 1; i < pieces.size(); ++i) {
			s.append(glue);
			s.append(pieces[i]);
		}
	}
	return s;
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(const std::string &delimeter, const std::string &string, int limit) {
	std::vector<std::string> r;

	if(!string.empty()) {
		if(limit >= 0) {
			if(limit == 0) {
				limit = 1;
			}

			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {

				if(--limit == 0) {
					break;
				}

				r.emplace_back(string.substr(first, last - first));
				first = last + delimeter.size();
				last  = string.find(delimeter, last + delimeter.size());
			}

			r.emplace_back(string.substr(first));
		} else {
			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {
				r.emplace_back(string.substr(first, last - first));
				first = last + delimeter.size();
				last  = string.find(delimeter, last + delimeter.size());
			}

			r.emplace_back(string.substr(first));

			while(limit < 0) {
				r.pop_back();
				++limit;
			}
		}
	}

	return r;
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(const std::string &delimeter, const std::string &string) {
	return explode(delimeter, string, std::numeric_limits<int>::max());
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(char delimeter, const std::string &string, int limit) {
	std::vector<std::string> r;

	if(!string.empty()) {
		if(limit >= 0) {
			if(limit == 0) {
				limit = 1;
			}

			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {

				if(--limit == 0) {
					break;
				}

				r.emplace_back(string.substr(first, last - first));
				first = last + 1;
				last  = string.find(delimeter, last + 1);
			}

			r.emplace_back(string.substr(first));
		} else {
			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {
				r.emplace_back(string.substr(first, last - first));
				first = last + 1;
				last  = string.find(delimeter, last + 1);
			}

			r.emplace_back(string.substr(first));

			while(limit < 0) {
				r.pop_back();
				++limit;
			}
		}
	}

	return r;
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(char delimeter, const std::string &string) {
	return explode(delimeter, string, std::numeric_limits<int>::max());
}

//------------------------------------------------------------------------------
// Name: split
//------------------------------------------------------------------------------
template <class Op>
void split(const std::string &s, char delim, Op op) {
	std::stringstream ss(s);
	for(std::string item; std::getline(ss, item, delim); ) {
		*op++ = item;
	}
}

//------------------------------------------------------------------------------
// Name: split
//------------------------------------------------------------------------------
inline std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, std::back_inserter(elems));
    return elems;
}

}

#endif
<commit_msg>added safe_ctype<commit_after>
#ifndef UTILITIES_STRING_HPP_
#define UTILITIES_STRING_HPP_

#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

namespace string {

//------------------------------------------------------------------------------
// Name: safe_ctype
//------------------------------------------------------------------------------
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
	return F(c);
}

//------------------------------------------------------------------------------
// Name: ends_with
//------------------------------------------------------------------------------
inline bool ends_with(const std::string &s, char ch) {
	return !s.empty() && s.back() == ch;
}

//------------------------------------------------------------------------------
// Name: ends_with
//------------------------------------------------------------------------------
inline bool ends_with(const std::string &s, const std::string &suffix) {
	return std::mismatch(suffix.rbegin(), suffix.rend(), s.rbegin()).first == suffix.rend();
}

//------------------------------------------------------------------------------
// Name: starts_with
//------------------------------------------------------------------------------
inline bool starts_with(const std::string &s, char ch) {
	return !s.empty() && s.front() == ch;
}

//------------------------------------------------------------------------------
// Name: starts_with
//------------------------------------------------------------------------------
inline bool starts_with(const std::string &s, const std::string &prefix) {
	return std::mismatch(prefix.begin(), prefix.end(), s.begin()).first == prefix.end();
}

//------------------------------------------------------------------------------
// Name: rtrim
//------------------------------------------------------------------------------
inline void rtrim(std::string &s) {
	s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

//------------------------------------------------------------------------------
// Name: ltrim
//------------------------------------------------------------------------------
inline void ltrim(std::string &s) {
	s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
}

//------------------------------------------------------------------------------
// Name: trim
//------------------------------------------------------------------------------
inline void trim(std::string &s) {
	ltrim(s);
	rtrim(s);
}

//------------------------------------------------------------------------------
// Name: rtrim_copy
//------------------------------------------------------------------------------
inline std::string rtrim_copy(std::string s) {
	rtrim(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: ltrim_copy
//------------------------------------------------------------------------------
inline std::string ltrim_copy(std::string s) {
	ltrim(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: trim_copy
//------------------------------------------------------------------------------
inline std::string trim_copy(std::string &s) {
	trim(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: toupper
//------------------------------------------------------------------------------
inline void toupper(std::string &s) {
	std::transform(s.begin(), s.end(), s.begin(), std::ptr_fun<int, int>(std::toupper));
}

//------------------------------------------------------------------------------
// Name: tolower
//------------------------------------------------------------------------------
inline void tolower(std::string &s) {
	std::transform(s.begin(), s.end(), s.begin(), std::ptr_fun<int, int>(std::tolower));
}

//------------------------------------------------------------------------------
// Name: toupper_copy
//------------------------------------------------------------------------------
inline std::string toupper_copy(std::string s) {
	toupper(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: tolower_copy
//------------------------------------------------------------------------------
inline std::string tolower_copy(std::string s) {
	tolower(s);
	return s;
}

//------------------------------------------------------------------------------
// Name: implode
// Desc: join elements with a char
//------------------------------------------------------------------------------
inline std::string implode(char glue, const std::vector<std::string> &pieces) {
	std::string s;
	if(!pieces.empty()) {
		s.append(pieces[0]);
		for(size_t i = 1; i < pieces.size(); ++i) {
			s.push_back(glue);
			s.append(pieces[i]);
		}
	}
	return s;
}

//------------------------------------------------------------------------------
// Name: implode
// Desc: join elements with a string
//------------------------------------------------------------------------------
inline std::string implode(const std::string &glue, const std::vector<std::string> &pieces) {
	std::string s;
	if(!pieces.empty()) {
		s.append(pieces[0]);
		for(size_t i = 1; i < pieces.size(); ++i) {
			s.append(glue);
			s.append(pieces[i]);
		}
	}
	return s;
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(const std::string &delimeter, const std::string &string, int limit) {
	std::vector<std::string> r;

	if(!string.empty()) {
		if(limit >= 0) {
			if(limit == 0) {
				limit = 1;
			}

			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {

				if(--limit == 0) {
					break;
				}

				r.emplace_back(string.substr(first, last - first));
				first = last + delimeter.size();
				last  = string.find(delimeter, last + delimeter.size());
			}

			r.emplace_back(string.substr(first));
		} else {
			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {
				r.emplace_back(string.substr(first, last - first));
				first = last + delimeter.size();
				last  = string.find(delimeter, last + delimeter.size());
			}

			r.emplace_back(string.substr(first));

			while(limit < 0) {
				r.pop_back();
				++limit;
			}
		}
	}

	return r;
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(const std::string &delimeter, const std::string &string) {
	return explode(delimeter, string, std::numeric_limits<int>::max());
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(char delimeter, const std::string &string, int limit) {
	std::vector<std::string> r;

	if(!string.empty()) {
		if(limit >= 0) {
			if(limit == 0) {
				limit = 1;
			}

			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {

				if(--limit == 0) {
					break;
				}

				r.emplace_back(string.substr(first, last - first));
				first = last + 1;
				last  = string.find(delimeter, last + 1);
			}

			r.emplace_back(string.substr(first));
		} else {
			size_t first = 0;
			size_t last  = string.find(delimeter);

			while(last != std::string::npos) {
				r.emplace_back(string.substr(first, last - first));
				first = last + 1;
				last  = string.find(delimeter, last + 1);
			}

			r.emplace_back(string.substr(first));

			while(limit < 0) {
				r.pop_back();
				++limit;
			}
		}
	}

	return r;
}

//------------------------------------------------------------------------------
// Name: explode
//------------------------------------------------------------------------------
inline std::vector<std::string> explode(char delimeter, const std::string &string) {
	return explode(delimeter, string, std::numeric_limits<int>::max());
}

//------------------------------------------------------------------------------
// Name: split
//------------------------------------------------------------------------------
template <class Op>
void split(const std::string &s, char delim, Op op) {
	std::stringstream ss(s);
	for(std::string item; std::getline(ss, item, delim); ) {
		*op++ = item;
	}
}

//------------------------------------------------------------------------------
// Name: split
//------------------------------------------------------------------------------
inline std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, std::back_inserter(elems));
    return elems;
}

}

#endif
<|endoftext|>


<commit_before>#include "string.hxx"

#include <cassert>

#include <string>
#include <vector>

namespace algods {
    namespace string {

        static std::vector<long long> kmp_prefix_func(const std::string& pattern) {
            std::vector<long long> shifts(pattern.size(), -1);
            long long q = -1;
            for(auto i = 1; i < pattern.size(); ++i) {
                while(q >= 0 && pattern[q + 1] != pattern[i]) {
                    q = shifts[q];
                }
                if(pattern[q + 1] == pattern[i]) {
                    ++q;
                }
                shifts[i] = q;
            }
            return shifts;
        }

        static std::string::size_type kmp(const std::string& text,
                                          std::string::size_type pos,
                                          const std::string& pattern,
                                          const std::vector<long long>& shifts) {
            if(pos >= text.size()) {
                return std::string::npos;
            }
            if(pos + pattern.size() > text.size()) {
                return std::string::npos;
            }
            if(pattern.size() == 0) {
                return 0;
            }
            long long q = -1;
            for(auto i = pos; i < text.size(); ++i) {
                while(q >= 0 && pattern[q + 1] != text[i]) {
                    q = shifts[q];
                }
                if(pattern[q + 1] == text[i]) {
                    ++q;
                }
                if(q + 1 == pattern.size()) {
                    return i - pattern.size() + 1;
                }
            }
            return std::string::npos;
        }

        std::string::size_type kmp(const std::string& text, std::string::size_type pos,
                                   const std::string& pattern) {
            auto shifts = kmp_prefix_func(pattern);
            return kmp(text, pos, pattern, shifts);
        }

        std::string::size_type kmp(const std::string& text, const std::string& pattern) {
            return kmp(text, 0, pattern);
        }

        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        static void replace_equal(std::string& s, const std::string& p,
                                         const std::string& r) {
            assert(p.size() == r.size() && "p.size() == r.size() is not satisfied");

            auto shifts = kmp_prefix_func(p);
            std::string::size_type pos = 0;
            while((pos = kmp(s, pos, p, shifts)) != std::string::npos) {
                for(auto i = std::string::size_type(0); i < r.size(); ++i) {
                    s[pos] = r[i];
                    ++pos;
                }
            }
        }

        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        static void replace_greater(std::string& s, const std::string& p,
                                    const std::string& r) {
            assert(p.size() > r.size() && "p.size() > r.size() is not satisfied");

            auto shifts = kmp_prefix_func(p);
            long long count = 0;
            std::string::size_type pos = 0;
            std::string::size_type start = pos;
            std::string::size_type idx = 0;
            while((pos = kmp(s, pos, p, shifts)) != std::string::npos) {
                for(auto i = start; i != pos; ++i, ++start, ++idx) {
                    s[idx] = s[start];
                }
                for(auto i = idx, j = std::string::size_type(0); j < r.size(); ++j) {
                    s[i] = r[j];
                }
                idx += r.size();
                pos += p.size();
                start = pos;
                ++count;
            }
            for(; start < s.size(); ++start, ++idx) {
                s[idx] = s[start];
            }
            s.resize(s.size() - count * (p.size() - r.size()));
        }

        static std::vector<long long> reverse_kmp_prefix_func(const std::string& pattern) {
            std::vector<long long> shifts(pattern.size(), pattern.size());
            auto q = pattern.size();
            for(long long i = pattern.size() - 2; i >= 0; --i) {
                while(q < pattern.size() && pattern[q - 1] != pattern[i]) {
                    q = pattern[q];
                }
                if(pattern[q - 1] == pattern[i]) {
                    --q;
                }
                shifts[i] = q;
            }
            return shifts;
        }
        static std::string::size_type reverse_kmp(const std::string& text,
                                                  std::string::size_type pos,
                                                  const std::string& pattern,
                                                  const std::vector<long long>& reverse_shifts) {
            if(pos >= text.size()) {
                return std::string::npos;
            }
            if(pos < pattern.size()) {
                return std::string::npos;
            }
            if(pattern.size() == 0) {
                return 0;
            }
            long long q = pattern.size();
            for(long long i = pos; i >= 0; --i) {
                while(q < pattern.size() && pattern[q - 1] != text[i]) {
                    q = reverse_shifts[q];
                }
                if(pattern[q - 1] == text[i]) {
                    --q;
                }
                if(q - 1 < 0) {
                    return i;
                }
            }
            return std::string::npos;
        }
        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        static void replace_less(std::string& s, const std::string& p,
                                    const std::string& r) {
            assert(p.size() < r.size() && "p.size() < r.size() is not satisfied");

            auto reverse_shifts = reverse_kmp_prefix_func(p);
            std::string::size_type pos = s.size() - 1;
            long long count = 0;
            while((pos = reverse_kmp(s, pos, p, reverse_shifts)) != std::string::npos) {
                ++count;
            }
            auto size = s.size();
            s.resize(s.size() + count * (r.size() - p.size()));
            pos = size - 1;
            auto end = pos;
            auto idx = s.size() - 1;
            while((pos = reverse_kmp(s, pos, p, reverse_shifts)) != std::string::npos) {
                for(auto i = end; i >= pos + p.size(); --i, --idx, --end) {
                    s[idx] = s[end];
                }
                for(auto i = 1; i <= r.size(); ++i, --idx) {
                    s[idx] = r[r.size() - i];
                }
                --pos;
                end = pos;
            }
        }
        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        void replace(std::string& s, const std::string& p, const std::string& r) {
            if(p.size() == r.size()) {
                replace_equal(s, p, r);
            } else if(p.size() > r.size()) {
                replace_greater(s, p, r);
            } else {
                replace_less(s, p, r);
            }
        }

        void remove_space(std::string& s) {
            const static char space = ' ';
            auto next_pos = std::string::size_type(0);
            auto cur_pos = std::string::size_type(0);
            auto space_count = 0;
            auto size = 0;
            while(cur_pos < s.size()) {
                if(s[cur_pos] != space) {
                    s[next_pos] = s[cur_pos];
                    ++next_pos;
                    space_count = 0;
                    size  = next_pos;
                }
                if(s[cur_pos] == space) {
                    if(space_count < 1 && next_pos != 0) {
                        s[next_pos] = space;
                        ++next_pos;
                    }
                    ++space_count;
                }
                ++cur_pos;
            }
            s.resize(size);
        }
    }
}
<commit_msg>naive match for string<commit_after>#include "string.hxx"

#include <cassert>

#include <string>
#include <vector>

namespace algods {
    namespace string {

        static std::vector<long long> kmp_prefix_func(const std::string& pattern) {
            std::vector<long long> shifts(pattern.size(), -1);
            long long q = -1;
            for(auto i = 1; i < pattern.size(); ++i) {
                while(q >= 0 && pattern[q + 1] != pattern[i]) {
                    q = shifts[q];
                }
                if(pattern[q + 1] == pattern[i]) {
                    ++q;
                }
                shifts[i] = q;
            }
            return shifts;
        }

        static std::string::size_type kmp(const std::string& text,
                                          std::string::size_type pos,
                                          const std::string& pattern,
                                          const std::vector<long long>& shifts) {
            if(pos >= text.size()) {
                return std::string::npos;
            }
            if(pos + pattern.size() > text.size()) {
                return std::string::npos;
            }
            if(pattern.size() == 0) {
                return 0;
            }
            long long q = -1;
            for(auto i = pos; i < text.size(); ++i) {
                while(q >= 0 && pattern[q + 1] != text[i]) {
                    q = shifts[q];
                }
                if(pattern[q + 1] == text[i]) {
                    ++q;
                }
                if(q + 1 == pattern.size()) {
                    return i - pattern.size() + 1;
                }
            }
            return std::string::npos;
        }

        std::string::size_type kmp(const std::string& text, std::string::size_type pos,
                                   const std::string& pattern) {
            auto shifts = kmp_prefix_func(pattern);
            return kmp(text, pos, pattern, shifts);
        }

        std::string::size_type kmp(const std::string& text, const std::string& pattern) {
            return kmp(text, 0, pattern);
        }

        std::string::size_type naive_match(const std::string& text,
                                           std::string::size_type pos,
                                           const std::string& pattern) {
            if(pos + pattern.size() > text.size()) {
                return std::string::npos;
            }
            for(auto i = pos; i <= text.size() - pattern.size(); ++i) {
                auto k = 0;
                for(auto j = i; k < pattern.size(); ++j; ++k) {
                    if(text[j] != pattern[k]) {
                        break;
                    }
                }
                if(k == pattern.size()) {
                    return i;
                }
            }
            return std::string::npos;
        }

        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        static void replace_equal(std::string& s, const std::string& p,
                                         const std::string& r) {
            assert(p.size() == r.size() && "p.size() == r.size() is not satisfied");

            auto shifts = kmp_prefix_func(p);
            std::string::size_type pos = 0;
            while((pos = kmp(s, pos, p, shifts)) != std::string::npos) {
                for(auto i = std::string::size_type(0); i < r.size(); ++i) {
                    s[pos] = r[i];
                    ++pos;
                }
            }
        }

        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        static void replace_greater(std::string& s, const std::string& p,
                                    const std::string& r) {
            assert(p.size() > r.size() && "p.size() > r.size() is not satisfied");

            auto shifts = kmp_prefix_func(p);
            long long count = 0;
            std::string::size_type pos = 0;
            std::string::size_type start = pos;
            std::string::size_type idx = 0;
            while((pos = kmp(s, pos, p, shifts)) != std::string::npos) {
                for(auto i = start; i != pos; ++i, ++start, ++idx) {
                    s[idx] = s[start];
                }
                for(auto i = idx, j = std::string::size_type(0); j < r.size(); ++j) {
                    s[i] = r[j];
                }
                idx += r.size();
                pos += p.size();
                start = pos;
                ++count;
            }
            for(; start < s.size(); ++start, ++idx) {
                s[idx] = s[start];
            }
            s.resize(s.size() - count * (p.size() - r.size()));
        }

        static std::vector<long long> reverse_kmp_prefix_func(const std::string& pattern) {
            std::vector<long long> shifts(pattern.size(), pattern.size());
            auto q = pattern.size();
            for(long long i = pattern.size() - 2; i >= 0; --i) {
                while(q < pattern.size() && pattern[q - 1] != pattern[i]) {
                    q = pattern[q];
                }
                if(pattern[q - 1] == pattern[i]) {
                    --q;
                }
                shifts[i] = q;
            }
            return shifts;
        }
        static std::string::size_type reverse_kmp(const std::string& text,
                                                  std::string::size_type pos,
                                                  const std::string& pattern,
                                                  const std::vector<long long>& reverse_shifts) {
            if(pos >= text.size()) {
                return std::string::npos;
            }
            if(pos < pattern.size()) {
                return std::string::npos;
            }
            if(pattern.size() == 0) {
                return 0;
            }
            long long q = pattern.size();
            for(long long i = pos; i >= 0; --i) {
                while(q < pattern.size() && pattern[q - 1] != text[i]) {
                    q = reverse_shifts[q];
                }
                if(pattern[q - 1] == text[i]) {
                    --q;
                }
                if(q - 1 < 0) {
                    return i;
                }
            }
            return std::string::npos;
        }
        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        static void replace_less(std::string& s, const std::string& p,
                                    const std::string& r) {
            assert(p.size() < r.size() && "p.size() < r.size() is not satisfied");

            auto reverse_shifts = reverse_kmp_prefix_func(p);
            std::string::size_type pos = s.size() - 1;
            long long count = 0;
            while((pos = reverse_kmp(s, pos, p, reverse_shifts)) != std::string::npos) {
                ++count;
            }
            auto size = s.size();
            s.resize(s.size() + count * (r.size() - p.size()));
            pos = size - 1;
            auto end = pos;
            auto idx = s.size() - 1;
            while((pos = reverse_kmp(s, pos, p, reverse_shifts)) != std::string::npos) {
                for(auto i = end; i >= pos + p.size(); --i, --idx, --end) {
                    s[idx] = s[end];
                }
                for(auto i = 1; i <= r.size(); ++i, --idx) {
                    s[idx] = r[r.size() - i];
                }
                --pos;
                end = pos;
            }
        }
        /********************************************************************************
         ** O(n) on time, O(1) on space
         ********************************************************************************/
        void replace(std::string& s, const std::string& p, const std::string& r) {
            if(p.size() == r.size()) {
                replace_equal(s, p, r);
            } else if(p.size() > r.size()) {
                replace_greater(s, p, r);
            } else {
                replace_less(s, p, r);
            }
        }

        void remove_space(std::string& s) {
            const static char space = ' ';
            auto next_pos = std::string::size_type(0);
            auto cur_pos = std::string::size_type(0);
            auto space_count = 0;
            auto size = 0;
            while(cur_pos < s.size()) {
                if(s[cur_pos] != space) {
                    s[next_pos] = s[cur_pos];
                    ++next_pos;
                    space_count = 0;
                    size  = next_pos;
                }
                if(s[cur_pos] == space) {
                    if(space_count < 1 && next_pos != 0) {
                        s[next_pos] = space;
                        ++next_pos;
                    }
                    ++space_count;
                }
                ++cur_pos;
            }
            s.resize(size);
        }
    }
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2009-2015, Jack Poulson
   All rights reserved.

   This file is part of Elemental and is under the BSD 2-Clause License,
   which can be found in the LICENSE file in the root directory, or at
   http://opensource.org/licenses/BSD-2-Clause
*/
#include "El.hpp"

namespace El {

template<typename F>
pair<Base<F>,Base<F>>
ExtremalSingValEst( const SparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("ExtremalSingValEst"))
    typedef Base<F> Real;
    Matrix<Real> T;
    ProductLanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w, ASCENDING );

    pair<Real,Real> extremal;
    extremal.first = Sqrt( Max(w.Get(0,0),Real(0)) );
    extremal.second = Sqrt( Max(w.Get(k-1,0),Real(0)) );
    return extremal;
}

template<typename F>
pair<Base<F>,Base<F>>
ExtremalSingValEst( const DistSparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("ExtremalSingValEst"))
    typedef Base<F> Real;
    Matrix<Real> T;
    ProductLanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w, ASCENDING );

    pair<Real,Real> extremal;
    extremal.first = Sqrt( Max(w.Get(0,0),Real(0)) );
    extremal.second = Sqrt( Max(w.Get(k-1,0),Real(0)) );
    return extremal;
}

template<typename F>
pair<Base<F>,Base<F>>
HermitianExtremalSingValEst( const SparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("HermitianExtremalSingValEst"))
    typedef Base<F> Real;
    Matrix<Real> T;
    Lanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w, ASCENDING );

    pair<Real,Real> extremal;
    extremal.first = MaxNorm(w);
    extremal.second = extremal.first;
    for( Int i=0; i<k; ++i )
        extremal.second = Min(extremal.second,Abs(w.Get(i,0)));
    return extremal;
}

template<typename F>
pair<Base<F>,Base<F>>
HermitianExtremalSingValEst( const DistSparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("HermitianExtremalSingValEst"))
    typedef Base<F> Real;

    Matrix<Real> T;
    Lanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w, ASCENDING );

    pair<Real,Real> extremal;
    extremal.first = MaxNorm(w);
    extremal.second = extremal.first;
    for( Int i=0; i<k; ++i )
        extremal.second = Min(extremal.second,Abs(w.Get(i,0)));

    return extremal;
}

#define PROTO(F) \
  template pair<Base<F>,Base<F>> ExtremalSingValEst \
  ( const SparseMatrix<F>& A, Int basisSize ); \
  template pair<Base<F>,Base<F>> ExtremalSingValEst \
  ( const DistSparseMatrix<F>& A, Int basisSize ); \
  template pair<Base<F>,Base<F>> HermitianExtremalSingValEst \
  ( const SparseMatrix<F>& A, Int basisSize ); \
  template pair<Base<F>,Base<F>> HermitianExtremalSingValEst \
  ( const DistSparseMatrix<F>& A, Int basisSize );

#define EL_NO_INT_PROTO
#include "El/macros/Instantiate.h"

} // namespace El
<commit_msg>Fixing mistakes in singular value estimation<commit_after>/*
   Copyright (c) 2009-2015, Jack Poulson
   All rights reserved.

   This file is part of Elemental and is under the BSD 2-Clause License,
   which can be found in the LICENSE file in the root directory, or at
   http://opensource.org/licenses/BSD-2-Clause
*/
#include "El.hpp"

namespace El {

template<typename F>
pair<Base<F>,Base<F>>
ExtremalSingValEst( const SparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("ExtremalSingValEst"))
    typedef Base<F> Real;
    Matrix<Real> T;
    ProductLanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w, ASCENDING );

    pair<Real,Real> extremal;
    extremal.first = Sqrt( Max(w.Get(0,0),Real(0)) );
    extremal.second = Sqrt( Max(w.Get(k-1,0),Real(0)) );
    return extremal;
}

template<typename F>
pair<Base<F>,Base<F>>
ExtremalSingValEst( const DistSparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("ExtremalSingValEst"))
    typedef Base<F> Real;
    Matrix<Real> T;
    ProductLanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w, ASCENDING );

    pair<Real,Real> extremal;
    extremal.first = Sqrt( Max(w.Get(0,0),Real(0)) );
    extremal.second = Sqrt( Max(w.Get(k-1,0),Real(0)) );
    return extremal;
}

template<typename F>
pair<Base<F>,Base<F>>
HermitianExtremalSingValEst( const SparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("HermitianExtremalSingValEst"))
    typedef Base<F> Real;
    Matrix<Real> T;
    Lanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w );

    pair<Real,Real> extremal;
    extremal.second = MaxNorm(w);
    extremal.first = extremal.second;
    for( Int i=0; i<k; ++i )
        extremal.first = Min(extremal.first,Abs(w.Get(i,0)));
    return extremal;
}

template<typename F>
pair<Base<F>,Base<F>>
HermitianExtremalSingValEst( const DistSparseMatrix<F>& A, Int basisSize )
{
    DEBUG_ONLY(CSE cse("HermitianExtremalSingValEst"))
    typedef Base<F> Real;

    Matrix<Real> T;
    Lanczos( A, T, basisSize );
    const Int k = T.Height();
    if( k == 0 )
        return pair<Real,Real>(0,0);

    Matrix<Real> d, dSub;
    d = GetDiagonal( T );
    dSub = GetDiagonal( T, -1 );

    Matrix<Real> w;
    HermitianTridiagEig( d, dSub, w );

    pair<Real,Real> extremal;
    extremal.second = MaxNorm(w);
    extremal.first = extremal.second;
    for( Int i=0; i<k; ++i )
        extremal.first = Min(extremal.first,Abs(w.Get(i,0)));

    return extremal;
}

#define PROTO(F) \
  template pair<Base<F>,Base<F>> ExtremalSingValEst \
  ( const SparseMatrix<F>& A, Int basisSize ); \
  template pair<Base<F>,Base<F>> ExtremalSingValEst \
  ( const DistSparseMatrix<F>& A, Int basisSize ); \
  template pair<Base<F>,Base<F>> HermitianExtremalSingValEst \
  ( const SparseMatrix<F>& A, Int basisSize ); \
  template pair<Base<F>,Base<F>> HermitianExtremalSingValEst \
  ( const DistSparseMatrix<F>& A, Int basisSize );

#define EL_NO_INT_PROTO
#include "El/macros/Instantiate.h"

} // namespace El
<|endoftext|>


<commit_before>//===-- EditlineTest.cpp -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_DISABLE_LIBEDIT

#define EDITLINE_TEST_DUMP_OUTPUT 0

#include <stdio.h>
#include <unistd.h>

#include <memory>
#include <thread>

#include "gtest/gtest.h"

#include "lldb/Core/Error.h"
#include "lldb/Core/StringList.h"
#include "lldb/Host/Editline.h"
#include "lldb/Host/Pipe.h"
#include "lldb/Utility/PseudoTerminal.h"

namespace
{
    const size_t TIMEOUT_MILLIS = 5000;
}

class FilePointer
{
public:

    FilePointer () = delete;

    FilePointer (const FilePointer&) = delete;

    FilePointer (FILE *file_p)
    : _file_p (file_p)
    {
    }

    ~FilePointer ()
    {
        if (_file_p != nullptr)
        {
            const int close_result = fclose (_file_p);
            EXPECT_EQ(0, close_result);
        }
    }

    operator FILE* ()
    {
        return _file_p;
    }

private:

    FILE *_file_p;

};

/**
 Wraps an Editline class, providing a simple way to feed
 input (as if from the keyboard) and receive output from Editline.
 */
class EditlineAdapter
{
public:

    EditlineAdapter ();

    void
    CloseInput ();

    bool
    IsValid () const
    {
        return _editline_sp.get () != nullptr;
    }

    lldb_private::Editline&
    GetEditline ()
    {
        return *_editline_sp;
    }

    bool
    SendLine (const std::string &line);

    bool
    SendLines (const std::vector<std::string> &lines);

    bool
    GetLine (std::string &line, bool &interrupted, size_t timeout_millis);

    bool
    GetLines (lldb_private::StringList &lines, bool &interrupted, size_t timeout_millis);

    void
    ConsumeAllOutput ();

private:

    static bool
    IsInputComplete (
        lldb_private::Editline * editline,
        lldb_private::StringList & lines,
        void * baton);

    std::unique_ptr<lldb_private::Editline> _editline_sp;

    lldb_utility::PseudoTerminal _pty;
    int _pty_master_fd;
    int _pty_slave_fd;

    std::unique_ptr<FilePointer> _el_slave_file;
};

EditlineAdapter::EditlineAdapter () :
    _editline_sp (),
    _pty (),
    _pty_master_fd (-1),
    _pty_slave_fd (-1),
    _el_slave_file ()
{
    lldb_private::Error error;

    // Open the first master pty available.
    char error_string[256];
    error_string[0] = '\0';
    if (!_pty.OpenFirstAvailableMaster (O_RDWR, error_string, sizeof (error_string)))
    {
        fprintf(stderr, "failed to open first available master pty: '%s'\n", error_string);
        return;
    }

    // Grab the master fd.  This is a file descriptor we will:
    // (1) write to when we want to send input to editline.
    // (2) read from when we want to see what editline sends back.
    _pty_master_fd = _pty.GetMasterFileDescriptor();

    // Open the corresponding slave pty.
    if (!_pty.OpenSlave (O_RDWR, error_string, sizeof (error_string)))
    {
        fprintf(stderr, "failed to open slave pty: '%s'\n", error_string);
        return;
    }
    _pty_slave_fd = _pty.GetSlaveFileDescriptor();

    _el_slave_file.reset (new FilePointer (fdopen (_pty_slave_fd, "rw")));
    EXPECT_FALSE (nullptr == *_el_slave_file);
    if (*_el_slave_file == nullptr)
        return;

    // Create an Editline instance.
    _editline_sp.reset (new lldb_private::Editline("gtest editor", *_el_slave_file, *_el_slave_file, *_el_slave_file, false));
    _editline_sp->SetPrompt ("> ");

    // Hookup our input complete callback.
    _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this);
}

void
EditlineAdapter::CloseInput ()
{
    if (_el_slave_file != nullptr)
        _el_slave_file.reset (nullptr);
}

bool
EditlineAdapter::SendLine (const std::string &line)
{
    // Ensure we're valid before proceeding.
    if (!IsValid ())
        return false;

    // Write the line out to the pipe connected to editline's input.
    ssize_t input_bytes_written =
        ::write (_pty_master_fd,
                 line.c_str(),
                 line.length() * sizeof (std::string::value_type));

    const char *eoln = "\n";
    const size_t eoln_length = strlen(eoln);
    input_bytes_written =
        ::write (_pty_master_fd,
                 eoln,
                 eoln_length * sizeof (char));

    EXPECT_EQ (eoln_length * sizeof (char), input_bytes_written);
    return eoln_length * sizeof (char) == input_bytes_written;
}

bool
EditlineAdapter::SendLines (const std::vector<std::string> &lines)
{
    for (auto &line : lines)
    {
#if EDITLINE_TEST_DUMP_OUTPUT
        printf ("<stdin> sending line \"%s\"\n", line.c_str());
#endif
        if (!SendLine (line))
            return false;
    }
    return true;
}

// We ignore the timeout for now.
bool
EditlineAdapter::GetLine (std::string &line, bool &interrupted, size_t /* timeout_millis */)
{
    // Ensure we're valid before proceeding.
    if (!IsValid ())
        return false;

    _editline_sp->GetLine (line, interrupted);
    return true;
}

bool
EditlineAdapter::GetLines (lldb_private::StringList &lines, bool &interrupted, size_t /* timeout_millis */)
{
    // Ensure we're valid before proceeding.
    if (!IsValid ())
        return false;

    _editline_sp->GetLines (1, lines, interrupted);
    return true;
}

bool
EditlineAdapter::IsInputComplete (
        lldb_private::Editline * editline,
        lldb_private::StringList & lines,
        void * baton)
{
    // We'll call ourselves complete if we've received a balanced set of braces.
    int start_block_count = 0;
    int brace_balance = 0;

    for (size_t i = 0; i < lines.GetSize (); ++i)
    {
        for (auto ch : lines[i])
        {
            if (ch == '{')
            {
                ++start_block_count;
                ++brace_balance;
            }
            else if (ch == '}')
                --brace_balance;
        }
    }

    return (start_block_count > 0) && (brace_balance == 0);
}

void
EditlineAdapter::ConsumeAllOutput ()
{
    FilePointer output_file (fdopen (_pty_master_fd, "r"));

    int ch;
    while ((ch = fgetc(output_file)) != EOF)
    {
#if EDITLINE_TEST_DUMP_OUTPUT
        char display_str[] = { 0, 0, 0 };
        switch (ch)
        {
            case '\t':
                display_str[0] = '\\';
                display_str[1] = 't';
                break;
            case '\n':
                display_str[0] = '\\';
                display_str[1] = 'n';
                break;
            case '\r':
                display_str[0] = '\\';
                display_str[1] = 'r';
                break;
            default:
                display_str[0] = ch;
                break;
        }
        printf ("<stdout> 0x%02x (%03d) (%s)\n", ch, ch, display_str);
        // putc(ch, stdout);
#endif
    }
}

TEST (EditlineTest, EditlineReceivesSingleLineText)
{
    setenv ("TERM", "vt100", 1);

    // Create an editline.
    EditlineAdapter el_adapter;
    EXPECT_TRUE (el_adapter.IsValid ());
    if (!el_adapter.IsValid ())
        return;

    // Dump output.
    std::thread el_output_thread( [&] { el_adapter.ConsumeAllOutput (); });

    // Send it some text via our virtual keyboard.
    const std::string input_text ("Hello, world");
    EXPECT_TRUE (el_adapter.SendLine (input_text));

    // Verify editline sees what we put in.
    std::string el_reported_line;
    bool input_interrupted = false;
    const bool received_line = el_adapter.GetLine (el_reported_line, input_interrupted, TIMEOUT_MILLIS);

    EXPECT_TRUE (received_line);
    EXPECT_FALSE (input_interrupted);
    EXPECT_EQ (input_text, el_reported_line);

    el_adapter.CloseInput();
    el_output_thread.join();
}

TEST (EditlineTest, EditlineReceivesMultiLineText)
{
    setenv ("TERM", "vt100", 1);

    // Create an editline.
    EditlineAdapter el_adapter;
    EXPECT_TRUE (el_adapter.IsValid ());
    if (!el_adapter.IsValid ())
        return;

    // Stick editline output/error dumpers on separate threads.
    std::thread el_output_thread( [&] { el_adapter.ConsumeAllOutput (); });

    // Send it some text via our virtual keyboard.
    std::vector<std::string> input_lines;
    input_lines.push_back ("int foo()");
    input_lines.push_back ("{");
    input_lines.push_back ("printf(\"Hello, world\");");
    input_lines.push_back ("}");
    input_lines.push_back ("");

    EXPECT_TRUE (el_adapter.SendLines (input_lines));

    // Verify editline sees what we put in.
    lldb_private::StringList el_reported_lines;
    bool input_interrupted = false;

    EXPECT_TRUE (el_adapter.GetLines (el_reported_lines, input_interrupted, TIMEOUT_MILLIS));
    EXPECT_FALSE (input_interrupted);

    // Without any auto indentation support, our output should directly match our input.
    EXPECT_EQ (input_lines.size (), el_reported_lines.GetSize ());
    if (input_lines.size () == el_reported_lines.GetSize ())
    {
        for (auto i = 0; i < input_lines.size(); ++i)
            EXPECT_EQ (input_lines[i], el_reported_lines[i]);
    }

    el_adapter.CloseInput();
    el_output_thread.join();
}

#endif
<commit_msg>added test fixture to EditlineTest gtest<commit_after>//===-- EditlineTest.cpp ----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_DISABLE_LIBEDIT

#define EDITLINE_TEST_DUMP_OUTPUT 0

#include <stdio.h>
#include <unistd.h>

#include <memory>
#include <thread>

#include "gtest/gtest.h"

#include "lldb/Core/Error.h"
#include "lldb/Core/StringList.h"
#include "lldb/Host/Editline.h"
#include "lldb/Host/Pipe.h"
#include "lldb/Utility/PseudoTerminal.h"

namespace
{
    const size_t TIMEOUT_MILLIS = 5000;
}

class FilePointer
{
public:

    FilePointer () = delete;

    FilePointer (const FilePointer&) = delete;

    FilePointer (FILE *file_p)
    : _file_p (file_p)
    {
    }

    ~FilePointer ()
    {
        if (_file_p != nullptr)
        {
            const int close_result = fclose (_file_p);
            EXPECT_EQ(0, close_result);
        }
    }

    operator FILE* ()
    {
        return _file_p;
    }

private:

    FILE *_file_p;

};

/**
 Wraps an Editline class, providing a simple way to feed
 input (as if from the keyboard) and receive output from Editline.
 */
class EditlineAdapter
{
public:

    EditlineAdapter ();

    void
    CloseInput ();

    bool
    IsValid () const
    {
        return _editline_sp.get () != nullptr;
    }

    lldb_private::Editline&
    GetEditline ()
    {
        return *_editline_sp;
    }

    bool
    SendLine (const std::string &line);

    bool
    SendLines (const std::vector<std::string> &lines);

    bool
    GetLine (std::string &line, bool &interrupted, size_t timeout_millis);

    bool
    GetLines (lldb_private::StringList &lines, bool &interrupted, size_t timeout_millis);

    void
    ConsumeAllOutput ();

private:

    static bool
    IsInputComplete (
        lldb_private::Editline * editline,
        lldb_private::StringList & lines,
        void * baton);

    std::unique_ptr<lldb_private::Editline> _editline_sp;

    lldb_utility::PseudoTerminal _pty;
    int _pty_master_fd;
    int _pty_slave_fd;

    std::unique_ptr<FilePointer> _el_slave_file;
};

EditlineAdapter::EditlineAdapter () :
    _editline_sp (),
    _pty (),
    _pty_master_fd (-1),
    _pty_slave_fd (-1),
    _el_slave_file ()
{
    lldb_private::Error error;

    // Open the first master pty available.
    char error_string[256];
    error_string[0] = '\0';
    if (!_pty.OpenFirstAvailableMaster (O_RDWR, error_string, sizeof (error_string)))
    {
        fprintf(stderr, "failed to open first available master pty: '%s'\n", error_string);
        return;
    }

    // Grab the master fd.  This is a file descriptor we will:
    // (1) write to when we want to send input to editline.
    // (2) read from when we want to see what editline sends back.
    _pty_master_fd = _pty.GetMasterFileDescriptor();

    // Open the corresponding slave pty.
    if (!_pty.OpenSlave (O_RDWR, error_string, sizeof (error_string)))
    {
        fprintf(stderr, "failed to open slave pty: '%s'\n", error_string);
        return;
    }
    _pty_slave_fd = _pty.GetSlaveFileDescriptor();

    _el_slave_file.reset (new FilePointer (fdopen (_pty_slave_fd, "rw")));
    EXPECT_FALSE (nullptr == *_el_slave_file);
    if (*_el_slave_file == nullptr)
        return;

    // Create an Editline instance.
    _editline_sp.reset (new lldb_private::Editline("gtest editor", *_el_slave_file, *_el_slave_file, *_el_slave_file, false));
    _editline_sp->SetPrompt ("> ");

    // Hookup our input complete callback.
    _editline_sp->SetIsInputCompleteCallback(IsInputComplete, this);
}

void
EditlineAdapter::CloseInput ()
{
    if (_el_slave_file != nullptr)
        _el_slave_file.reset (nullptr);
}

bool
EditlineAdapter::SendLine (const std::string &line)
{
    // Ensure we're valid before proceeding.
    if (!IsValid ())
        return false;

    // Write the line out to the pipe connected to editline's input.
    ssize_t input_bytes_written =
        ::write (_pty_master_fd,
                 line.c_str(),
                 line.length() * sizeof (std::string::value_type));

    const char *eoln = "\n";
    const size_t eoln_length = strlen(eoln);
    input_bytes_written =
        ::write (_pty_master_fd,
                 eoln,
                 eoln_length * sizeof (char));

    EXPECT_EQ (eoln_length * sizeof (char), input_bytes_written);
    return eoln_length * sizeof (char) == input_bytes_written;
}

bool
EditlineAdapter::SendLines (const std::vector<std::string> &lines)
{
    for (auto &line : lines)
    {
#if EDITLINE_TEST_DUMP_OUTPUT
        printf ("<stdin> sending line \"%s\"\n", line.c_str());
#endif
        if (!SendLine (line))
            return false;
    }
    return true;
}

// We ignore the timeout for now.
bool
EditlineAdapter::GetLine (std::string &line, bool &interrupted, size_t /* timeout_millis */)
{
    // Ensure we're valid before proceeding.
    if (!IsValid ())
        return false;

    _editline_sp->GetLine (line, interrupted);
    return true;
}

bool
EditlineAdapter::GetLines (lldb_private::StringList &lines, bool &interrupted, size_t /* timeout_millis */)
{
    // Ensure we're valid before proceeding.
    if (!IsValid ())
        return false;

    _editline_sp->GetLines (1, lines, interrupted);
    return true;
}

bool
EditlineAdapter::IsInputComplete (
        lldb_private::Editline * editline,
        lldb_private::StringList & lines,
        void * baton)
{
    // We'll call ourselves complete if we've received a balanced set of braces.
    int start_block_count = 0;
    int brace_balance = 0;

    for (size_t i = 0; i < lines.GetSize (); ++i)
    {
        for (auto ch : lines[i])
        {
            if (ch == '{')
            {
                ++start_block_count;
                ++brace_balance;
            }
            else if (ch == '}')
                --brace_balance;
        }
    }

    return (start_block_count > 0) && (brace_balance == 0);
}

void
EditlineAdapter::ConsumeAllOutput ()
{
    FilePointer output_file (fdopen (_pty_master_fd, "r"));

    int ch;
    while ((ch = fgetc(output_file)) != EOF)
    {
#if EDITLINE_TEST_DUMP_OUTPUT
        char display_str[] = { 0, 0, 0 };
        switch (ch)
        {
            case '\t':
                display_str[0] = '\\';
                display_str[1] = 't';
                break;
            case '\n':
                display_str[0] = '\\';
                display_str[1] = 'n';
                break;
            case '\r':
                display_str[0] = '\\';
                display_str[1] = 'r';
                break;
            default:
                display_str[0] = ch;
                break;
        }
        printf ("<stdout> 0x%02x (%03d) (%s)\n", ch, ch, display_str);
        // putc(ch, stdout);
#endif
    }
}

class EditlineTestFixture : public ::testing::Test
{
private:
    EditlineAdapter _el_adapter;
    std::shared_ptr<std::thread> _sp_output_thread;

public:
    void SetUp()
    {
        // We need a TERM set properly for editline to work as expected.
        setenv("TERM", "vt100", 1);

        // Validate the editline adapter.
        EXPECT_TRUE(_el_adapter.IsValid());
        if (!_el_adapter.IsValid())
            return;

        // Dump output.
        _sp_output_thread.reset(new std::thread([&] { _el_adapter.ConsumeAllOutput(); }));
    }

    void TearDown()
    {
        _el_adapter.CloseInput();
        if (_sp_output_thread)
            _sp_output_thread->join();
    }

    EditlineAdapter &GetEditlineAdapter() { return _el_adapter; }
};

TEST_F(EditlineTestFixture, EditlineReceivesSingleLineText)
{
    // Send it some text via our virtual keyboard.
    const std::string input_text ("Hello, world");
    EXPECT_TRUE(GetEditlineAdapter().SendLine(input_text));

    // Verify editline sees what we put in.
    std::string el_reported_line;
    bool input_interrupted = false;
    const bool received_line = GetEditlineAdapter().GetLine(el_reported_line, input_interrupted, TIMEOUT_MILLIS);

    EXPECT_TRUE (received_line);
    EXPECT_FALSE (input_interrupted);
    EXPECT_EQ (input_text, el_reported_line);
}

TEST_F(EditlineTestFixture, EditlineReceivesMultiLineText)
{
    // Send it some text via our virtual keyboard.
    std::vector<std::string> input_lines;
    input_lines.push_back ("int foo()");
    input_lines.push_back ("{");
    input_lines.push_back ("printf(\"Hello, world\");");
    input_lines.push_back ("}");
    input_lines.push_back ("");

    EXPECT_TRUE(GetEditlineAdapter().SendLines(input_lines));

    // Verify editline sees what we put in.
    lldb_private::StringList el_reported_lines;
    bool input_interrupted = false;

    EXPECT_TRUE(GetEditlineAdapter().GetLines(el_reported_lines, input_interrupted, TIMEOUT_MILLIS));
    EXPECT_FALSE (input_interrupted);

    // Without any auto indentation support, our output should directly match our input.
    EXPECT_EQ (input_lines.size (), el_reported_lines.GetSize ());
    if (input_lines.size () == el_reported_lines.GetSize ())
    {
        for (auto i = 0; i < input_lines.size(); ++i)
            EXPECT_EQ (input_lines[i], el_reported_lines[i]);
    }
}

#endif
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * Version: MPL 1.1 / GPLv3+ / LGPLv3+
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License or as specified alternatively below. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Major Contributor(s):
 *  Copyright (C) 2011 Markus Mohrhard <markus.mohrhard@googlemail.com> (initial developer)
 *
 * All Rights Reserved.
 *
 * For minor contributions see the git repository.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
 * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
 * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
 * instead of those above.
 */

#include <rtl/ustring.hxx>
#include "unotest/detail/unotestdllapi.hxx"

#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/frame/XDesktop.hpp>

struct TestMacroInfo
{
    rtl::OUString sFileBaseName;
    rtl::OUString sMacroUrl;
};

namespace unotest {

class OOO_DLLPUBLIC_UNOTEST MacrosTest
{
public:
    com::sun::star::uno::Reference< com::sun::star::lang::XComponent > loadFromDesktop(const rtl::OUString& rURL);

protected:
    com::sun::star::uno::Reference< com::sun::star::frame::XDesktop> mxDesktop;
};

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>unotest: missing include guard in macros_test.hxx<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * Version: MPL 1.1 / GPLv3+ / LGPLv3+
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License or as specified alternatively below. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Major Contributor(s):
 *  Copyright (C) 2011 Markus Mohrhard <markus.mohrhard@googlemail.com> (initial developer)
 *
 * All Rights Reserved.
 *
 * For minor contributions see the git repository.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
 * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
 * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
 * instead of those above.
 */

#ifndef INCLUDED_UNOTEST_MACROS_TEST_HXX
#define INCLUDED_UNOTEST_MACROS_TEST_HXX

#include <rtl/ustring.hxx>
#include "unotest/detail/unotestdllapi.hxx"

#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/frame/XDesktop.hpp>

struct TestMacroInfo
{
    rtl::OUString sFileBaseName;
    rtl::OUString sMacroUrl;
};

namespace unotest {

class OOO_DLLPUBLIC_UNOTEST MacrosTest
{
public:
    com::sun::star::uno::Reference< com::sun::star::lang::XComponent > loadFromDesktop(const rtl::OUString& rURL);

protected:
    com::sun::star::uno::Reference< com::sun::star::frame::XDesktop> mxDesktop;
};

}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>