<commit_before>/*
The MIT License (MIT)

Copyright (c) 2013-2014 winlin

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <srs_app_http_conn.hpp>

#ifdef SRS_HTTP_SERVER

#include <sstream>
using namespace std;

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <srs_kernel_log.hpp>
#include <srs_kernel_error.hpp>
#include <srs_app_socket.hpp>
#include <srs_app_http.hpp>
#include <srs_core_autofree.hpp>
#include <srs_app_json.hpp>
#include <srs_app_config.hpp>

#define SRS_HTTP_DEFAULT_PAGE "index.html"

SrsHttpRoot::SrsHttpRoot()
{
    // TODO: FIXME: support reload vhosts.
}

SrsHttpRoot::~SrsHttpRoot()
{
}

int SrsHttpRoot::initialize()
{
    int ret = ERROR_SUCCESS;

    bool default_root_exists = false;

    // add other virtual path
    SrsConfDirective* root = _srs_config->get_root();
    for (int i = 0; i < (int)root->directives.size(); i++) {
        SrsConfDirective* conf = root->at(i);

        if (!conf->is_vhost()) {
            continue;
        }

        std::string vhost = conf->arg0();
        if (!_srs_config->get_vhost_http_enabled(vhost)) {
            continue;
        }

        std::string mount = _srs_config->get_vhost_http_mount(vhost);
        std::string dir = _srs_config->get_vhost_http_dir(vhost);

        handlers.push_back(new SrsHttpVhost(vhost, mount, dir));

        if (mount == "/") {
            default_root_exists = true;
        }
    }

    if (!default_root_exists) {
        // add root
        handlers.push_back(new SrsHttpVhost(
            "__http__", "/", _srs_config->get_http_stream_dir()));
    }

    return ret;
}

int SrsHttpRoot::best_match(const char* path, int length, SrsHttpHandlerMatch** ppmatch)
{
    int ret = ERROR_SUCCESS;

    // find the best matched child handler.
    std::vector<SrsHttpHandler*>::iterator it;
    for (it = handlers.begin(); it != handlers.end(); ++it) {
        SrsHttpHandler* h = *it;

        // search all child handlers.
        h->best_match(path, length, ppmatch);
    }

    // if already matched by child, return.
    if (*ppmatch) {
        return ret;
    }

    // not matched, error.
    return ERROR_HTTP_HANDLER_MATCH_URL;
}

bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase)
{
    status_code = HTTP_InternalServerError;
    reason_phrase = HTTP_InternalServerError_str;

    return false;
}

int SrsHttpRoot::do_process_request(SrsSocket* skt, SrsHttpMessage* req)
{
    int ret = ERROR_SUCCESS;
    return ret;
}

SrsHttpVhost::SrsHttpVhost(std::string vhost, std::string mount, std::string dir)
{
    _vhost = vhost;
    _mount = mount;
    _dir = dir;
}

SrsHttpVhost::~SrsHttpVhost()
{
}

bool SrsHttpVhost::can_handle(const char* path, int length, const char** /*pchild*/)
{
    return srs_path_like(_mount.c_str(), path, length);
}

bool SrsHttpVhost::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase)
{
    std::string fullpath = get_request_file(req);

    if (::access(fullpath.c_str(), F_OK | R_OK) < 0) {
        srs_warn("check file %s does not exists", fullpath.c_str());

        status_code = HTTP_NotFound;
        reason_phrase = HTTP_NotFound_str;
        return false;
    }

    return true;
}

int SrsHttpVhost::do_process_request(SrsSocket* skt, SrsHttpMessage* req)
{
    int ret = ERROR_SUCCESS;

    std::string fullpath = get_request_file(req);

    int fd = ::open(fullpath.c_str(), O_RDONLY);
    if (fd < 0) {
        ret = ERROR_HTTP_OPEN_FILE;
        srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }

    int64_t length = (int64_t)::lseek(fd, 0, SEEK_END);
    ::lseek(fd, 0, SEEK_SET);

    char* buf = new char[length];
    SrsAutoFree(char, buf, true);

    if (::read(fd, buf, length) < 0) {
        ::close(fd);
        ret = ERROR_HTTP_READ_FILE;
        srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }
    ::close(fd);

    std::string str;
    str.append(buf, length);

    if (srs_string_ends_with(fullpath, ".ts")) {
        return res_mpegts(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".m3u8")) {
        return res_m3u8(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".xml")) {
        return res_xml(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".js")) {
        return res_javascript(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".json")) {
        return res_json(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".swf")) {
        return res_swf(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".css")) {
        return res_css(skt, req, str);
    } else {
        return res_text(skt, req, str);
    }

    return ret;
}

string SrsHttpVhost::get_request_file(SrsHttpMessage* req)
{
    std::string fullpath = _dir + "/";

    // if root, directly use the matched url.
    if (_mount == "/") {
        // add the dir
        fullpath += req->match()->matched_url;
        // if file speicified, add the file.
        if (!req->match()->unmatched_url.empty()) {
            fullpath += "/" + req->match()->unmatched_url;
        }
    } else {
        // virtual path, ignore the virutal path.
        fullpath += req->match()->unmatched_url;
    }

    // add default pages.
    if (srs_string_ends_with(fullpath, "/")) {
        fullpath += SRS_HTTP_DEFAULT_PAGE;
    }

    return fullpath;
}

string SrsHttpVhost::vhost()
{
    return _vhost;
}

string SrsHttpVhost::mount()
{
    return _mount;
}

string SrsHttpVhost::dir()
{
    return _dir;
}

SrsHttpConn::SrsHttpConn(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler)
    : SrsConnection(srs_server, client_stfd)
{
    parser = new SrsHttpParser();
    handler = _handler;
    requires_crossdomain = false;
}

SrsHttpConn::~SrsHttpConn()
{
    srs_freep(parser);
}

int SrsHttpConn::do_cycle()
{
    int ret = ERROR_SUCCESS;

    if ((ret = get_peer_ip()) != ERROR_SUCCESS) {
        srs_error("get peer ip failed. ret=%d", ret);
        return ret;
    }
    srs_trace("http get peer ip success. ip=%s", ip);

    // initialize parser
    if ((ret = parser->initialize(HTTP_REQUEST)) != ERROR_SUCCESS) {
        srs_error("http initialize http parser failed. ret=%d", ret);
        return ret;
    }

    // underlayer socket
    SrsSocket skt(stfd);

    // process http messages.
    for (;;) {
        SrsHttpMessage* req = NULL;

        // get a http message
        if ((ret = parser->parse_message(&skt, &req)) != ERROR_SUCCESS) {
            return ret;
        }

        // if SUCCESS, always NOT-NULL and completed message.
        srs_assert(req);
        srs_assert(req->is_complete());

        // always free it in this scope.
        SrsAutoFree(SrsHttpMessage, req, false);

        // ok, handle http request.
        if ((ret = process_request(&skt, req)) != ERROR_SUCCESS) {
            return ret;
        }
    }

    return ret;
}

int SrsHttpConn::process_request(SrsSocket* skt, SrsHttpMessage* req)
{
    int ret = ERROR_SUCCESS;

    // parse uri to schema/server:port/path?query
    if ((ret = req->parse_uri()) != ERROR_SUCCESS) {
        return ret;
    }

    srs_trace("http request parsed, method=%d, url=%s, content-length=%"PRId64"",
        req->method(), req->url().c_str(), req->content_length());

    // TODO: maybe need to parse the url.
    std::string url = req->path();

    SrsHttpHandlerMatch* p = NULL;
    if ((ret = handler->best_match(url.data(), url.length(), &p)) != ERROR_SUCCESS) {
        srs_warn("failed to find the best match handler for url. ret=%d", ret);
        return ret;
    }

    // if success, p and pstart should be valid.
    srs_assert(p);
    srs_assert(p->handler);
    srs_assert(p->matched_url.length() <= url.length());
    srs_info("best match handler, matched_url=%s", p->matched_url.c_str());

    req->set_match(p);
    req->set_requires_crossdomain(requires_crossdomain);

    // use handler to process request.
    if ((ret = p->handler->process_request(skt, req)) != ERROR_SUCCESS) {
        srs_warn("handler failed to process http request. ret=%d", ret);
        return ret;
    }

    if (req->requires_crossdomain()) {
        requires_crossdomain = true;
    }

    return ret;
}

#endif
<commit_msg>add comments for http conn<commit_after>/*
The MIT License (MIT)

Copyright (c) 2013-2014 winlin

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <srs_app_http_conn.hpp>

#ifdef SRS_HTTP_SERVER

#include <sstream>
using namespace std;

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <srs_kernel_log.hpp>
#include <srs_kernel_error.hpp>
#include <srs_app_socket.hpp>
#include <srs_app_http.hpp>
#include <srs_core_autofree.hpp>
#include <srs_app_json.hpp>
#include <srs_app_config.hpp>

#define SRS_HTTP_DEFAULT_PAGE "index.html"

SrsHttpRoot::SrsHttpRoot()
{
    // TODO: FIXME: support reload vhosts.
}

SrsHttpRoot::~SrsHttpRoot()
{
}

int SrsHttpRoot::initialize()
{
    int ret = ERROR_SUCCESS;

    bool default_root_exists = false;

    // add other virtual path
    SrsConfDirective* root = _srs_config->get_root();
    for (int i = 0; i < (int)root->directives.size(); i++) {
        SrsConfDirective* conf = root->at(i);

        if (!conf->is_vhost()) {
            continue;
        }

        std::string vhost = conf->arg0();
        if (!_srs_config->get_vhost_http_enabled(vhost)) {
            continue;
        }

        std::string mount = _srs_config->get_vhost_http_mount(vhost);
        std::string dir = _srs_config->get_vhost_http_dir(vhost);

        handlers.push_back(new SrsHttpVhost(vhost, mount, dir));

        if (mount == "/") {
            default_root_exists = true;
        }
    }

    if (!default_root_exists) {
        // add root
        handlers.push_back(new SrsHttpVhost(
            "__http__", "/", _srs_config->get_http_stream_dir()));
    }

    return ret;
}

int SrsHttpRoot::best_match(const char* path, int length, SrsHttpHandlerMatch** ppmatch)
{
    int ret = ERROR_SUCCESS;

    // find the best matched child handler.
    std::vector<SrsHttpHandler*>::iterator it;
    for (it = handlers.begin(); it != handlers.end(); ++it) {
        SrsHttpHandler* h = *it;

        // search all child handlers.
        h->best_match(path, length, ppmatch);
    }

    // if already matched by child, return.
    if (*ppmatch) {
        return ret;
    }

    // not matched, error.
    return ERROR_HTTP_HANDLER_MATCH_URL;
}

bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase)
{
    status_code = HTTP_InternalServerError;
    reason_phrase = HTTP_InternalServerError_str;

    return false;
}

int SrsHttpRoot::do_process_request(SrsSocket* skt, SrsHttpMessage* req)
{
    int ret = ERROR_SUCCESS;
    return ret;
}

SrsHttpVhost::SrsHttpVhost(std::string vhost, std::string mount, std::string dir)
{
    _vhost = vhost;
    _mount = mount;
    _dir = dir;
}

SrsHttpVhost::~SrsHttpVhost()
{
}

bool SrsHttpVhost::can_handle(const char* path, int length, const char** /*pchild*/)
{
    return srs_path_like(_mount.c_str(), path, length);
}

bool SrsHttpVhost::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase)
{
    std::string fullpath = get_request_file(req);

    if (::access(fullpath.c_str(), F_OK | R_OK) < 0) {
        srs_warn("check file %s does not exists", fullpath.c_str());

        status_code = HTTP_NotFound;
        reason_phrase = HTTP_NotFound_str;
        return false;
    }

    return true;
}

int SrsHttpVhost::do_process_request(SrsSocket* skt, SrsHttpMessage* req)
{
    int ret = ERROR_SUCCESS;

    std::string fullpath = get_request_file(req);

    int fd = ::open(fullpath.c_str(), O_RDONLY);
    if (fd < 0) {
        ret = ERROR_HTTP_OPEN_FILE;
        srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }

    int64_t length = (int64_t)::lseek(fd, 0, SEEK_END);
    ::lseek(fd, 0, SEEK_SET);

    char* buf = new char[length];
    SrsAutoFree(char, buf, true);

    // TODO: FIXME: use st_read.
    if (::read(fd, buf, length) < 0) {
        ::close(fd);
        ret = ERROR_HTTP_READ_FILE;
        srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }
    ::close(fd);

    std::string str;
    str.append(buf, length);

    if (srs_string_ends_with(fullpath, ".ts")) {
        return res_mpegts(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".m3u8")) {
        return res_m3u8(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".xml")) {
        return res_xml(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".js")) {
        return res_javascript(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".json")) {
        return res_json(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".swf")) {
        return res_swf(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".css")) {
        return res_css(skt, req, str);
    } else {
        return res_text(skt, req, str);
    }

    return ret;
}

string SrsHttpVhost::get_request_file(SrsHttpMessage* req)
{
    std::string fullpath = _dir + "/";

    // if root, directly use the matched url.
    if (_mount == "/") {
        // add the dir
        fullpath += req->match()->matched_url;
        // if file speicified, add the file.
        if (!req->match()->unmatched_url.empty()) {
            fullpath += "/" + req->match()->unmatched_url;
        }
    } else {
        // virtual path, ignore the virutal path.
        fullpath += req->match()->unmatched_url;
    }

    // add default pages.
    if (srs_string_ends_with(fullpath, "/")) {
        fullpath += SRS_HTTP_DEFAULT_PAGE;
    }

    return fullpath;
}

string SrsHttpVhost::vhost()
{
    return _vhost;
}

string SrsHttpVhost::mount()
{
    return _mount;
}

string SrsHttpVhost::dir()
{
    return _dir;
}

SrsHttpConn::SrsHttpConn(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler)
    : SrsConnection(srs_server, client_stfd)
{
    parser = new SrsHttpParser();
    handler = _handler;
    requires_crossdomain = false;
}

SrsHttpConn::~SrsHttpConn()
{
    srs_freep(parser);
}

int SrsHttpConn::do_cycle()
{
    int ret = ERROR_SUCCESS;

    if ((ret = get_peer_ip()) != ERROR_SUCCESS) {
        srs_error("get peer ip failed. ret=%d", ret);
        return ret;
    }
    srs_trace("http get peer ip success. ip=%s", ip);

    // initialize parser
    if ((ret = parser->initialize(HTTP_REQUEST)) != ERROR_SUCCESS) {
        srs_error("http initialize http parser failed. ret=%d", ret);
        return ret;
    }

    // underlayer socket
    SrsSocket skt(stfd);

    // process http messages.
    for (;;) {
        SrsHttpMessage* req = NULL;

        // get a http message
        if ((ret = parser->parse_message(&skt, &req)) != ERROR_SUCCESS) {
            return ret;
        }

        // if SUCCESS, always NOT-NULL and completed message.
        srs_assert(req);
        srs_assert(req->is_complete());

        // always free it in this scope.
        SrsAutoFree(SrsHttpMessage, req, false);

        // ok, handle http request.
        if ((ret = process_request(&skt, req)) != ERROR_SUCCESS) {
            return ret;
        }
    }

    return ret;
}

int SrsHttpConn::process_request(SrsSocket* skt, SrsHttpMessage* req)
{
    int ret = ERROR_SUCCESS;

    // parse uri to schema/server:port/path?query
    if ((ret = req->parse_uri()) != ERROR_SUCCESS) {
        return ret;
    }

    srs_trace("http request parsed, method=%d, url=%s, content-length=%"PRId64"",
        req->method(), req->url().c_str(), req->content_length());

    // TODO: maybe need to parse the url.
    std::string url = req->path();

    SrsHttpHandlerMatch* p = NULL;
    if ((ret = handler->best_match(url.data(), url.length(), &p)) != ERROR_SUCCESS) {
        srs_warn("failed to find the best match handler for url. ret=%d", ret);
        return ret;
    }

    // if success, p and pstart should be valid.
    srs_assert(p);
    srs_assert(p->handler);
    srs_assert(p->matched_url.length() <= url.length());
    srs_info("best match handler, matched_url=%s", p->matched_url.c_str());

    req->set_match(p);
    req->set_requires_crossdomain(requires_crossdomain);

    // use handler to process request.
    if ((ret = p->handler->process_request(skt, req)) != ERROR_SUCCESS) {
        srs_warn("handler failed to process http request. ret=%d", ret);
        return ret;
    }

    if (req->requires_crossdomain()) {
        requires_crossdomain = true;
    }

    return ret;
}

#endif
<|endoftext|>


<commit_before>#include <xcb/xcb.h>
#include <cstdio>
#include <ev++.h>
#include <cstring>
#include <memory>
#include <wordexp.h>
#include <basedir.h>
#include <basedir_fs.h>
#include <getopt.h>

#include "rwte/config.h"
#include "rwte/renderer.h"
#include "rwte/rwte.h"
#include "rwte/logging.h"
#include "rwte/sigwatcher.h"
#include "rwte/term.h"
#include "rwte/tty.h"
#include "rwte/window.h"
#include "lua/config.h"
#include "lua/state.h"
#include "lua/logging.h"
#include "lua/term.h"
#include "lua/window.h"
#include "rwte/version.h"

// globals
Window window;
Options options;
Rwte rwte;
std::unique_ptr<Term> g_term;
std::unique_ptr<Tty> g_tty;
lua_State * g_L = NULL;

#define LOGGER() (logging::get("rwte"))

#define MIN(a, b) ((a) < (b)? (a) : (b))
#define MAX(a, b) ((a) < (b)? (b) : (a))

// default values to use if we don't have
// a default value in config
static const float DEFAULT_BLINK_RATE = 0.6;

Options::Options() :
    cmd(0),
    title("rwte"),
    noalt(false)
{ }

Rwte::Rwte() :
    m_lua(std::make_shared<lua::State>())
{
    m_lua->openlibs();

    m_child.set<Rwte,&Rwte::childcb>(this);
    m_flush.set<Rwte,&Rwte::flushcb>(this);
    m_blink.set<Rwte,&Rwte::blinkcb>(this);
}

void Rwte::resize(uint16_t width, uint16_t height)
{
    if (width == 0)
        width = window.width();
    if (height == 0)
        height = window.height();

    if (window.width() != width || window.height() != height)
    {
        window.resize(width, height);
        g_term->resize(window.cols(), window.rows());

        if (g_tty)
            g_tty->resize();
    }
}

void Rwte::watch_child(pid_t pid)
{
    LOGGER()->debug("watching child {}", pid);
    m_child.start(pid);
}

void Rwte::refresh()
{
    if (!m_flush.is_active())
        m_flush.start(1.0/60.0);
}

void Rwte::start_blink()
{
    if (!m_blink.is_active())
    {
        float rate = lua::config::get_float(
                "blink_rate", DEFAULT_BLINK_RATE);

        m_blink.start(rate, rate);
    }
    else
    {
        // reset the timer if it's already active
        // (so we don't blink until idle)
        m_blink.stop();
        m_blink.start();
    }
}

void Rwte::stop_blink()
{
    if (m_blink.is_active())
        m_blink.stop();
}

void Rwte::childcb(ev::child &w, int)
{
    if (!WIFEXITED(w.rstatus) || WEXITSTATUS(w.rstatus))
        LOGGER()->warn("child finished with error {}", w.rstatus);
    else
        LOGGER()->info("child exited");
    w.loop.break_loop(ev::ALL);
}

void Rwte::flushcb(ev::timer &, int)
{
    window.draw();
}

void Rwte::blinkcb(ev::timer &, int)
{
    g_term->blink();
}

static void add_to_search_path(lua::State *L, const std::vector<std::string>& searchpaths, bool for_lua)
{
    if (L->type(-1) != LUA_TSTRING)
    {
        LOGGER()->warn("package.path is not a string");
        return;
    }

    for (auto& searchpath : searchpaths)
    {
        L->pushstring(fmt::format(";{}{}", searchpath,
                for_lua? "/?.lua" : "/?.so"));

        if (for_lua)
        {
            L->pushstring(fmt::format(";{}/?/init.lua", searchpath,
                    for_lua? "/?.lua" : "/?.so"));

            // pushed two, concat them with string already on stack
            L->concat(3);
        }
        else
        {
            // pushed one, concat it with string already on stack
            L->concat(2);
        }
    }

    // add rwte lib path
    if (for_lua)
    {
        L->pushstring(
            ";" RWTE_LIB_PATH "/?.lua"
            ";" RWTE_LIB_PATH "/?/init.lua");
        L->concat(2);
    }
    else
    {
        L->pushstring(";" RWTE_LIB_PATH "/?.so");
        L->concat(2);
    }
}

static bool run_file(lua::State *L, const char *path)
{
    if (L->loadfile(path) || L->pcall(0, 0, 0))
    {
        LOGGER()->error("lua config error: {}", L->tostring(-1));
        L->pop(1);
        return false;
    }
    else
        return true;
}

static bool run_config(lua::State *L, xdgHandle *xdg, const char *confpatharg)
{
    // try specified path first
    if (confpatharg)
    {
        if (run_file(L, confpatharg))
            return true;
        else
            LOGGER()->warn("unable to run specified config ({}); "
                    "running config.lua", confpatharg);
    }

    // try paths from xdgConfigFind
    char *paths = xdgConfigFind("rwte/config.lua", xdg);
    // paths from xdgConfigFind are null-terminated, with
    // empty string at the end (double null)
    char *tmp = paths;
    while (*tmp)
    {
        if (run_file(L, tmp))
            return true;
        tmp += std::strlen(tmp) + 1;
    }
    std::free(paths);

    // finally try CONFIG_FILE
    // use wordexp to expand possible ~ in the path
    wordexp_t exp_result;
    wordexp(CONFIG_FILE, &exp_result, 0);
    bool result = run_file(L, exp_result.we_wordv[0]);
    wordfree(&exp_result);

    return result;
}

static void exit_help(int code)
{
    fprintf((code == EXIT_SUCCESS) ? stdout : stderr,
        "Usage: rwte [options] [-- args]\n"
        "  -c, --config FILE     overrides config file\n"
        "  -a, --noalt           disables alt screens\n"
        "  -f, --font FONT       pango font string\n"
        "  -g, --geometry GEOM   window geometry; colsxrows, e.g.,\n"
        "                        \"80x24\" (the default)\n"
        "  -t, --title TITLE     window title; defaults to rwte\n"
        "  -n, --name NAME       window name; defaults to $TERM\n"
        "  -w, --winclass CLASS  overrides window class\n"
        "  -e, --exe COMMAND     command to execute instead of shell;\n"
        "                        if specified, any arguments to the\n"
        "                        command may be specified after a \"--\"\n"
        "  -o, --out OUT         writes all io to this file;\n"
        "                        \"-\" means stdout\n"
        "  -l, --line LINE       use a tty line instead of creating a\n"
        "                        new pty; LINE is expected to be the\n"
        "                        device\n"
        "  -h, --help            show help\n"
        "  -b, --bench           run config and exit\n"
        "  -v, --version         show version and exit\n");
    exit(code);
}

static void exit_version()
{
    fprintf(stdout, "rwte %s\n", version_string());
    exit(EXIT_SUCCESS);
}

static bool parse_geometry(const char *g, int *cols, int *rows)
{
    // parse cols
    char *end = nullptr;
    int c = strtol(g, &end, 10);
    if (c > 0 && *end == 'x')
    {
        // move past x
        end++;

        // parse rows
        int r = strtol(end, &end, 10);
        if (r > 0 && *end == 0)
        {
            *cols = c;
            *rows = r;
            return true;
        }
    }

    return false;
}

int main(int argc, char *argv[])
{
    auto L = rwte.lua();

    // register internal modules, logging first
    register_lualogging(L.get());
    register_luaterm(L.get());
    register_luawindow(L.get());

    // feed lua our args
    L->newtable();
    for (int i = 0; i < argc; i++)
    {
        L->pushstring(argv[i]);
        L->seti(-2, i+1);
    }
    L->setglobal("args");

    static struct option long_options[] =
    {
        {"config", required_argument, nullptr, 'c'},
        {"winclass", required_argument, nullptr, 'w'},
        {"noalt", no_argument, nullptr, 'a'},
        {"font", required_argument, nullptr, 'f'},
        {"geometry", required_argument, nullptr, 'g'}, // colsxrows, e.g., 80x24
        {"title", required_argument, nullptr, 't'},
        {"name", required_argument, nullptr, 'n'},
        {"exe", required_argument, nullptr, 'e'},
        {"out", required_argument, nullptr, 'o'},
        {"line", required_argument, nullptr, 'l'},
        {"help", no_argument, nullptr, 'h'},
        {"bench", no_argument, nullptr, 'b'},
        {"version", no_argument, nullptr, 'v'},
        {nullptr, 0, nullptr, 0}
    };

    const char *confpath = nullptr;

    int opt;
    int cols = 0, rows = 0;
    bool got_exe = false;
    bool got_bench = false;
    bool got_title = false;
    while ((opt = getopt_long(argc, argv, "-c:w:af:g:t:n:o:l:hbve:",
                long_options, NULL)) != -1)
    {
        switch (opt)
        {
        case 'c':
            confpath = optarg;
            break;
        case 'w':
            options.winclass = optarg;
            break;
        case 'a':
            options.noalt = true;
            break;
        case 'f':
            options.font = optarg;
            break;
        case 'g':
            if (!parse_geometry(optarg, &cols, &rows))
                LOGGER()->warn("ignoring invalid geometry '{}'", optarg);
            break;
        case 't':
            options.title = optarg;
            got_title = true;
            break;
        case 'n':
            options.winname = optarg;
            break;
        case 'o':
            options.io = optarg;
            break;
        case 'l':
            options.line = optarg;
            break;
        case 'h':
            exit_help(EXIT_SUCCESS);
            break;
        case 'b':
            got_bench = true;
            break;
        case 'v':
            exit_version();
            break;
        case 'e':
            // todo: handle -e
            LOGGER()->info("exe: {}", optarg);
            got_exe = true;
            break;
        case 1:
            fprintf(stderr, "%s: invalid arg -- '%s'\n",
                    argv[0], argv[optind-1]);
        default:
            exit_help(EXIT_FAILURE);
        }
    }

    // todo: handle these with -e
    if (optind < argc)
    {
        LOGGER()->info("non-option args:");
        while (optind < argc)
            LOGGER()->info("{}", argv[optind++]);
    }

    {
        // Get XDG basedir data
        xdgHandle xdg;
        xdgInitHandle(&xdg);

        // make a list of pachage search paths
        std::vector<std::string> searchpaths;
        const char *const *xdgconfigdirs = xdgSearchableConfigDirectories(&xdg);
        for(; *xdgconfigdirs; xdgconfigdirs++)
        {
            // append /rwte to each dir
            std::string path = *xdgconfigdirs;
            path += "/rwte";
            searchpaths.push_back(path);
        }

        // add search paths to lua world
        L->getglobal("package");
        if (L->istable(-1))
        {
            L->getfield(-1, "path");
            add_to_search_path(L.get(), searchpaths, true);
            L->setfield(-2, "path");

            L->getfield(-1, "cpath");
            add_to_search_path(L.get(), searchpaths, false);
            L->setfield(-2, "cpath");
        }
        else
            LOGGER()->error("package is not a table");
        L->pop();

        // find and run configuration file
        if (!run_config(L.get(), &xdg, confpath))
            LOGGER()->fatal("could not find/run config.lua");

        xdgWipeHandle(&xdg);
    }

    // nothing else to do if bench arg was specified
    if (got_bench)
        return 0;

    // if a title was passed on command line, then
    // use that rather than checking lua config
    if (!got_title)
    {
        L->getglobal("config");
        if (L->istable(-1))
        {
            L->getfield(-1, "title");
            std::string title = L->tostring(-1);
            if (!title.empty())
                options.title = title;
            L->pop();
        }
        else
            LOGGER()->fatal("expected 'config' to be table");
        L->pop();
    }

    // get ready, loop!
    ev::default_loop main_loop;

    // get cols and rows, default to 80x24
    if (cols == 0 || rows == 0)
    {
        L->getglobal("config");
        L->getfield(-1, "default_cols");
        cols = L->tointegerdef(-1, 80);
        L->getfield(-2, "default_rows");
        rows = L->tointegerdef(-1, 24);
        L->pop(3);
    }

    cols = MAX(cols, 1);
    rows = MAX(rows, 1);

    g_term = std::make_unique<Term>(cols, rows);

    // todo: width and height are arbitrary
    if (!window.create(cols, rows))
        return 1;

    {
        SigWatcher sigwatcher;
        main_loop.run();
    }

    window.destroy();

    LOGGER()->debug("exiting");
    return 0;
}
<commit_msg>Moved around the main loop.<commit_after>#include <xcb/xcb.h>
#include <cstdio>
#include <ev++.h>
#include <cstring>
#include <memory>
#include <wordexp.h>
#include <basedir.h>
#include <basedir_fs.h>
#include <getopt.h>

#include "rwte/config.h"
#include "rwte/renderer.h"
#include "rwte/rwte.h"
#include "rwte/logging.h"
#include "rwte/sigwatcher.h"
#include "rwte/term.h"
#include "rwte/tty.h"
#include "rwte/window.h"
#include "lua/config.h"
#include "lua/state.h"
#include "lua/logging.h"
#include "lua/term.h"
#include "lua/window.h"
#include "rwte/version.h"

// globals
Window window;
Options options;
Rwte rwte;
std::unique_ptr<Term> g_term;
std::unique_ptr<Tty> g_tty;
lua_State * g_L = NULL;

#define LOGGER() (logging::get("rwte"))

#define MIN(a, b) ((a) < (b)? (a) : (b))
#define MAX(a, b) ((a) < (b)? (b) : (a))

// default values to use if we don't have
// a default value in config
static const float DEFAULT_BLINK_RATE = 0.6;

Options::Options() :
    cmd(0),
    title("rwte"),
    noalt(false)
{ }

Rwte::Rwte() :
    m_lua(std::make_shared<lua::State>())
{
    m_lua->openlibs();

    m_child.set<Rwte,&Rwte::childcb>(this);
    m_flush.set<Rwte,&Rwte::flushcb>(this);
    m_blink.set<Rwte,&Rwte::blinkcb>(this);
}

void Rwte::resize(uint16_t width, uint16_t height)
{
    if (width == 0)
        width = window.width();
    if (height == 0)
        height = window.height();

    if (window.width() != width || window.height() != height)
    {
        window.resize(width, height);
        g_term->resize(window.cols(), window.rows());

        if (g_tty)
            g_tty->resize();
    }
}

void Rwte::watch_child(pid_t pid)
{
    LOGGER()->debug("watching child {}", pid);
    m_child.start(pid);
}

void Rwte::refresh()
{
    if (!m_flush.is_active())
        m_flush.start(1.0/60.0);
}

void Rwte::start_blink()
{
    if (!m_blink.is_active())
    {
        float rate = lua::config::get_float(
                "blink_rate", DEFAULT_BLINK_RATE);

        m_blink.start(rate, rate);
    }
    else
    {
        // reset the timer if it's already active
        // (so we don't blink until idle)
        m_blink.stop();
        m_blink.start();
    }
}

void Rwte::stop_blink()
{
    if (m_blink.is_active())
        m_blink.stop();
}

void Rwte::childcb(ev::child &w, int)
{
    if (!WIFEXITED(w.rstatus) || WEXITSTATUS(w.rstatus))
        LOGGER()->warn("child finished with error {}", w.rstatus);
    else
        LOGGER()->info("child exited");
    w.loop.break_loop(ev::ALL);
}

void Rwte::flushcb(ev::timer &, int)
{
    window.draw();
}

void Rwte::blinkcb(ev::timer &, int)
{
    g_term->blink();
}

static void add_to_search_path(lua::State *L, const std::vector<std::string>& searchpaths, bool for_lua)
{
    if (L->type(-1) != LUA_TSTRING)
    {
        LOGGER()->warn("package.path is not a string");
        return;
    }

    for (auto& searchpath : searchpaths)
    {
        L->pushstring(fmt::format(";{}{}", searchpath,
                for_lua? "/?.lua" : "/?.so"));

        if (for_lua)
        {
            L->pushstring(fmt::format(";{}/?/init.lua", searchpath,
                    for_lua? "/?.lua" : "/?.so"));

            // pushed two, concat them with string already on stack
            L->concat(3);
        }
        else
        {
            // pushed one, concat it with string already on stack
            L->concat(2);
        }
    }

    // add rwte lib path
    if (for_lua)
    {
        L->pushstring(
            ";" RWTE_LIB_PATH "/?.lua"
            ";" RWTE_LIB_PATH "/?/init.lua");
        L->concat(2);
    }
    else
    {
        L->pushstring(";" RWTE_LIB_PATH "/?.so");
        L->concat(2);
    }
}

static bool run_file(lua::State *L, const char *path)
{
    if (L->loadfile(path) || L->pcall(0, 0, 0))
    {
        LOGGER()->error("lua config error: {}", L->tostring(-1));
        L->pop(1);
        return false;
    }
    else
        return true;
}

static bool run_config(lua::State *L, xdgHandle *xdg, const char *confpatharg)
{
    // try specified path first
    if (confpatharg)
    {
        if (run_file(L, confpatharg))
            return true;
        else
            LOGGER()->warn("unable to run specified config ({}); "
                    "running config.lua", confpatharg);
    }

    // try paths from xdgConfigFind
    char *paths = xdgConfigFind("rwte/config.lua", xdg);
    // paths from xdgConfigFind are null-terminated, with
    // empty string at the end (double null)
    char *tmp = paths;
    while (*tmp)
    {
        if (run_file(L, tmp))
            return true;
        tmp += std::strlen(tmp) + 1;
    }
    std::free(paths);

    // finally try CONFIG_FILE
    // use wordexp to expand possible ~ in the path
    wordexp_t exp_result;
    wordexp(CONFIG_FILE, &exp_result, 0);
    bool result = run_file(L, exp_result.we_wordv[0]);
    wordfree(&exp_result);

    return result;
}

static void exit_help(int code)
{
    fprintf((code == EXIT_SUCCESS) ? stdout : stderr,
        "Usage: rwte [options] [-- args]\n"
        "  -c, --config FILE     overrides config file\n"
        "  -a, --noalt           disables alt screens\n"
        "  -f, --font FONT       pango font string\n"
        "  -g, --geometry GEOM   window geometry; colsxrows, e.g.,\n"
        "                        \"80x24\" (the default)\n"
        "  -t, --title TITLE     window title; defaults to rwte\n"
        "  -n, --name NAME       window name; defaults to $TERM\n"
        "  -w, --winclass CLASS  overrides window class\n"
        "  -e, --exe COMMAND     command to execute instead of shell;\n"
        "                        if specified, any arguments to the\n"
        "                        command may be specified after a \"--\"\n"
        "  -o, --out OUT         writes all io to this file;\n"
        "                        \"-\" means stdout\n"
        "  -l, --line LINE       use a tty line instead of creating a\n"
        "                        new pty; LINE is expected to be the\n"
        "                        device\n"
        "  -h, --help            show help\n"
        "  -b, --bench           run config and exit\n"
        "  -v, --version         show version and exit\n");
    exit(code);
}

static void exit_version()
{
    fprintf(stdout, "rwte %s\n", version_string());
    exit(EXIT_SUCCESS);
}

static bool parse_geometry(const char *g, int *cols, int *rows)
{
    // parse cols
    char *end = nullptr;
    int c = strtol(g, &end, 10);
    if (c > 0 && *end == 'x')
    {
        // move past x
        end++;

        // parse rows
        int r = strtol(end, &end, 10);
        if (r > 0 && *end == 0)
        {
            *cols = c;
            *rows = r;
            return true;
        }
    }

    return false;
}

int main(int argc, char *argv[])
{
    auto L = rwte.lua();

    // register internal modules, logging first
    register_lualogging(L.get());
    register_luaterm(L.get());
    register_luawindow(L.get());

    // feed lua our args
    L->newtable();
    for (int i = 0; i < argc; i++)
    {
        L->pushstring(argv[i]);
        L->seti(-2, i+1);
    }
    L->setglobal("args");

    static struct option long_options[] =
    {
        {"config", required_argument, nullptr, 'c'},
        {"winclass", required_argument, nullptr, 'w'},
        {"noalt", no_argument, nullptr, 'a'},
        {"font", required_argument, nullptr, 'f'},
        {"geometry", required_argument, nullptr, 'g'}, // colsxrows, e.g., 80x24
        {"title", required_argument, nullptr, 't'},
        {"name", required_argument, nullptr, 'n'},
        {"exe", required_argument, nullptr, 'e'},
        {"out", required_argument, nullptr, 'o'},
        {"line", required_argument, nullptr, 'l'},
        {"help", no_argument, nullptr, 'h'},
        {"bench", no_argument, nullptr, 'b'},
        {"version", no_argument, nullptr, 'v'},
        {nullptr, 0, nullptr, 0}
    };

    const char *confpath = nullptr;

    int opt;
    int cols = 0, rows = 0;
    bool got_exe = false;
    bool got_bench = false;
    bool got_title = false;
    while ((opt = getopt_long(argc, argv, "-c:w:af:g:t:n:o:l:hbve:",
                long_options, NULL)) != -1)
    {
        switch (opt)
        {
        case 'c':
            confpath = optarg;
            break;
        case 'w':
            options.winclass = optarg;
            break;
        case 'a':
            options.noalt = true;
            break;
        case 'f':
            options.font = optarg;
            break;
        case 'g':
            if (!parse_geometry(optarg, &cols, &rows))
                LOGGER()->warn("ignoring invalid geometry '{}'", optarg);
            break;
        case 't':
            options.title = optarg;
            got_title = true;
            break;
        case 'n':
            options.winname = optarg;
            break;
        case 'o':
            options.io = optarg;
            break;
        case 'l':
            options.line = optarg;
            break;
        case 'h':
            exit_help(EXIT_SUCCESS);
            break;
        case 'b':
            got_bench = true;
            break;
        case 'v':
            exit_version();
            break;
        case 'e':
            // todo: handle -e
            LOGGER()->info("exe: {}", optarg);
            got_exe = true;
            break;
        case 1:
            fprintf(stderr, "%s: invalid arg -- '%s'\n",
                    argv[0], argv[optind-1]);
        default:
            exit_help(EXIT_FAILURE);
        }
    }

    // todo: handle these with -e
    if (optind < argc)
    {
        LOGGER()->info("non-option args:");
        while (optind < argc)
            LOGGER()->info("{}", argv[optind++]);
    }

    {
        // Get XDG basedir data
        xdgHandle xdg;
        xdgInitHandle(&xdg);

        // make a list of pachage search paths
        std::vector<std::string> searchpaths;
        const char *const *xdgconfigdirs = xdgSearchableConfigDirectories(&xdg);
        for(; *xdgconfigdirs; xdgconfigdirs++)
        {
            // append /rwte to each dir
            std::string path = *xdgconfigdirs;
            path += "/rwte";
            searchpaths.push_back(path);
        }

        // add search paths to lua world
        L->getglobal("package");
        if (L->istable(-1))
        {
            L->getfield(-1, "path");
            add_to_search_path(L.get(), searchpaths, true);
            L->setfield(-2, "path");

            L->getfield(-1, "cpath");
            add_to_search_path(L.get(), searchpaths, false);
            L->setfield(-2, "cpath");
        }
        else
            LOGGER()->error("package is not a table");
        L->pop();

        // find and run configuration file
        if (!run_config(L.get(), &xdg, confpath))
            LOGGER()->fatal("could not find/run config.lua");

        xdgWipeHandle(&xdg);
    }

    // nothing else to do if bench arg was specified
    if (got_bench)
        return 0;

    // if a title was passed on command line, then
    // use that rather than checking lua config
    if (!got_title)
    {
        L->getglobal("config");
        if (L->istable(-1))
        {
            L->getfield(-1, "title");
            std::string title = L->tostring(-1);
            if (!title.empty())
                options.title = title;
            L->pop();
        }
        else
            LOGGER()->fatal("expected 'config' to be table");
        L->pop();
    }

    // get cols and rows, default to 80x24
    if (cols == 0 || rows == 0)
    {
        L->getglobal("config");
        L->getfield(-1, "default_cols");
        cols = L->tointegerdef(-1, 80);
        L->getfield(-2, "default_rows");
        rows = L->tointegerdef(-1, 24);
        L->pop(3);
    }

    cols = MAX(cols, 1);
    rows = MAX(rows, 1);

    // get ready, loop!
    ev::default_loop main_loop;

    g_term = std::make_unique<Term>(cols, rows);

    // todo: width and height are arbitrary
    if (!window.create(cols, rows))
        return 1;

    {
        SigWatcher sigwatcher;
        main_loop.run();
    }

    window.destroy();

    LOGGER()->debug("exiting");
    return 0;
}
<|endoftext|>


<commit_before>#include "scope.h"

#include "ast.h"

#include <cassert>
#include <iostream>

Identifier* Scope::Find(const Token* tok) {
  auto ret = Find(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

Identifier* Scope::FindInCurScope(const Token* tok) {
  auto ret = FindInCurScope(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

Identifier* Scope::FindTag(const Token* tok) {
  auto ret = FindTag(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

Identifier* Scope::FindTagInCurScope(const Token* tok) {
  auto ret = FindTagInCurScope(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

void Scope::Insert(Identifier* ident) {
  Insert(ident->Name(), ident);
}

void Scope::InsertTag(Identifier* ident) {
  Insert(TagName(ident->Name()), ident);
}

Identifier* Scope::Find(const std::string& name) {
  auto ident = identMap_.find(name);
  if (ident != identMap_.end())
    return ident->second;
  if (type_ == S_FILE || parent_ == nullptr)
    return nullptr;
  return parent_->Find(name);
}

Identifier* Scope::FindInCurScope(const std::string& name) {
  auto ident = identMap_.find(name);
  if (ident == identMap_.end())
    return nullptr;
  return ident->second;
}

void Scope::Insert(const std::string& name, Identifier* ident) {
  assert(FindInCurScope(name) == nullptr);
  identMap_[name] = ident;
}

Identifier* Scope::FindTag(const std::string& name) {
  auto tag = Find(TagName(name));
  if (tag) assert(tag->ToTypeName());
  return tag;
}

Identifier* Scope::FindTagInCurScope(const std::string& name) {
  auto tag = FindInCurScope(TagName(name));
  assert(tag == nullptr || tag->ToTypeName());
  return tag;
}

Scope::TagList Scope::AllTagsInCurScope() const {
  TagList tags;
  for (auto& kv: identMap_) {
    if (IsTagName(kv.first))
      tags.push_back(kv.second);
  }
  return tags;
}

void Scope::Print() {
  std::cout << "scope: " << this << std::endl;

  auto iter = identMap_.begin();
  for (; iter != identMap_.end(); ++iter) {
    auto name = iter->first;
    auto ident = iter->second;
    if (ident->ToTypeName()) {
      std::cout << name << "\t[type:\t"
                << ident->Type()->Str() << "]" << std::endl;
    } else {
      std::cout << name << "\t[object:\t";
      std::cout << ident->Type()->Str() << "]" << std::endl;
    }
  }
  std::cout << std::endl;
}
<commit_msg>beautify code;<commit_after>#include "scope.h"

#include "ast.h"

#include <cassert>
#include <iostream>

Identifier* Scope::Find(const Token* tok) {
  auto ret = Find(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

Identifier* Scope::FindInCurScope(const Token* tok) {
  auto ret = FindInCurScope(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

Identifier* Scope::FindTag(const Token* tok) {
  auto ret = FindTag(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

Identifier* Scope::FindTagInCurScope(const Token* tok) {
  auto ret = FindTagInCurScope(tok->str_);
  if (ret) ret->SetTok(tok);
  return ret;
}

void Scope::Insert(Identifier* ident) {
  Insert(ident->Name(), ident);
}

void Scope::InsertTag(Identifier* ident) {
  Insert(TagName(ident->Name()), ident);
}

Identifier* Scope::Find(const std::string& name) {
  auto ident = identMap_.find(name);
  if (ident != identMap_.end())
    return ident->second;
  if (type_ == S_FILE || parent_ == nullptr)
    return nullptr;
  return parent_->Find(name);
}

Identifier* Scope::FindInCurScope(const std::string& name) {
  auto ident = identMap_.find(name);
  if (ident == identMap_.end())
    return nullptr;
  return ident->second;
}

void Scope::Insert(const std::string& name, Identifier* ident) {
  assert(FindInCurScope(name) == nullptr);
  identMap_[name] = ident;
}

Identifier* Scope::FindTag(const std::string& name) {
  auto tag = Find(TagName(name));
  if (tag) assert(tag->ToTypeName());
  return tag;
}

Identifier* Scope::FindTagInCurScope(const std::string& name) {
  auto tag = FindInCurScope(TagName(name));
  assert(tag == nullptr || tag->ToTypeName());
  return tag;
}

Scope::TagList Scope::AllTagsInCurScope() const {
  TagList tags;
  for (auto& kv: identMap_) {
    if (IsTagName(kv.first))
      tags.push_back(kv.second);
  }
  return tags;
}

void Scope::Print() {
  std::cout << "scope: " << this << std::endl;

  auto iter = identMap_.begin();
  for (; iter != identMap_.end(); ++iter) {
    auto name = iter->first;
    auto ident = iter->second;
    if (ident->ToTypeName()) {
      std::cout << name << "\t[type:\t"
                << ident->Type()->Str() << "]" << std::endl;
    } else {
      std::cout << name << "\t[object:\t"
                << ident->Type()->Str() << "]" << std::endl;
    }
  }
  std::cout << std::endl;
}
<|endoftext|>


<commit_before><commit_msg>non-bonded bead lists can now be made by from atom name by using <type1>name:XXX</type1> in xml file<commit_after><|endoftext|>


<commit_before>/* Copyright (c) 2010-2015, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QtCore>
#include <QHostAddress>
#include <QSet>
#include <TActionContext>
#include <TWebApplication>
#include <TAppSettings>
#include <THttpRequest>
#include <THttpResponse>
#include <THttpUtility>
#include <TDispatcher>
#include <TActionController>
#include <TSessionStore>
#include "tsystemglobal.h"
#include "thttpsocket.h"
#include "tsessionmanager.h"
#include "turlroute.h"
#include "tabstractwebsocket.h"

/*!
  \class TActionContext
  \brief The TActionContext class is the base class of contexts for
  action controllers.
*/

TActionContext::TActionContext()
    : TDatabaseContext(),
      stopped(false),
      socketDesc(0),
      currController(0),
      httpReq(0)
{ }

TActionContext::~TActionContext()
{
    release();
}

static bool directViewRenderMode()
{
    static int mode = -1;
    if (mode < 0) {
        mode = (int)Tf::appSettings()->value(Tf::DirectViewRenderMode).toBool();
    }
    return (bool)mode;
}

void TActionContext::execute(THttpRequest &request, const QByteArray &socketUuid)
{
    T_TRACEFUNC("");

    THttpResponseHeader responseHeader;
    accessLogger.open();

    try {
        httpReq = &request;
        const THttpRequestHeader &hdr = httpReq->header();

        // Access log
        accessLogger.setTimestamp(QDateTime::currentDateTime());
        QByteArray firstLine = hdr.method() + ' ' + hdr.path();
        firstLine += QString(" HTTP/%1.%2").arg(hdr.majorVersion()).arg(hdr.minorVersion()).toLatin1();
        accessLogger.setRequest(firstLine);
        accessLogger.setRemoteHost( (Tf::appSettings()->value(Tf::ListenPort).toUInt() > 0) ? clientAddress().toString().toLatin1() : QByteArray("(unix)") );

        tSystemDebug("method : %s", hdr.method().data());
        tSystemDebug("path : %s", hdr.path().data());

        // HTTP method
        Tf::HttpMethod method = httpReq->method();
        QString path = THttpUtility::fromUrlEncoding(hdr.path().mid(0, hdr.path().indexOf('?')));

        // Routing info exists?
        QStringList components = TUrlRoute::splitPath(path);
        TRouting rt = TUrlRoute::instance().findRouting(method, components);

        tSystemDebug("Routing: controller:%s  action:%s", rt.controller.data(),
                     rt.action.data());

        if (rt.isEmpty()) {
            // Default URL routing

            if (directViewRenderMode()) { // Direct view render mode?
                // Direct view setting
                rt.setRouting("directcontroller", "show", components);
            } else {
                QByteArray c = components.value(0).toLatin1().toLower();
                if (!c.isEmpty()) {
                    if (!TActionController::disabledControllers().contains(c)) { // Can not call 'ApplicationController'
                        // Default action: "index"
                        QByteArray action = components.value(1, QLatin1String("index")).toLatin1();
                        rt.setRouting(c + "controller", action, components.mid(2));
                    }
                }
                tSystemDebug("Active Controller : %s", rt.controller.data());
            }
        }

        // Call controller method
        TDispatcher<TActionController> ctlrDispatcher(rt.controller);
        currController = ctlrDispatcher.object();
        if (currController) {
            currController->setActionName(rt.action);
            currController->setSocketUuid(socketUuid);

            // Session
            if (currController->sessionEnabled()) {
                TSession session;
                QByteArray sessionId = httpReq->cookie(TSession::sessionName());
                if (!sessionId.isEmpty()) {
                    // Finds a session
                    session = TSessionManager::instance().findSession(sessionId);
                }
                currController->setSession(session);

                // Exports flash-variant
                currController->exportAllFlashVariants();
            }

            // Verify authenticity token
            if (Tf::appSettings()->value(Tf::EnableCsrfProtectionModule, true).toBool()
                && currController->csrfProtectionEnabled() && !currController->exceptionActionsOfCsrfProtection().contains(rt.action)) {

                if (method == Tf::Post || method == Tf::Put || method == Tf::Delete) {
                    if (!currController->verifyRequest(*httpReq)) {
                        throw SecurityException("Invalid authenticity token", __FILE__, __LINE__);
                    }
                }
            }

            if (currController->sessionEnabled()) {
                if (currController->session().id().isEmpty() || Tf::appSettings()->value(Tf::SessionAutoIdRegeneration).toBool()) {
                    TSessionManager::instance().remove(currController->session().sessionId); // Removes the old session
                    // Re-generate session ID
                    currController->session().sessionId = TSessionManager::instance().generateId();
                    tSystemDebug("Re-generate session ID: %s", currController->session().sessionId.data());
                }
                // Sets CSRF protection informaion
                TActionController::setCsrfProtectionInto(currController->session());
            }

            // Database Transaction
            setTransactionEnabled(currController->transactionEnabled());

            // Do filters
            if (Q_LIKELY(currController->preFilter())) {

                // Dispathes
                bool dispatched = ctlrDispatcher.invoke(rt.action, rt.params);
                if (Q_LIKELY(dispatched)) {
                    autoRemoveFiles << currController->autoRemoveFiles;  // Adds auto-remove files

                    // Post fileter
                    currController->postFilter();

                    if (Q_UNLIKELY(currController->rollbackRequested())) {
                        rollbackTransactions();
                    } else {
                        // Commits a transaction to the database
                        commitTransactions();
                    }

                    // Session store
                    if (currController->sessionEnabled()) {
                        bool stored = TSessionManager::instance().store(currController->session());
                        if (Q_LIKELY(stored)) {
                            QDateTime expire;
                            if (TSessionManager::sessionLifeTime() > 0) {
                                expire = QDateTime::currentDateTime().addSecs(TSessionManager::sessionLifeTime());
                            }

                            // Sets the path in the session cookie
                            QString cookiePath = Tf::appSettings()->value(Tf::SessionCookiePath).toString();
                            currController->addCookie(TSession::sessionName(), currController->session().id(), expire, cookiePath, QString(), false, true);
                        }
                    }

                    // WebSocket tasks
                    if (!currController->taskList.isEmpty()) {
                        for (auto &task : currController->taskList) {
                            const QVariant &taskData = task.second;

                            switch (task.first) {
                            case TActionController::SendTextTo: {
                                QVariantList lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toByteArray());
                                if (websocket) {
                                    websocket->sendText(lst[1].toString());
                                }
                                break; }

                            case TActionController::SendBinaryTo: {
                                QVariantList lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toByteArray());
                                if (websocket) {
                                    websocket->sendBinary(lst[1].toByteArray());
                                }
                                break; }

                            case TActionController::SendCloseTo: {
                                QVariantList lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toByteArray());
                                if (websocket) {
                                    websocket->sendClose(lst[1].toInt());
                                }
                                break; }

                            default:
                                tSystemError("Invalid logic  [%s:%d]",  __FILE__, __LINE__);
                                break;
                            }
                        }
                    }
                }
            }

            // Sets charset to the content-type
            QByteArray ctype = currController->response.header().contentType().toLower();
            if (ctype.startsWith("text") && !ctype.contains("charset")) {
                ctype += "; charset=";
                ctype += Tf::app()->codecForHttpOutput()->name();
                currController->response.header().setContentType(ctype);
            }

            // Sets the default status code of HTTP response
            accessLogger.setStatusCode( (!currController->response.isBodyNull()) ? currController->statusCode() : Tf::InternalServerError );
            currController->response.header().setStatusLine(accessLogger.statusCode(), THttpUtility::getResponseReasonPhrase(accessLogger.statusCode()));

            // Writes a response and access log
            qint64 bodyLength = (currController->response.header().contentLength() > 0) ? currController->response.header().contentLength() : currController->response.bodyLength();
            int bytes = writeResponse(currController->response.header(), currController->response.bodyIODevice(),
                                      bodyLength);
            accessLogger.setResponseBytes(bytes);

            // Session GC
            TSessionManager::instance().collectGarbage();

        } else {
            accessLogger.setStatusCode( Tf::BadRequest );  // Set a default status code

            if (method == Tf::Get) {  // GET Method
                path.remove(0, 1);
                QFile reqPath(Tf::app()->publicPath() + path);
                QFileInfo fi(reqPath);

                if (fi.isFile() && fi.isReadable()) {
                    // Check "If-Modified-Since" header for caching
                    bool sendfile = true;
                    QByteArray ifModifiedSince = hdr.rawHeader("If-Modified-Since");

                    if (!ifModifiedSince.isEmpty()) {
                        QDateTime dt = THttpUtility::fromHttpDateTimeString(ifModifiedSince);
                        sendfile = (!dt.isValid() || dt != fi.lastModified());
                    }

                    if (sendfile) {
                        // Sends a request file
                        responseHeader.setRawHeader("Last-Modified", THttpUtility::toHttpDateTimeString(fi.lastModified()));
                        QByteArray type = Tf::app()->internetMediaType(fi.suffix());
                        int bytes = writeResponse(Tf::OK, responseHeader, type, &reqPath, reqPath.size());
                        accessLogger.setResponseBytes( bytes );
                    } else {
                        // Not send the data
                        int bytes = writeResponse(Tf::NotModified, responseHeader);
                        accessLogger.setResponseBytes( bytes );
                    }
                } else {
                    int bytes = writeResponse(Tf::NotFound, responseHeader);
                    accessLogger.setResponseBytes( bytes );
                }
                accessLogger.setStatusCode( responseHeader.statusCode() );

            } else if (method == Tf::Post) {
                // file upload?
            } else {
                // HEAD, DELETE, ...
            }
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        tSystemWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        int bytes = writeResponse(e.statusCode(), responseHeader);
        accessLogger.setResponseBytes( bytes );
        accessLogger.setStatusCode( e.statusCode() );
    } catch (SqlException &e) {
        tError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (KvsException &e) {
        tError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (SecurityException &e) {
        tError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (RuntimeException &e) {
        tError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (StandardException &e) {
        tError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (...) {
        tError("Caught Exception");
        tSystemError("Caught Exception");
        closeHttpSocket();
    }

    TActionContext::accessLogger.write();  // Writes access log
}

void TActionContext::release()
{
    TDatabaseContext::release();

    for (QListIterator<TTemporaryFile *> i(tempFiles); i.hasNext(); ) {
        delete i.next();
    }
    tempFiles.clear();

    for (QStringListIterator i(autoRemoveFiles); i.hasNext(); ) {
        QFile(i.next()).remove();
    }
    autoRemoveFiles.clear();
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header)
{
    T_TRACEFUNC("statusCode:%d", statusCode);
    QByteArray body;
    if (statusCode >= 400) {
        QFile html(Tf::app()->publicPath() + QString::number(statusCode) + ".html");
        if (html.exists() && html.open(QIODevice::ReadOnly)) {
            body = html.readAll();
            html.close();
        }
    }
    if (body.isEmpty()) {
        body  = "";
        body += THttpUtility::getResponseReasonPhrase(statusCode);
        body += " (";
        body += QByteArray::number(statusCode);
        body += ")";
    }

    QBuffer buf(&body);
    return writeResponse(statusCode, header, "text/html", &buf, body.length());
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("statusCode:%d  contentType:%s  length:%s", statusCode, contentType.data(), qPrintable(QString::number(length)));

    header.setStatusLine(statusCode, THttpUtility::getResponseReasonPhrase(statusCode));
    if (!contentType.isEmpty())
        header.setContentType(contentType);

    return writeResponse(header, body, length);
}

qint64 TActionContext::writeResponse(THttpResponseHeader &header, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("length:%s", qPrintable(QString::number(length)));

    header.setContentLength(length);
    header.setRawHeader("Server", "TreeFrog server");
    header.setCurrentDate();

    // Write data
    return writeResponse(header, body);
}

void TActionContext::emitError(int )
{ }

TTemporaryFile &TActionContext::createTemporaryFile()
{
    TTemporaryFile *file = new TTemporaryFile();
    tempFiles << file;
    return *file;
}

QHostAddress TActionContext::clientAddress() const
{
    return httpReq->clientAddress();
}
<commit_msg>fix a bug of setting cookie for session.<commit_after>/* Copyright (c) 2010-2015, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QtCore>
#include <QHostAddress>
#include <QSet>
#include <TActionContext>
#include <TWebApplication>
#include <TAppSettings>
#include <THttpRequest>
#include <THttpResponse>
#include <THttpUtility>
#include <TDispatcher>
#include <TActionController>
#include <TSessionStore>
#include "tsystemglobal.h"
#include "thttpsocket.h"
#include "tsessionmanager.h"
#include "turlroute.h"
#include "tabstractwebsocket.h"

/*!
  \class TActionContext
  \brief The TActionContext class is the base class of contexts for
  action controllers.
*/

TActionContext::TActionContext()
    : TDatabaseContext(),
      stopped(false),
      socketDesc(0),
      currController(0),
      httpReq(0)
{ }

TActionContext::~TActionContext()
{
    release();
}

static bool directViewRenderMode()
{
    static int mode = -1;
    if (mode < 0) {
        mode = (int)Tf::appSettings()->value(Tf::DirectViewRenderMode).toBool();
    }
    return (bool)mode;
}

void TActionContext::execute(THttpRequest &request, const QByteArray &socketUuid)
{
    T_TRACEFUNC("");

    THttpResponseHeader responseHeader;
    accessLogger.open();

    try {
        httpReq = &request;
        const THttpRequestHeader &hdr = httpReq->header();

        // Access log
        accessLogger.setTimestamp(QDateTime::currentDateTime());
        QByteArray firstLine = hdr.method() + ' ' + hdr.path();
        firstLine += QString(" HTTP/%1.%2").arg(hdr.majorVersion()).arg(hdr.minorVersion()).toLatin1();
        accessLogger.setRequest(firstLine);
        accessLogger.setRemoteHost( (Tf::appSettings()->value(Tf::ListenPort).toUInt() > 0) ? clientAddress().toString().toLatin1() : QByteArray("(unix)") );

        tSystemDebug("method : %s", hdr.method().data());
        tSystemDebug("path : %s", hdr.path().data());

        // HTTP method
        Tf::HttpMethod method = httpReq->method();
        QString path = THttpUtility::fromUrlEncoding(hdr.path().mid(0, hdr.path().indexOf('?')));

        // Routing info exists?
        QStringList components = TUrlRoute::splitPath(path);
        TRouting rt = TUrlRoute::instance().findRouting(method, components);

        tSystemDebug("Routing: controller:%s  action:%s", rt.controller.data(),
                     rt.action.data());

        if (rt.isEmpty()) {
            // Default URL routing

            if (directViewRenderMode()) { // Direct view render mode?
                // Direct view setting
                rt.setRouting("directcontroller", "show", components);
            } else {
                QByteArray c = components.value(0).toLatin1().toLower();
                if (!c.isEmpty()) {
                    if (!TActionController::disabledControllers().contains(c)) { // Can not call 'ApplicationController'
                        // Default action: "index"
                        QByteArray action = components.value(1, QLatin1String("index")).toLatin1();
                        rt.setRouting(c + "controller", action, components.mid(2));
                    }
                }
                tSystemDebug("Active Controller : %s", rt.controller.data());
            }
        }

        // Call controller method
        TDispatcher<TActionController> ctlrDispatcher(rt.controller);
        currController = ctlrDispatcher.object();
        if (currController) {
            currController->setActionName(rt.action);
            currController->setSocketUuid(socketUuid);

            // Session
            if (currController->sessionEnabled()) {
                TSession session;
                QByteArray sessionId = httpReq->cookie(TSession::sessionName());
                if (!sessionId.isEmpty()) {
                    // Finds a session
                    session = TSessionManager::instance().findSession(sessionId);
                }
                currController->setSession(session);

                // Exports flash-variant
                currController->exportAllFlashVariants();
            }

            // Verify authenticity token
            if (Tf::appSettings()->value(Tf::EnableCsrfProtectionModule, true).toBool()
                && currController->csrfProtectionEnabled() && !currController->exceptionActionsOfCsrfProtection().contains(rt.action)) {

                if (method == Tf::Post || method == Tf::Put || method == Tf::Delete) {
                    if (!currController->verifyRequest(*httpReq)) {
                        throw SecurityException("Invalid authenticity token", __FILE__, __LINE__);
                    }
                }
            }

            if (currController->sessionEnabled()) {
                if (currController->session().id().isEmpty() || Tf::appSettings()->value(Tf::SessionAutoIdRegeneration).toBool()) {
                    TSessionManager::instance().remove(currController->session().sessionId); // Removes the old session
                    // Re-generate session ID
                    currController->session().sessionId = TSessionManager::instance().generateId();
                    tSystemDebug("Re-generate session ID: %s", currController->session().sessionId.data());
                }
                // Sets CSRF protection informaion
                TActionController::setCsrfProtectionInto(currController->session());
            }

            // Database Transaction
            setTransactionEnabled(currController->transactionEnabled());

            // Do filters
            if (Q_LIKELY(currController->preFilter())) {

                // Dispathes
                bool dispatched = ctlrDispatcher.invoke(rt.action, rt.params);
                if (Q_LIKELY(dispatched)) {
                    autoRemoveFiles << currController->autoRemoveFiles;  // Adds auto-remove files

                    // Post fileter
                    currController->postFilter();

                    if (Q_UNLIKELY(currController->rollbackRequested())) {
                        rollbackTransactions();
                    } else {
                        // Commits a transaction to the database
                        commitTransactions();
                    }

                    // Session store
                    if (currController->sessionEnabled()) {
                        bool stored = TSessionManager::instance().store(currController->session());
                        if (Q_LIKELY(stored)) {
                            static int cookielifetime = -1;
                            QDateTime expire;

                            if (cookielifetime < 0) {
                                cookielifetime = Tf::appSettings()->value(Tf::SessionLifeTime).toInt();
                            }
                            if (cookielifetime > 0) {
                                expire = QDateTime::currentDateTime().addSecs(cookielifetime);
                            }

                            // Sets the path in the session cookie
                            QString cookiePath = Tf::appSettings()->value(Tf::SessionCookiePath).toString();
                            currController->addCookie(TSession::sessionName(), currController->session().id(), expire, cookiePath, QString(), false, true);
                        }
                    }

                    // WebSocket tasks
                    if (!currController->taskList.isEmpty()) {
                        for (auto &task : currController->taskList) {
                            const QVariant &taskData = task.second;

                            switch (task.first) {
                            case TActionController::SendTextTo: {
                                QVariantList lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toByteArray());
                                if (websocket) {
                                    websocket->sendText(lst[1].toString());
                                }
                                break; }

                            case TActionController::SendBinaryTo: {
                                QVariantList lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toByteArray());
                                if (websocket) {
                                    websocket->sendBinary(lst[1].toByteArray());
                                }
                                break; }

                            case TActionController::SendCloseTo: {
                                QVariantList lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toByteArray());
                                if (websocket) {
                                    websocket->sendClose(lst[1].toInt());
                                }
                                break; }

                            default:
                                tSystemError("Invalid logic  [%s:%d]",  __FILE__, __LINE__);
                                break;
                            }
                        }
                    }
                }
            }

            // Sets charset to the content-type
            QByteArray ctype = currController->response.header().contentType().toLower();
            if (ctype.startsWith("text") && !ctype.contains("charset")) {
                ctype += "; charset=";
                ctype += Tf::app()->codecForHttpOutput()->name();
                currController->response.header().setContentType(ctype);
            }

            // Sets the default status code of HTTP response
            accessLogger.setStatusCode( (!currController->response.isBodyNull()) ? currController->statusCode() : Tf::InternalServerError );
            currController->response.header().setStatusLine(accessLogger.statusCode(), THttpUtility::getResponseReasonPhrase(accessLogger.statusCode()));

            // Writes a response and access log
            qint64 bodyLength = (currController->response.header().contentLength() > 0) ? currController->response.header().contentLength() : currController->response.bodyLength();
            int bytes = writeResponse(currController->response.header(), currController->response.bodyIODevice(),
                                      bodyLength);
            accessLogger.setResponseBytes(bytes);

            // Session GC
            TSessionManager::instance().collectGarbage();

        } else {
            accessLogger.setStatusCode( Tf::BadRequest );  // Set a default status code

            if (method == Tf::Get) {  // GET Method
                path.remove(0, 1);
                QFile reqPath(Tf::app()->publicPath() + path);
                QFileInfo fi(reqPath);

                if (fi.isFile() && fi.isReadable()) {
                    // Check "If-Modified-Since" header for caching
                    bool sendfile = true;
                    QByteArray ifModifiedSince = hdr.rawHeader("If-Modified-Since");

                    if (!ifModifiedSince.isEmpty()) {
                        QDateTime dt = THttpUtility::fromHttpDateTimeString(ifModifiedSince);
                        sendfile = (!dt.isValid() || dt != fi.lastModified());
                    }

                    if (sendfile) {
                        // Sends a request file
                        responseHeader.setRawHeader("Last-Modified", THttpUtility::toHttpDateTimeString(fi.lastModified()));
                        QByteArray type = Tf::app()->internetMediaType(fi.suffix());
                        int bytes = writeResponse(Tf::OK, responseHeader, type, &reqPath, reqPath.size());
                        accessLogger.setResponseBytes( bytes );
                    } else {
                        // Not send the data
                        int bytes = writeResponse(Tf::NotModified, responseHeader);
                        accessLogger.setResponseBytes( bytes );
                    }
                } else {
                    int bytes = writeResponse(Tf::NotFound, responseHeader);
                    accessLogger.setResponseBytes( bytes );
                }
                accessLogger.setStatusCode( responseHeader.statusCode() );

            } else if (method == Tf::Post) {
                // file upload?
            } else {
                // HEAD, DELETE, ...
            }
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        tSystemWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        int bytes = writeResponse(e.statusCode(), responseHeader);
        accessLogger.setResponseBytes( bytes );
        accessLogger.setStatusCode( e.statusCode() );
    } catch (SqlException &e) {
        tError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (KvsException &e) {
        tError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (SecurityException &e) {
        tError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (RuntimeException &e) {
        tError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (StandardException &e) {
        tError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (...) {
        tError("Caught Exception");
        tSystemError("Caught Exception");
        closeHttpSocket();
    }

    TActionContext::accessLogger.write();  // Writes access log
}

void TActionContext::release()
{
    TDatabaseContext::release();

    for (QListIterator<TTemporaryFile *> i(tempFiles); i.hasNext(); ) {
        delete i.next();
    }
    tempFiles.clear();

    for (QStringListIterator i(autoRemoveFiles); i.hasNext(); ) {
        QFile(i.next()).remove();
    }
    autoRemoveFiles.clear();
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header)
{
    T_TRACEFUNC("statusCode:%d", statusCode);
    QByteArray body;
    if (statusCode >= 400) {
        QFile html(Tf::app()->publicPath() + QString::number(statusCode) + ".html");
        if (html.exists() && html.open(QIODevice::ReadOnly)) {
            body = html.readAll();
            html.close();
        }
    }
    if (body.isEmpty()) {
        body  = "";
        body += THttpUtility::getResponseReasonPhrase(statusCode);
        body += " (";
        body += QByteArray::number(statusCode);
        body += ")";
    }

    QBuffer buf(&body);
    return writeResponse(statusCode, header, "text/html", &buf, body.length());
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("statusCode:%d  contentType:%s  length:%s", statusCode, contentType.data(), qPrintable(QString::number(length)));

    header.setStatusLine(statusCode, THttpUtility::getResponseReasonPhrase(statusCode));
    if (!contentType.isEmpty())
        header.setContentType(contentType);

    return writeResponse(header, body, length);
}

qint64 TActionContext::writeResponse(THttpResponseHeader &header, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("length:%s", qPrintable(QString::number(length)));

    header.setContentLength(length);
    header.setRawHeader("Server", "TreeFrog server");
    header.setCurrentDate();

    // Write data
    return writeResponse(header, body);
}

void TActionContext::emitError(int )
{ }

TTemporaryFile &TActionContext::createTemporaryFile()
{
    TTemporaryFile *file = new TTemporaryFile();
    tempFiles << file;
    return *file;
}

QHostAddress TActionContext::clientAddress() const
{
    return httpReq->clientAddress();
}
<|endoftext|>


<commit_before>#include <boost/test/unit_test.hpp>

#include "Image.hpp"
#include "BitstreamGeneric.hpp"
#include "JpegSegments.hpp"

int add(int i, int j) { return i+j; }

BOOST_AUTO_TEST_CASE(test_add) {
    // seven ways to detect and report the same error:
    BOOST_CHECK(add(2, 2) == 4);        // #1 continues on error

    BOOST_REQUIRE(add(2, 2) == 4);      // #2 throws on error

    if (add(2, 2) != 4)
        BOOST_ERROR("Ouch...");            // #3 continues on error

    if (add(2, 2) != 4)
        BOOST_FAIL("Ouch...");             // #4 throws on error

    if (add(2, 2) != 4) throw "Ouch..."; // #5 throws on error

    BOOST_CHECK_MESSAGE(add(2, 2) == 4,  // #6 continues on error
                        "add(..) result: " << add(2, 2));

    BOOST_CHECK_EQUAL(add(2, 2), 4);	  // #7 continues on error
}

BOOST_AUTO_TEST_CASE(image_color_conv_test) {
    auto image = loadPPM("res/tester_p3.ppm");
    BOOST_CHECK(image.R(3, 0) == 15);
    BOOST_CHECK(image.G(3, 0) == 0);
    BOOST_CHECK(image.B(3, 0) == 15);

    BOOST_CHECK(image.R(1, 1) == 0);
    BOOST_CHECK(image.G(1, 1) == 15);
    BOOST_CHECK(image.B(1, 1) == 7);

    auto YCbCr_image = image.convertToColorSpace(Image::YCbCr);
    BOOST_CHECK(YCbCr_image.Y(0, 3) == 6);
    BOOST_CHECK(YCbCr_image.Cb(0, 3) == 133);
    BOOST_CHECK(YCbCr_image.Cr(0, 3) == 134);

    YCbCr_image = image.convertToColorSpace(Image::YCbCr);
    BOOST_CHECK(YCbCr_image.Y(0, 3) == 6);
    BOOST_CHECK(YCbCr_image.Cb(0, 3) == 133);
    BOOST_CHECK(YCbCr_image.Cr(0, 3) == 134);

    auto rgb_image = image.convertToColorSpace(Image::RGB);
    BOOST_CHECK(rgb_image.R(0, 3) == 15);
    BOOST_CHECK(rgb_image.G(0, 3) == 0);
    BOOST_CHECK(rgb_image.B(0, 3) == 15);
}

BOOST_AUTO_TEST_CASE(image_subsampling_test)
{
    auto image_orig = loadPPM("res/tester_p3.ppm");

    {
        auto image = image_orig;
        image.applySubsampling(Image::S444);
        BOOST_CHECK_EQUAL(image.B.size2(), 8);
        BOOST_CHECK_EQUAL(image.B.size1(), 8);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S422);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 8);

        // B channel:
        // 0 0
        // 0 0
        // 0 7
        // 15 0
        BOOST_CHECK_EQUAL(image.B(2, 0), 0);
        BOOST_CHECK_EQUAL(image.B(2, 1), 7);
        BOOST_CHECK_EQUAL(image.B(3, 0), 15);
        BOOST_CHECK_EQUAL(image.B(3, 1), 0);

        // G channel:
        // 0 0
        // 0 0
        // 0 15
        // 0 0
        BOOST_CHECK_EQUAL(image.G(2, 0), 0);
        BOOST_CHECK_EQUAL(image.G(2, 1), 15);
        BOOST_CHECK_EQUAL(image.G(3, 0), 0);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S411);
        BOOST_CHECK_EQUAL(image.B.size2(), 2);
        BOOST_CHECK_EQUAL(image.B.size1(), 8);

        // B channel:
        // 0
        // 0
        // 0
        // 15
        BOOST_CHECK_EQUAL(image.B(2, 0), 0);
        BOOST_CHECK_EQUAL(image.B(3, 0), 15);

        // G channel:
        // 0
        // 0
        // 0
        // 0
        BOOST_CHECK_EQUAL(image.G(2, 0), 0);
        BOOST_CHECK_EQUAL(image.G(3, 0), 0);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S420);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 4);

        // B channel:
        // 0 0
        // 0 7
        BOOST_CHECK_EQUAL(image.B(1, 0), 0);
        BOOST_CHECK_EQUAL(image.B(1, 1), 7);

        // G channel:
        // 0 0
        // 0 15
        BOOST_CHECK_EQUAL(image.G(1, 0), 0);
        BOOST_CHECK_EQUAL(image.G(1, 1), 15);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S420_m);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 4);

        // B channel:
        // 1 3
        // 3 1
        BOOST_CHECK_EQUAL(image.B(0, 0), 1);
        BOOST_CHECK_EQUAL(image.B(1, 0), 3);
        BOOST_CHECK_EQUAL(image.B(0, 1), 3);
        BOOST_CHECK_EQUAL(image.B(1, 1), 1);

        // G channel:
        // 3 0
        // 0 3
        BOOST_CHECK_EQUAL(image.G(0, 0), 3);
        BOOST_CHECK_EQUAL(image.G(1, 0), 0);
        BOOST_CHECK_EQUAL(image.G(0, 1), 0);
        BOOST_CHECK_EQUAL(image.G(1, 1), 3);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S420_lm);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 4);

        // B channel:
        // 0 0
        // 7 3
        BOOST_CHECK_EQUAL(image.B(0, 0), 0);
        BOOST_CHECK_EQUAL(image.B(0, 1), 0);
        BOOST_CHECK_EQUAL(image.B(1, 0), 7);
        BOOST_CHECK_EQUAL(image.B(1, 1), 3);

        // G channel:
        // 0 0
        // 0 7
        BOOST_CHECK_EQUAL(image.G(0, 0), 0);
        BOOST_CHECK_EQUAL(image.G(1, 0), 0);
        BOOST_CHECK_EQUAL(image.G(0, 1), 0);
        BOOST_CHECK_EQUAL(image.G(1, 1), 7);
    }
}

BOOST_AUTO_TEST_CASE(jpeg_segments_test)
{
    // playing with byte memory
    {
        Segment::Bytes<5> bytes{ { 0 } };
        Segment::set(bytes, { 1, 2, 3, 4, 255 });

        BOOST_CHECK_EQUAL(5, sizeof(bytes));

        BOOST_CHECK_EQUAL(bytes[0], 1);
        BOOST_CHECK_EQUAL(bytes[1], 2);
        BOOST_CHECK_EQUAL(bytes[2], 3);
        BOOST_CHECK_EQUAL(bytes[3], 4);
        BOOST_CHECK_EQUAL(bytes[4], 255);

        Byte copy[] = { 'J', 'F', 'I', 'F', '\0' };
        memcpy(bytes.data(), copy, 5);

        BOOST_CHECK_EQUAL(bytes[0], 'J');
        BOOST_CHECK_EQUAL(bytes[1], 'F');
        BOOST_CHECK_EQUAL(bytes[2], 'I');
        BOOST_CHECK_EQUAL(bytes[3], 'F');
        BOOST_CHECK_EQUAL(bytes[4], '\0');
    }

    // writing jpeg segments
    {
        Image img(4, 4, Image::RGB);
        img.writeJPEG(L"abc.jpeg");
    }

    // writing jpeg segments
    {
        auto image = loadPPM("res/Draigoch_p6.ppm");
        image.writeJPEG(L"Draigoch.jpeg");
    }

    // setting segment data
    {
        using namespace Segment;

        // APP0 Seg
        BOOST_CHECK_EQUAL(18, sizeof(APP0));

        APP0.setLen(256)
            .setXdensity(1)
            .setYdensity(1);

        BOOST_CHECK_EQUAL(APP0.len[0], 1);
        BOOST_CHECK_EQUAL(APP0.len[1], 0);

        BOOST_CHECK_EQUAL(APP0.x_density[0], 0);
        BOOST_CHECK_EQUAL(APP0.x_density[1], 1);

        BOOST_CHECK_EQUAL(APP0.y_density[0], 0);
        BOOST_CHECK_EQUAL(APP0.y_density[1], 1);

        // SOF0 seg (3 component version)
        BOOST_CHECK_EQUAL(13, sizeof(Segment::SOF0_1c));
        BOOST_CHECK_EQUAL(19, sizeof(Segment::SOF0_3c));

        SOF0_3c.setImageSizeX(256)
               .setImageSizeY(256)
               .setCompSetup(
        { CompSetup::Y, CompSetup::NoSubSampling, 0,
          CompSetup::Cb, CompSetup::Half, 1,
          CompSetup::Cr, CompSetup::Half, 2, }
        );

        BOOST_CHECK_EQUAL(SOF0_3c.image_size_x[0], 1);
        BOOST_CHECK_EQUAL(SOF0_3c.image_size_x[1], 0);

        BOOST_CHECK_EQUAL(SOF0_3c.image_size_y[0], 1);
        BOOST_CHECK_EQUAL(SOF0_3c.image_size_y[1], 0);

        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[0], CompSetup::Y);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[1], CompSetup::NoSubSampling);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[2], 0);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[3], CompSetup::Cb);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[4], CompSetup::Half);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[5], 1);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[6], CompSetup::Cr);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[7], CompSetup::Half);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[8], 2);
    }
}<commit_msg>fix color conversion tests (as we are operating on floating point numbers now)<commit_after>#include <boost/test/unit_test.hpp>

#include "Image.hpp"
#include "BitstreamGeneric.hpp"
#include "JpegSegments.hpp"

int add(int i, int j) { return i+j; }

#define CHECK_CLOSE(left, right) BOOST_CHECK_CLOSE(left, right, 0.1)

BOOST_AUTO_TEST_CASE(test_add) {
    // seven ways to detect and report the same error:
    BOOST_CHECK(add(2, 2) == 4);        // #1 continues on error

    BOOST_REQUIRE(add(2, 2) == 4);      // #2 throws on error

    if (add(2, 2) != 4)
        BOOST_ERROR("Ouch...");            // #3 continues on error

    if (add(2, 2) != 4)
        BOOST_FAIL("Ouch...");             // #4 throws on error

    if (add(2, 2) != 4) throw "Ouch..."; // #5 throws on error

    BOOST_CHECK_MESSAGE(add(2, 2) == 4,  // #6 continues on error
                        "add(..) result: " << add(2, 2));

    BOOST_CHECK_EQUAL(add(2, 2), 4);	  // #7 continues on error
}

BOOST_AUTO_TEST_CASE(image_loading_test) {
    auto image = loadPPM("res/tester_p3.ppm");
    BOOST_CHECK(image.R(0, 0) == 0);
    BOOST_CHECK(image.G(0, 0) == 0);
    BOOST_CHECK(image.B(0, 0) == 0);

    BOOST_CHECK(image.R(0, 3) == 15);
    BOOST_CHECK(image.G(0, 3) == 0);
    BOOST_CHECK(image.B(0, 3) == 15);

    BOOST_CHECK(image.R(2, 2) == 0);
    BOOST_CHECK(image.G(2, 2) == 15);
    BOOST_CHECK(image.B(2, 2) == 7);

    BOOST_CHECK(image.R(0, 0) == 0);
    BOOST_CHECK(image.G(0, 0) == 0);
    BOOST_CHECK(image.B(0, 0) == 0);

    // out of bounds indexing
    BOOST_CHECK(image.R(7, 0) == 15);
    BOOST_CHECK(image.G(7, 0) == 0);
    BOOST_CHECK(image.B(7, 0) == 15);

    BOOST_CHECK(image.R(0, 7) == 15);
    BOOST_CHECK(image.G(0, 7) == 0);
    BOOST_CHECK(image.B(0, 7) == 15);

    BOOST_CHECK(image.R(1, 7) == 0);
    BOOST_CHECK(image.G(1, 7) == 0);
    BOOST_CHECK(image.B(1, 7) == 0);

    BOOST_CHECK(image.R(7, 1) == 0);
    BOOST_CHECK(image.G(7, 1) == 0);
    BOOST_CHECK(image.B(7, 1) == 0);

    BOOST_CHECK(image.R(7, 7) == 0);
    BOOST_CHECK(image.G(7, 7) == 0);
    BOOST_CHECK(image.B(7, 7) == 0);
}

BOOST_AUTO_TEST_CASE(image_color_conv_test) {
    auto image = loadPPM("res/tester_p3.ppm");

    auto YCbCr_image = image.convertToColorSpace(Image::YCbCr);
    CHECK_CLOSE(YCbCr_image.Y(0, 3), 6.1949);
    CHECK_CLOSE(YCbCr_image.Cb(0, 3), 132.9695);
    CHECK_CLOSE(YCbCr_image.Cr(0, 3), 134.2805);

    CHECK_CLOSE(YCbCr_image. Y(1, 1), 9.6030);
    CHECK_CLOSE(YCbCr_image.Cb(1, 1), 126.5319);
    CHECK_CLOSE(YCbCr_image.Cr(1, 1), 121.1519);

    // converting from YCbCr to YCbCr doesn't do a thing!
    YCbCr_image = image.convertToColorSpace(Image::YCbCr);
    CHECK_CLOSE(YCbCr_image.Y(0, 3), 6.1949);
    CHECK_CLOSE(YCbCr_image.Cb(0, 3), 132.9695);
    CHECK_CLOSE(YCbCr_image.Cr(0, 3), 134.2805);

    auto rgb_image = image.convertToColorSpace(Image::RGB);
    CHECK_CLOSE(rgb_image.R(0, 3), 15);
    CHECK_CLOSE(rgb_image.G(0, 3), 0);
    CHECK_CLOSE(rgb_image.B(0, 3), 15);

    CHECK_CLOSE(rgb_image.R(1, 1), 0);
    CHECK_CLOSE(rgb_image.G(1, 1), 15);
    CHECK_CLOSE(rgb_image.B(1, 1), 7);
}

BOOST_AUTO_TEST_CASE(image_subsampling_test)
{
    auto image_orig = loadPPM("res/tester_p3.ppm");

    {
        auto image = image_orig;
        image.applySubsampling(Image::S444);
        BOOST_CHECK_EQUAL(image.B.size2(), 8);
        BOOST_CHECK_EQUAL(image.B.size1(), 8);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S422);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 8);

        // B channel:
        // 0 0
        // 0 0
        // 0 7
        // 15 0
        BOOST_CHECK_EQUAL(image.B(2, 0), 0);
        BOOST_CHECK_EQUAL(image.B(2, 1), 7);
        BOOST_CHECK_EQUAL(image.B(3, 0), 15);
        BOOST_CHECK_EQUAL(image.B(3, 1), 0);

        // G channel:
        // 0 0
        // 0 0
        // 0 15
        // 0 0
        BOOST_CHECK_EQUAL(image.G(2, 0), 0);
        BOOST_CHECK_EQUAL(image.G(2, 1), 15);
        BOOST_CHECK_EQUAL(image.G(3, 0), 0);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S411);
        BOOST_CHECK_EQUAL(image.B.size2(), 2);
        BOOST_CHECK_EQUAL(image.B.size1(), 8);

        // B channel:
        // 0
        // 0
        // 0
        // 15
        BOOST_CHECK_EQUAL(image.B(2, 0), 0);
        BOOST_CHECK_EQUAL(image.B(3, 0), 15);

        // G channel:
        // 0
        // 0
        // 0
        // 0
        BOOST_CHECK_EQUAL(image.G(2, 0), 0);
        BOOST_CHECK_EQUAL(image.G(3, 0), 0);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S420);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 4);

        // B channel:
        // 0 0
        // 0 7
        BOOST_CHECK_EQUAL(image.B(1, 0), 0);
        BOOST_CHECK_EQUAL(image.B(1, 1), 7);

        // G channel:
        // 0 0
        // 0 15
        BOOST_CHECK_EQUAL(image.G(1, 0), 0);
        BOOST_CHECK_EQUAL(image.G(1, 1), 15);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S420_m);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 4);

        // B channel:
        // 1 3
        // 3 1
        BOOST_CHECK_EQUAL(image.B(0, 0), 1);
        BOOST_CHECK_EQUAL(image.B(1, 0), 3);
        BOOST_CHECK_EQUAL(image.B(0, 1), 3);
        BOOST_CHECK_EQUAL(image.B(1, 1), 1);

        // G channel:
        // 3 0
        // 0 3
        BOOST_CHECK_EQUAL(image.G(0, 0), 3);
        BOOST_CHECK_EQUAL(image.G(1, 0), 0);
        BOOST_CHECK_EQUAL(image.G(0, 1), 0);
        BOOST_CHECK_EQUAL(image.G(1, 1), 3);
    }

    {
        auto image = image_orig;
        image.applySubsampling(Image::S420_lm);
        BOOST_CHECK_EQUAL(image.B.size2(), 4);
        BOOST_CHECK_EQUAL(image.B.size1(), 4);

        // B channel:
        // 0 0
        // 7 3
        BOOST_CHECK_EQUAL(image.B(0, 0), 0);
        BOOST_CHECK_EQUAL(image.B(0, 1), 0);
        BOOST_CHECK_EQUAL(image.B(1, 0), 7);
        BOOST_CHECK_EQUAL(image.B(1, 1), 3);

        // G channel:
        // 0 0
        // 0 7
        BOOST_CHECK_EQUAL(image.G(0, 0), 0);
        BOOST_CHECK_EQUAL(image.G(1, 0), 0);
        BOOST_CHECK_EQUAL(image.G(0, 1), 0);
        BOOST_CHECK_EQUAL(image.G(1, 1), 7);
    }
}

BOOST_AUTO_TEST_CASE(jpeg_segments_test)
{
    // playing with byte memory
    {
        Segment::Bytes<5> bytes{ { 0 } };
        Segment::set(bytes, { 1, 2, 3, 4, 255 });

        BOOST_CHECK_EQUAL(5, sizeof(bytes));

        BOOST_CHECK_EQUAL(bytes[0], 1);
        BOOST_CHECK_EQUAL(bytes[1], 2);
        BOOST_CHECK_EQUAL(bytes[2], 3);
        BOOST_CHECK_EQUAL(bytes[3], 4);
        BOOST_CHECK_EQUAL(bytes[4], 255);

        Byte copy[] = { 'J', 'F', 'I', 'F', '\0' };
        memcpy(bytes.data(), copy, 5);

        BOOST_CHECK_EQUAL(bytes[0], 'J');
        BOOST_CHECK_EQUAL(bytes[1], 'F');
        BOOST_CHECK_EQUAL(bytes[2], 'I');
        BOOST_CHECK_EQUAL(bytes[3], 'F');
        BOOST_CHECK_EQUAL(bytes[4], '\0');
    }

    // writing jpeg segments
    {
        Image img(4, 4, Image::RGB);
        img.writeJPEG(L"abc.jpeg");
    }

    // writing jpeg segments
    {
        auto image = loadPPM("res/Draigoch_p6.ppm");
        image.writeJPEG(L"Draigoch.jpeg");
    }

    // setting segment data
    {
        using namespace Segment;

        // APP0 Seg
        BOOST_CHECK_EQUAL(18, sizeof(APP0));

        APP0.setLen(256)
            .setXdensity(1)
            .setYdensity(1);

        BOOST_CHECK_EQUAL(APP0.len[0], 1);
        BOOST_CHECK_EQUAL(APP0.len[1], 0);

        BOOST_CHECK_EQUAL(APP0.x_density[0], 0);
        BOOST_CHECK_EQUAL(APP0.x_density[1], 1);

        BOOST_CHECK_EQUAL(APP0.y_density[0], 0);
        BOOST_CHECK_EQUAL(APP0.y_density[1], 1);

        // SOF0 seg (3 component version)
        BOOST_CHECK_EQUAL(13, sizeof(Segment::SOF0_1c));
        BOOST_CHECK_EQUAL(19, sizeof(Segment::SOF0_3c));

        SOF0_3c.setImageSizeX(256)
               .setImageSizeY(256)
               .setCompSetup(
        { CompSetup::Y, CompSetup::NoSubSampling, 0,
          CompSetup::Cb, CompSetup::Half, 1,
          CompSetup::Cr, CompSetup::Half, 2, }
        );

        BOOST_CHECK_EQUAL(SOF0_3c.image_size_x[0], 1);
        BOOST_CHECK_EQUAL(SOF0_3c.image_size_x[1], 0);

        BOOST_CHECK_EQUAL(SOF0_3c.image_size_y[0], 1);
        BOOST_CHECK_EQUAL(SOF0_3c.image_size_y[1], 0);

        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[0], CompSetup::Y);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[1], CompSetup::NoSubSampling);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[2], 0);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[3], CompSetup::Cb);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[4], CompSetup::Half);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[5], 1);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[6], CompSetup::Cr);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[7], CompSetup::Half);
        BOOST_CHECK_EQUAL(SOF0_3c.component_setup[8], 2);
    }
}<|endoftext|>