<commit_before>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "updater.h"
#include "clientversion.h"
#include "util.h"
#include "chainparams.h"
#include "rpc/protocol.h"

#include <stdio.h>
#include <curl/curl.h>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>

using namespace boost::filesystem;

struct DownloadProgress {
    double lastruntime;
    CURL *curl;
    void(*progressFunction)(curl_off_t, curl_off_t);
};

// Global updater instance
Updater updater;

Updater::Updater() :
    os(DetectOS()),
    status(false),
    version(-1),
    stopDownload(false),
    url("https://raw.githubusercontent.com/terracoin/terracoin/master/update.json")
{
}

Updater::OS Updater::DetectOS()
{
    OS os = Updater::LINUX_32;
    #ifdef _WIN32
        os = Updater::WINDOWS_32;
        #ifdef _WIN64
            os = Updater::WINDOWS_64;
        #endif
    #elif __APPLE__
        os = Updater::MAC_OS;
    #elif __linux__
        os = Updater::LINUX_32;
        #if defined(__i386__) || defined(__i686__) || defined(__i486__) || defined(__i586__)
            os = Updater::LINUX_32;
        #elif defined(__x86_64__) || defined(__amd64__)
            os = Updater::LINUX_64;
        #endif
    #endif
    return os;
}

size_t GetUpdateData(const char *data, size_t size, size_t nmemb, std::string *updateData) {
    size_t len = size * nmemb;

    if (updateData != NULL)
    {
        // Append the data to the buffer
        updateData->append(data, len);
    }

    return len;
}

UniValue Updater::ParseJson(std::string info)
{
    UniValue value(UniValue::VOBJ);
    value.read(info);
    return value;
}

bool Updater::NeedToBeUpdated()
{
    if (jsonData.getType() == UniValue::VOBJ)
    {
        UniValue version = jsonData["NeedToBeUpdated"];
        if (version.getType() == UniValue::VBOOL)
        {
            return version.getBool();
        }
    }
    return false;
}

void Updater::SetJsonPath()
{
    updaterInfoUrl = url;
    if (mapArgs.count("-updateurl"))
    {
        updaterInfoUrl = mapArgs["-updateurl"];
    }
}

void Updater::LoadUpdateInfo()
{
    CURL *curl;
    std::string updateData;
    SetJsonPath();
    CURLcode res = CURLE_FAILED_INIT;
    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, updaterInfoUrl.c_str());
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, GetUpdateData);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &updateData);
        res = curl_easy_perform(curl);
        if (res == CURLE_OK)
        {
            long response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
            if (response_code == HTTP_OK)
            {
                CheckAndUpdateStatus(updateData);
            }
            else
            {
                LogPrintf("Updater::GetUpdateInfo() - Error! Server response code - %d\n", response_code);
                throw std::runtime_error(strprintf("Error! Failed to get update information. \nServer response code - %d\n", response_code));
            }
        }
        else
        {
            LogPrintf("Updater::GetUpdateInfo() - Error! Couldn't get data json. Error code - %d\n", res);
            throw std::runtime_error(strprintf("Error! Couldn't get data json. Error code - %d\n", res));
        }
        curl_easy_cleanup(curl);
    }
}

std::string Updater::GetOsString(boost::optional<OS> os)
{
    if (!os) {
        return GetOsString(GetOS());
    }
    switch(os.get()) {
        case Updater::LINUX_32:
            return "Linux32";
        case Updater::LINUX_64:
            return "Linux64";
        case Updater::WINDOWS_32:
            return "Win32";
        case Updater::WINDOWS_64:
            return "Win64";
        case Updater::MAC_OS:
            return "Osx";
        default:
            assert(false);
    }
    return "";
}

std::string Updater::GetUrl(const UniValue& value)
{
    if (value.getType() == UniValue::VOBJ)
    {
        UniValue urlUniValue = value["Url"];
        if (urlUniValue.getType() == UniValue::VSTR)
        {
            return urlUniValue.get_str();
        }
    }
    return "";
}

std::string Updater::GetSha256sum(UniValue value)
{
    if (value.getType() == UniValue::VOBJ)
    {
        UniValue sha256sumUniValue = value["sha256sum"];
        if (sha256sumUniValue.getType() == UniValue::VSTR)
        {
            return sha256sumUniValue.get_str();
        }
    }
    return "";
}

int Updater::GetSize(UniValue value)
{
    if (value.getType() == UniValue::VOBJ && value.exists("size"))
    {
        UniValue sizeUniValue = value["size"];
        int size = -1;
        switch(sizeUniValue.getType()) {
            case UniValue::VNUM:
                size = sizeUniValue.get_int();
                break;
            default:
                std::istringstream ss(sizeUniValue.get_str());
                ss >> size;
                break;
        }

        return size;
    }
    return -1;
}

int Updater::GetVersionFromJson()
{
    if (jsonData.getType() == UniValue::VOBJ)
    {
        UniValue version = jsonData["Version"];
        if (version.getType() == UniValue::VNUM)
        {
            return version.get_int();
        }
    }
    return -1;
}

std::string Updater::GetDownloadUrl(boost::optional<OS> version)
{
    UniValue json = find_value(jsonData.get_obj(), GetOsString(version));
    return GetUrl(json);
}

std::string Updater::GetDownloadSha256Sum(boost::optional<OS> version)
{
    UniValue json = find_value(jsonData.get_obj(), GetOsString(version));
    return GetSha256sum(json);
}

int Updater::GetDownloadSize(boost::optional<OS> version)
{
    UniValue json = find_value(jsonData.get_obj(), GetOsString(version));
    return GetSize(json);
}

static size_t write_data(void *ptr, size_t size, size_t nmemb, const void *stream)
{
    size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
    return written;
}

static int xferinfo(void *p,
        curl_off_t dltotal, curl_off_t dlnow,
        curl_off_t ultotal, curl_off_t ulnow)
{
    struct DownloadProgress *myp = (struct DownloadProgress*)p;
    CURL *curl = myp->curl;
    double curtime = 0;

    curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);

    myp->lastruntime = curtime;

    myp->progressFunction(dlnow, dltotal);

    return updater.GetStopDownload();
}

void Updater::DownloadFileAsync(std::string url, std::string fileName, void(progressFunction)(curl_off_t, curl_off_t))
{
    boost::thread t(boost::bind(&Updater::DownloadFile, this, url, fileName, progressFunction));
}

CURLcode Updater::DownloadFile(std::string url, std::string fileName, void(progressFunction)(curl_off_t, curl_off_t))
{
    stopDownload = false;
    CURL *curl_handle;
    FILE *pagefile;
    CURLcode res = CURLE_FAILED_INIT;
    struct DownloadProgress prog;

    prog.progressFunction = progressFunction;

    curl_global_init(CURL_GLOBAL_ALL);
    curl_handle = curl_easy_init();
    if (curl_handle) {
        prog.lastruntime = 0;
        prog.curl = curl_handle;
        curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, false);
        curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
        curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);

        curl_easy_setopt(curl_handle, CURLOPT_XFERINFOFUNCTION, xferinfo);
        curl_easy_setopt(curl_handle, CURLOPT_XFERINFODATA, &prog);
        pagefile = fopen(fileName.c_str(), "wb");
        if(pagefile) {
            curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);
            res = curl_easy_perform(curl_handle);
            fclose(pagefile);
        }
        curl_easy_cleanup(curl_handle);

        if (res != CURLE_OK)
        {
            LogPrintf("Updater::DownloadFile() - Error! Failed to download file - %s. Error code - %d\n", url, res);
        }
    } else {
        LogPrintf("Updater::DownloadFile() - Error! Failed to start curl.\n");
    }
    return res;
}

void Updater::StopDownload()
{
    stopDownload = true;
}

void Updater::CheckAndUpdateStatus(const std::string& updateData)
{
    jsonData = ParseJson(updateData);
    version = GetVersionFromJson();
    LogPrintf("Updater::GetUpdateInfo() - Got version from json: %d\n", version);
    if (version > CLIENT_VERSION && NeedToBeUpdated())
    {
        LogPrintf("Updater::GetUpdateInfo() - Version is old\n");
        // There is an update
        status = true;
    }
}

bool Updater::GetStatus()
{
    LoadUpdateInfo();
    return status;
}
<commit_msg>curl prefers 0L over false<commit_after>// Copyright (c) 2014-2018 The Crown developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "updater.h"
#include "clientversion.h"
#include "util.h"
#include "chainparams.h"
#include "rpc/protocol.h"

#include <stdio.h>
#include <curl/curl.h>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>

using namespace boost::filesystem;

struct DownloadProgress {
    double lastruntime;
    CURL *curl;
    void(*progressFunction)(curl_off_t, curl_off_t);
};

// Global updater instance
Updater updater;

Updater::Updater() :
    os(DetectOS()),
    status(false),
    version(-1),
    stopDownload(false),
    url("https://raw.githubusercontent.com/terracoin/terracoin/master/update.json")
{
}

Updater::OS Updater::DetectOS()
{
    OS os = Updater::LINUX_32;
    #ifdef _WIN32
        os = Updater::WINDOWS_32;
        #ifdef _WIN64
            os = Updater::WINDOWS_64;
        #endif
    #elif __APPLE__
        os = Updater::MAC_OS;
    #elif __linux__
        os = Updater::LINUX_32;
        #if defined(__i386__) || defined(__i686__) || defined(__i486__) || defined(__i586__)
            os = Updater::LINUX_32;
        #elif defined(__x86_64__) || defined(__amd64__)
            os = Updater::LINUX_64;
        #endif
    #endif
    return os;
}

size_t GetUpdateData(const char *data, size_t size, size_t nmemb, std::string *updateData) {
    size_t len = size * nmemb;

    if (updateData != NULL)
    {
        // Append the data to the buffer
        updateData->append(data, len);
    }

    return len;
}

UniValue Updater::ParseJson(std::string info)
{
    UniValue value(UniValue::VOBJ);
    value.read(info);
    return value;
}

bool Updater::NeedToBeUpdated()
{
    if (jsonData.getType() == UniValue::VOBJ)
    {
        UniValue version = jsonData["NeedToBeUpdated"];
        if (version.getType() == UniValue::VBOOL)
        {
            return version.getBool();
        }
    }
    return false;
}

void Updater::SetJsonPath()
{
    updaterInfoUrl = url;
    if (mapArgs.count("-updateurl"))
    {
        updaterInfoUrl = mapArgs["-updateurl"];
    }
}

void Updater::LoadUpdateInfo()
{
    CURL *curl;
    std::string updateData;
    SetJsonPath();
    CURLcode res = CURLE_FAILED_INIT;
    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, updaterInfoUrl.c_str());
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, GetUpdateData);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &updateData);
        res = curl_easy_perform(curl);
        if (res == CURLE_OK)
        {
            long response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
            if (response_code == HTTP_OK)
            {
                CheckAndUpdateStatus(updateData);
            }
            else
            {
                LogPrintf("Updater::GetUpdateInfo() - Error! Server response code - %d\n", response_code);
                throw std::runtime_error(strprintf("Error! Failed to get update information. \nServer response code - %d\n", response_code));
            }
        }
        else
        {
            LogPrintf("Updater::GetUpdateInfo() - Error! Couldn't get data json. Error code - %d\n", res);
            throw std::runtime_error(strprintf("Error! Couldn't get data json. Error code - %d\n", res));
        }
        curl_easy_cleanup(curl);
    }
}

std::string Updater::GetOsString(boost::optional<OS> os)
{
    if (!os) {
        return GetOsString(GetOS());
    }
    switch(os.get()) {
        case Updater::LINUX_32:
            return "Linux32";
        case Updater::LINUX_64:
            return "Linux64";
        case Updater::WINDOWS_32:
            return "Win32";
        case Updater::WINDOWS_64:
            return "Win64";
        case Updater::MAC_OS:
            return "Osx";
        default:
            assert(false);
    }
    return "";
}

std::string Updater::GetUrl(const UniValue& value)
{
    if (value.getType() == UniValue::VOBJ)
    {
        UniValue urlUniValue = value["Url"];
        if (urlUniValue.getType() == UniValue::VSTR)
        {
            return urlUniValue.get_str();
        }
    }
    return "";
}

std::string Updater::GetSha256sum(UniValue value)
{
    if (value.getType() == UniValue::VOBJ)
    {
        UniValue sha256sumUniValue = value["sha256sum"];
        if (sha256sumUniValue.getType() == UniValue::VSTR)
        {
            return sha256sumUniValue.get_str();
        }
    }
    return "";
}

int Updater::GetSize(UniValue value)
{
    if (value.getType() == UniValue::VOBJ && value.exists("size"))
    {
        UniValue sizeUniValue = value["size"];
        int size = -1;
        switch(sizeUniValue.getType()) {
            case UniValue::VNUM:
                size = sizeUniValue.get_int();
                break;
            default:
                std::istringstream ss(sizeUniValue.get_str());
                ss >> size;
                break;
        }

        return size;
    }
    return -1;
}

int Updater::GetVersionFromJson()
{
    if (jsonData.getType() == UniValue::VOBJ)
    {
        UniValue version = jsonData["Version"];
        if (version.getType() == UniValue::VNUM)
        {
            return version.get_int();
        }
    }
    return -1;
}

std::string Updater::GetDownloadUrl(boost::optional<OS> version)
{
    UniValue json = find_value(jsonData.get_obj(), GetOsString(version));
    return GetUrl(json);
}

std::string Updater::GetDownloadSha256Sum(boost::optional<OS> version)
{
    UniValue json = find_value(jsonData.get_obj(), GetOsString(version));
    return GetSha256sum(json);
}

int Updater::GetDownloadSize(boost::optional<OS> version)
{
    UniValue json = find_value(jsonData.get_obj(), GetOsString(version));
    return GetSize(json);
}

static size_t write_data(void *ptr, size_t size, size_t nmemb, const void *stream)
{
    size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
    return written;
}

static int xferinfo(void *p,
        curl_off_t dltotal, curl_off_t dlnow,
        curl_off_t ultotal, curl_off_t ulnow)
{
    struct DownloadProgress *myp = (struct DownloadProgress*)p;
    CURL *curl = myp->curl;
    double curtime = 0;

    curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &curtime);

    myp->lastruntime = curtime;

    myp->progressFunction(dlnow, dltotal);

    return updater.GetStopDownload();
}

void Updater::DownloadFileAsync(std::string url, std::string fileName, void(progressFunction)(curl_off_t, curl_off_t))
{
    boost::thread t(boost::bind(&Updater::DownloadFile, this, url, fileName, progressFunction));
}

CURLcode Updater::DownloadFile(std::string url, std::string fileName, void(progressFunction)(curl_off_t, curl_off_t))
{
    stopDownload = false;
    CURL *curl_handle;
    FILE *pagefile;
    CURLcode res = CURLE_FAILED_INIT;
    struct DownloadProgress prog;

    prog.progressFunction = progressFunction;

    curl_global_init(CURL_GLOBAL_ALL);
    curl_handle = curl_easy_init();
    if (curl_handle) {
        prog.lastruntime = 0;
        prog.curl = curl_handle;
        curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
        curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);

        curl_easy_setopt(curl_handle, CURLOPT_XFERINFOFUNCTION, xferinfo);
        curl_easy_setopt(curl_handle, CURLOPT_XFERINFODATA, &prog);
        pagefile = fopen(fileName.c_str(), "wb");
        if(pagefile) {
            curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);
            res = curl_easy_perform(curl_handle);
            fclose(pagefile);
        }
        curl_easy_cleanup(curl_handle);

        if (res != CURLE_OK)
        {
            LogPrintf("Updater::DownloadFile() - Error! Failed to download file - %s. Error code - %d\n", url, res);
        }
    } else {
        LogPrintf("Updater::DownloadFile() - Error! Failed to start curl.\n");
    }
    return res;
}

void Updater::StopDownload()
{
    stopDownload = true;
}

void Updater::CheckAndUpdateStatus(const std::string& updateData)
{
    jsonData = ParseJson(updateData);
    version = GetVersionFromJson();
    LogPrintf("Updater::GetUpdateInfo() - Got version from json: %d\n", version);
    if (version > CLIENT_VERSION && NeedToBeUpdated())
    {
        LogPrintf("Updater::GetUpdateInfo() - Version is old\n");
        // There is an update
        status = true;
    }
}

bool Updater::GetStatus()
{
    LoadUpdateInfo();
    return status;
}
<|endoftext|>


<commit_before>#pragma once

#include <type_traits>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstddef>
#include <memory>
#include <string>
#include <uv.h>
#include "event.hpp"
#include "request.hpp"
#include "handle.hpp"
#include "util.hpp"

namespace uvw {

/**
 * @brief SendEvent event.
 *
 * It will be emitted by UDPHandle according with its functionalities.
 */
struct SendEvent: Event<SendEvent> {};

/**
 * @brief UDPDataEvent event.
 *
 * It will be emitted by UDPHandle according with its functionalities.
 */
struct UDPDataEvent: Event<UDPDataEvent> {
    explicit UDPDataEvent(Addr sender, std::unique_ptr<const char[]> data, std::size_t length, bool partial) noexcept
        : data{std::move(data)}, length{length}, sender{std::move(sender)}, partial{partial}
    {}

    std::unique_ptr<const char[]> data; /*!< A bunch of data read on the stream. */
    std::size_t length;  /*!< The amount of data read on the stream. */
    Addr sender; /*!< A valid instance of Addr. */
    bool partial; /*!< True if the message was truncated, false otherwise. */
};

namespace details {

enum class UVUdpFlags: std::underlying_type_t<uv_udp_flags> {
    IPV6ONLY = UV_UDP_IPV6ONLY,
    REUSEADDR = UV_UDP_REUSEADDR
};

enum class UVMembership: std::underlying_type_t<uv_membership> {
    LEAVE_GROUP = UV_LEAVE_GROUP,
    JOIN_GROUP = UV_JOIN_GROUP
};

class SendReq final: public Request<SendReq, uv_udp_send_t> {
public:
    using Deleter = void(*)(uv_buf_t *);

    SendReq(ConstructorAccess ca, std::shared_ptr<Loop> loop, std::unique_ptr<uv_buf_t[], Deleter> bufs, std::size_t nbufs)
        : Request<SendReq, uv_udp_send_t>{std::move(ca), std::move(loop)},
          bufs{std::move(bufs)},
          nbufs{nbufs}
    {}

    void send(uv_udp_t *handle, const struct sockaddr* addr) {
        invoke(&uv_udp_send, get(), handle, bufs.get(), nbufs, addr, &defaultCallback<SendEvent>);
    }

private:
    std::unique_ptr<uv_buf_t[], Deleter> bufs;
    std::size_t nbufs;
};

}

/**
 * @brief The UDPHandle handle.
 *
 * UDP handles encapsulate UDP communication for both clients and servers.

 * By default, _IPv4_ is used as a template parameter. The handle already
 * supports _IPv6_ out-of-the-box by using `uvw::IPv6`.
 *
 * To create an `UDPHandle` through a `Loop`, arguments follow:
 *
 * * An optional integer value that indicates optional flags used to initialize
 * the socket.
 *
 * See the official
 * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_init_ex)
 * for further details.
 */
class UDPHandle final: public Handle<UDPHandle, uv_udp_t> {
    template<typename I>
    static void recvCallback(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, unsigned flags) {
        typename details::IpTraits::Type *aptr = reinterpret_cast<const typename details::IpTraits::Type *>(addr);

        UDPHandle &udp = *(static_cast<UDPHandle*>(handle->data));
        // data will be destroyed no matter of what the value of nread is
        std::unique_ptr<const char[]> data{buf->base};

        if(nread > 0) {
            // data available (can be truncated)
            udp.publish(UDPDataEvent{details::address(aptr), std::move(data), nread, flags & UV_UDP_PARTIAL});
        } else if(nread == 0 && addr == nullptr) {
            // no more data to be read, doing nothing is fine
        } else if(nread == 0 && addr != nullptr) {
            // empty udp packet
            udp.publish(UDPDataEvent{details::address(aptr), std::move(data), nread, false});
        } else {
            // transmission error
            udp.publish(ErrorEvent(nread));
        }
    }

public:
    using Membership = details::UVMembership;
    using Bind = details::UVUdpFlags;
    using IPv4 = uvw::IPv4;
    using IPv6 = uvw::IPv6;

    using Handle::Handle;

    explicit UDPHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, unsigned int f)
        : Handle{std::move(ca), std::move(ref)}, tag{FLAGS}, flags{f}
    {}

    /**
     * @brief Initializes the handle. The actual socket is created lazily.
     * @return True in case of success, false otherwise.
     */
    bool init() {
        return (tag == FLAGS)
                ? initialize(&uv_udp_init_ex, flags)
                : initialize(&uv_udp_init);
    }

    /**
     * @brief Opens an existing file descriptor or SOCKET as a UDP handle.
     *
     * The passed file descriptor or SOCKET is not checked for its type, but
     * it’s required that it represents a valid datagram socket.
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_open)
     * for further details.
     *
     * @param sock A valid socket handle (either a file descriptor or a SOCKET).
     */
    void open(OSSocketHandle sock) {
        invoke(&uv_udp_open, get(), sock);
    }

    /**
     * @brief Binds the UDP handle to an IP address and port.
     *
     * Available flags are:
     *
     * * `UDPHandle::Bind::IPV6ONLY`
     * * `UDPHandle::Bind::REUSEADDR`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags)
     * for further details.
     *
     * @param ip The IP address to which to bind.
     * @param port The port to which to bind.
     * @param flags Optional additional flags.
     */
    template<typename I = IPv4>
    void bind(std::string ip, unsigned int port, Flags<Bind> flags = Flags<Bind>{}) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);
        invoke(&uv_udp_bind, get(), reinterpret_cast<const sockaddr *>(&addr), flags);
    }

    /**
     * @brief Binds the UDP handle to an IP address and port.
     *
     * Available flags are:
     *
     * * `UDPHandle::Bind::IPV6ONLY`
     * * `UDPHandle::Bind::REUSEADDR`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags)
     * for further details.
     *
     * @param addr A valid instance of Addr.
     * @param flags Optional additional flags.
     */
    template<typename I = IPv4>
    void bind(Addr addr, Flags<Bind> flags = Flags<Bind>{}) {
        bind(addr.ip, addr.port, flags);
    }

    /**
     * @brief Get the local IP and port of the UDP handle.
     * @return A valid instance of Addr, an empty one in case of errors.
     */
    template<typename I = IPv4>
    Addr sock() const noexcept {
        return details::address(&uv_udp_getsockname, get());
    }

    /**
     * @brief Sets membership for a multicast address.
     *
     * Available values for `membership` are:
     *
     * * `UDPHandle::Membership::LEAVE_GROUP`
     * * `UDPHandle::Membership::JOIN_GROUP`
     *
     * @param multicast Multicast address to set membership for.
     * @param iface Interface address.
     * @param membership Action to be performed.
     * @return True in case of success, false otherwise.
     */
    template<typename I = IPv4>
    bool multicastMembership(std::string multicast, std::string iface, Membership membership) {
        return (0 == uv_udp_set_membership(get(), multicast.data(), iface.data(), static_cast<uv_membership>(membership)));
    }

    /**
     * @brief Sets IP multicast loop flag.
     *
     * This makes multicast packets loop back to local sockets.
     *
     * @param enable True to enable multicast loop, false otherwise.
     * @return True in case of success, false otherwise.
     */
    bool multicastLoop(bool enable = true) {
        return (0 == uv_udp_set_multicast_loop(get(), enable));
    }

    /**
     * @brief Sets the multicast ttl.
     * @param val A value in the range `[1, 255]`.
     * @return True in case of success, false otherwise.
     */
    bool multicastTtl(int val) {
        return (0 == uv_udp_set_multicast_ttl(get(), val > 255 ? 255 : val));
    }

    /**
     * @brief Sets the multicast interface to send or receive data on.
     * @param iface Interface address.
     * @return True in case of success, false otherwise.
     */
    template<typename I = IPv4>
    bool multicastInterface(std::string iface) {
        return (0 == uv_udp_set_multicast_interface(get(), iface.data()));
    }

    /**
     * @brief Sets broadcast on or off.
     * @param enable True to set broadcast on, false otherwise.
     * @return True in case of success, false otherwise.
     */
    bool broadcast(bool enable = false) {
        return (0 == uv_udp_set_broadcast(get(), enable));
    }

    /**
     * @brief Sets the time to live.
     * @param val A value in the range `[1, 255]`.
     * @return True in case of success, false otherwise.
     */
    bool ttl(int val) {
        return (0 == uv_udp_set_ttl(get(), val > 255 ? 255 : val));
    }

    /**
     * @brief Sends data over the UDP socket.
     *
     * Note that if the socket has not previously been bound with `bind()`, it
     * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
     * random port number.
     *
     * The handle takes the ownership of the data and it is in charge of delete
     * them.
     *
     * A SendEvent event will be emitted when the data have been sent.

     * An ErrorEvent event will be emitted in case of errors.
     *
     * @param ip The address to which to send data.
     * @param port The port to which to send data.
     * @param data The data to be sent.
     * @param len The lenght of the submitted data.
     */
    template<typename I = IPv4>
    void send(std::string ip, unsigned int port, std::unique_ptr<char[]> data, std::size_t len) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);

        auto send = loop().resource(
                    std::unique_ptr<uv_buf_t[], details::SendReq::Deleter>{
                        new uv_buf_t[1]{ uv_buf_init(data.release(), len) },
                        [](uv_buf_t *bufs) { delete[] bufs->base; delete[] bufs; }
                    }, 1);

        auto listener = [ptr = shared_from_this()](const auto &event, details::SendReq &) {
            ptr->publish(event);
        };

        send->once<ErrorEvent>(listener);
        send->once<SendEvent>(listener);
        send->send(get(), reinterpret_cast<const sockaddr *>(&addr));
    }

    /**
     * @brief Sends data over the UDP socket.
     *
     * Note that if the socket has not previously been bound with `bind()`, it
     * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
     * random port number.
     *
     * The handle doesn't take the ownership of the data. Be sure that their
     * lifetime overcome the one of the request.
     *
     * A SendEvent event will be emitted when the data have been sent.

     * An ErrorEvent event will be emitted in case of errors.
     *
     * @param ip The address to which to send data.
     * @param port The port to which to send data.
     * @param data The data to be sent.
     * @param len The lenght of the submitted data.
     */
    template<typename I = IPv4>
    void send(std::string ip, unsigned int port, char *data, std::size_t len) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);

        auto send = loop().resource(
                    std::unique_ptr<uv_buf_t[], details::SendReq::Deleter>{
                        new uv_buf_t[1]{ uv_buf_init(data, len) },
                        [](uv_buf_t *bufs) { delete[] bufs; }
                    }, 1);

        auto listener = [ptr = shared_from_this()](const auto &event, details::SendReq &) {
            ptr->publish(event);
        };

        send->once<ErrorEvent>(listener);
        send->once<SendEvent>(listener);
        send->send(get(), reinterpret_cast<const sockaddr *>(&addr));
    }

    /**
     * @brief Sends data over the UDP socket.
     *
     * Same as `send()`, but it won’t queue a send request if it can’t be
     * completed immediately.
     *
     * @param ip The address to which to send data.
     * @param port The port to which to send data.
     * @param data The data to be sent.
     * @param len The lenght of the submitted data.
     * @return Number of bytes written.
     */
    template<typename I = IPv4>
    int trySend(std::string ip, unsigned int port, std::unique_ptr<char[]> data, std::size_t len) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);

        uv_buf_t bufs[] = { uv_buf_init(data.get(), len) };
        auto bw = uv_udp_try_send(get(), bufs, 1, reinterpret_cast<const sockaddr *>(&addr));

        if(bw < 0) {
            publish(ErrorEvent{bw});
            bw = 0;
        }

        return bw;
    }

    /**
     * @brief Prepares for receiving data.
     *
     * Note that if the socket has not previously been bound with `bind()`, it
     * is bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a random
     * port number.
     *
     * An UDPDataEvent event will be emitted when the handle receives data.

     * An ErrorEvent event will be emitted in case of errors.
     */
    template<typename I = IPv4>
    void recv() {
        invoke(&uv_udp_recv_start, get(), &allocCallback, &recvCallback);
    }

    /**
     * @brief Stops listening for incoming datagrams.
     */
    void stop() {
        invoke(&uv_udp_recv_stop, get());
    }

private:
    enum { DEFAULT, FLAGS } tag{DEFAULT};
    unsigned int flags{};
};

}
<commit_msg>Fix build failure when using UDPHandle::recv()<commit_after>#pragma once

#include <type_traits>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstddef>
#include <memory>
#include <string>
#include <uv.h>
#include "event.hpp"
#include "request.hpp"
#include "handle.hpp"
#include "util.hpp"

namespace uvw {

/**
 * @brief SendEvent event.
 *
 * It will be emitted by UDPHandle according with its functionalities.
 */
struct SendEvent: Event<SendEvent> {};

/**
 * @brief UDPDataEvent event.
 *
 * It will be emitted by UDPHandle according with its functionalities.
 */
struct UDPDataEvent: Event<UDPDataEvent> {
    explicit UDPDataEvent(Addr sender, std::unique_ptr<const char[]> data, std::size_t length, bool partial) noexcept
        : data{std::move(data)}, length{length}, sender{std::move(sender)}, partial{partial}
    {}

    std::unique_ptr<const char[]> data; /*!< A bunch of data read on the stream. */
    std::size_t length;  /*!< The amount of data read on the stream. */
    Addr sender; /*!< A valid instance of Addr. */
    bool partial; /*!< True if the message was truncated, false otherwise. */
};

namespace details {

enum class UVUdpFlags: std::underlying_type_t<uv_udp_flags> {
    IPV6ONLY = UV_UDP_IPV6ONLY,
    REUSEADDR = UV_UDP_REUSEADDR
};

enum class UVMembership: std::underlying_type_t<uv_membership> {
    LEAVE_GROUP = UV_LEAVE_GROUP,
    JOIN_GROUP = UV_JOIN_GROUP
};

class SendReq final: public Request<SendReq, uv_udp_send_t> {
public:
    using Deleter = void(*)(uv_buf_t *);

    SendReq(ConstructorAccess ca, std::shared_ptr<Loop> loop, std::unique_ptr<uv_buf_t[], Deleter> bufs, std::size_t nbufs)
        : Request<SendReq, uv_udp_send_t>{std::move(ca), std::move(loop)},
          bufs{std::move(bufs)},
          nbufs{nbufs}
    {}

    void send(uv_udp_t *handle, const struct sockaddr* addr) {
        invoke(&uv_udp_send, get(), handle, bufs.get(), nbufs, addr, &defaultCallback<SendEvent>);
    }

private:
    std::unique_ptr<uv_buf_t[], Deleter> bufs;
    std::size_t nbufs;
};

}

/**
 * @brief The UDPHandle handle.
 *
 * UDP handles encapsulate UDP communication for both clients and servers.

 * By default, _IPv4_ is used as a template parameter. The handle already
 * supports _IPv6_ out-of-the-box by using `uvw::IPv6`.
 *
 * To create an `UDPHandle` through a `Loop`, arguments follow:
 *
 * * An optional integer value that indicates optional flags used to initialize
 * the socket.
 *
 * See the official
 * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_init_ex)
 * for further details.
 */
class UDPHandle final: public Handle<UDPHandle, uv_udp_t> {
    template<typename I>
    static void recvCallback(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const sockaddr *addr, unsigned flags) {
        const typename details::IpTraits::Type *aptr = reinterpret_cast<const typename details::IpTraits::Type *>(addr);

        UDPHandle &udp = *(static_cast<UDPHandle*>(handle->data));
        // data will be destroyed no matter of what the value of nread is
        std::unique_ptr<const char[]> data{buf->base};

        if(nread > 0) {
            // data available (can be truncated)
            udp.publish(UDPDataEvent{details::address(aptr), std::move(data), nread, flags & UV_UDP_PARTIAL});
        } else if(nread == 0 && addr == nullptr) {
            // no more data to be read, doing nothing is fine
        } else if(nread == 0 && addr != nullptr) {
            // empty udp packet
            udp.publish(UDPDataEvent{details::address(aptr), std::move(data), nread, false});
        } else {
            // transmission error
            udp.publish(ErrorEvent(nread));
        }
    }

public:
    using Membership = details::UVMembership;
    using Bind = details::UVUdpFlags;
    using IPv4 = uvw::IPv4;
    using IPv6 = uvw::IPv6;

    using Handle::Handle;

    explicit UDPHandle(ConstructorAccess ca, std::shared_ptr<Loop> ref, unsigned int f)
        : Handle{std::move(ca), std::move(ref)}, tag{FLAGS}, flags{f}
    {}

    /**
     * @brief Initializes the handle. The actual socket is created lazily.
     * @return True in case of success, false otherwise.
     */
    bool init() {
        return (tag == FLAGS)
                ? initialize(&uv_udp_init_ex, flags)
                : initialize(&uv_udp_init);
    }

    /**
     * @brief Opens an existing file descriptor or SOCKET as a UDP handle.
     *
     * The passed file descriptor or SOCKET is not checked for its type, but
     * it’s required that it represents a valid datagram socket.
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_open)
     * for further details.
     *
     * @param sock A valid socket handle (either a file descriptor or a SOCKET).
     */
    void open(OSSocketHandle sock) {
        invoke(&uv_udp_open, get(), sock);
    }

    /**
     * @brief Binds the UDP handle to an IP address and port.
     *
     * Available flags are:
     *
     * * `UDPHandle::Bind::IPV6ONLY`
     * * `UDPHandle::Bind::REUSEADDR`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags)
     * for further details.
     *
     * @param ip The IP address to which to bind.
     * @param port The port to which to bind.
     * @param flags Optional additional flags.
     */
    template<typename I = IPv4>
    void bind(std::string ip, unsigned int port, Flags<Bind> flags = Flags<Bind>{}) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);
        invoke(&uv_udp_bind, get(), reinterpret_cast<const sockaddr *>(&addr), flags);
    }

    /**
     * @brief Binds the UDP handle to an IP address and port.
     *
     * Available flags are:
     *
     * * `UDPHandle::Bind::IPV6ONLY`
     * * `UDPHandle::Bind::REUSEADDR`
     *
     * See the official
     * [documentation](http://docs.libuv.org/en/v1.x/udp.html#c.uv_udp_flags)
     * for further details.
     *
     * @param addr A valid instance of Addr.
     * @param flags Optional additional flags.
     */
    template<typename I = IPv4>
    void bind(Addr addr, Flags<Bind> flags = Flags<Bind>{}) {
        bind(addr.ip, addr.port, flags);
    }

    /**
     * @brief Get the local IP and port of the UDP handle.
     * @return A valid instance of Addr, an empty one in case of errors.
     */
    template<typename I = IPv4>
    Addr sock() const noexcept {
        return details::address(&uv_udp_getsockname, get());
    }

    /**
     * @brief Sets membership for a multicast address.
     *
     * Available values for `membership` are:
     *
     * * `UDPHandle::Membership::LEAVE_GROUP`
     * * `UDPHandle::Membership::JOIN_GROUP`
     *
     * @param multicast Multicast address to set membership for.
     * @param iface Interface address.
     * @param membership Action to be performed.
     * @return True in case of success, false otherwise.
     */
    template<typename I = IPv4>
    bool multicastMembership(std::string multicast, std::string iface, Membership membership) {
        return (0 == uv_udp_set_membership(get(), multicast.data(), iface.data(), static_cast<uv_membership>(membership)));
    }

    /**
     * @brief Sets IP multicast loop flag.
     *
     * This makes multicast packets loop back to local sockets.
     *
     * @param enable True to enable multicast loop, false otherwise.
     * @return True in case of success, false otherwise.
     */
    bool multicastLoop(bool enable = true) {
        return (0 == uv_udp_set_multicast_loop(get(), enable));
    }

    /**
     * @brief Sets the multicast ttl.
     * @param val A value in the range `[1, 255]`.
     * @return True in case of success, false otherwise.
     */
    bool multicastTtl(int val) {
        return (0 == uv_udp_set_multicast_ttl(get(), val > 255 ? 255 : val));
    }

    /**
     * @brief Sets the multicast interface to send or receive data on.
     * @param iface Interface address.
     * @return True in case of success, false otherwise.
     */
    template<typename I = IPv4>
    bool multicastInterface(std::string iface) {
        return (0 == uv_udp_set_multicast_interface(get(), iface.data()));
    }

    /**
     * @brief Sets broadcast on or off.
     * @param enable True to set broadcast on, false otherwise.
     * @return True in case of success, false otherwise.
     */
    bool broadcast(bool enable = false) {
        return (0 == uv_udp_set_broadcast(get(), enable));
    }

    /**
     * @brief Sets the time to live.
     * @param val A value in the range `[1, 255]`.
     * @return True in case of success, false otherwise.
     */
    bool ttl(int val) {
        return (0 == uv_udp_set_ttl(get(), val > 255 ? 255 : val));
    }

    /**
     * @brief Sends data over the UDP socket.
     *
     * Note that if the socket has not previously been bound with `bind()`, it
     * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
     * random port number.
     *
     * The handle takes the ownership of the data and it is in charge of delete
     * them.
     *
     * A SendEvent event will be emitted when the data have been sent.

     * An ErrorEvent event will be emitted in case of errors.
     *
     * @param ip The address to which to send data.
     * @param port The port to which to send data.
     * @param data The data to be sent.
     * @param len The lenght of the submitted data.
     */
    template<typename I = IPv4>
    void send(std::string ip, unsigned int port, std::unique_ptr<char[]> data, std::size_t len) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);

        auto send = loop().resource(
                    std::unique_ptr<uv_buf_t[], details::SendReq::Deleter>{
                        new uv_buf_t[1]{ uv_buf_init(data.release(), len) },
                        [](uv_buf_t *bufs) { delete[] bufs->base; delete[] bufs; }
                    }, 1);

        auto listener = [ptr = shared_from_this()](const auto &event, details::SendReq &) {
            ptr->publish(event);
        };

        send->once<ErrorEvent>(listener);
        send->once<SendEvent>(listener);
        send->send(get(), reinterpret_cast<const sockaddr *>(&addr));
    }

    /**
     * @brief Sends data over the UDP socket.
     *
     * Note that if the socket has not previously been bound with `bind()`, it
     * will be bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a
     * random port number.
     *
     * The handle doesn't take the ownership of the data. Be sure that their
     * lifetime overcome the one of the request.
     *
     * A SendEvent event will be emitted when the data have been sent.

     * An ErrorEvent event will be emitted in case of errors.
     *
     * @param ip The address to which to send data.
     * @param port The port to which to send data.
     * @param data The data to be sent.
     * @param len The lenght of the submitted data.
     */
    template<typename I = IPv4>
    void send(std::string ip, unsigned int port, char *data, std::size_t len) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);

        auto send = loop().resource(
                    std::unique_ptr<uv_buf_t[], details::SendReq::Deleter>{
                        new uv_buf_t[1]{ uv_buf_init(data, len) },
                        [](uv_buf_t *bufs) { delete[] bufs; }
                    }, 1);

        auto listener = [ptr = shared_from_this()](const auto &event, details::SendReq &) {
            ptr->publish(event);
        };

        send->once<ErrorEvent>(listener);
        send->once<SendEvent>(listener);
        send->send(get(), reinterpret_cast<const sockaddr *>(&addr));
    }

    /**
     * @brief Sends data over the UDP socket.
     *
     * Same as `send()`, but it won’t queue a send request if it can’t be
     * completed immediately.
     *
     * @param ip The address to which to send data.
     * @param port The port to which to send data.
     * @param data The data to be sent.
     * @param len The lenght of the submitted data.
     * @return Number of bytes written.
     */
    template<typename I = IPv4>
    int trySend(std::string ip, unsigned int port, std::unique_ptr<char[]> data, std::size_t len) {
        typename details::IpTraits::Type addr;
        details::IpTraits::addrFunc(ip.data(), port, &addr);

        uv_buf_t bufs[] = { uv_buf_init(data.get(), len) };
        auto bw = uv_udp_try_send(get(), bufs, 1, reinterpret_cast<const sockaddr *>(&addr));

        if(bw < 0) {
            publish(ErrorEvent{bw});
            bw = 0;
        }

        return bw;
    }

    /**
     * @brief Prepares for receiving data.
     *
     * Note that if the socket has not previously been bound with `bind()`, it
     * is bound to `0.0.0.0` (the _all interfaces_ IPv4 address) and a random
     * port number.
     *
     * An UDPDataEvent event will be emitted when the handle receives data.

     * An ErrorEvent event will be emitted in case of errors.
     */
    template<typename I = IPv4>
    void recv() {
        invoke(&uv_udp_recv_start, get(), &allocCallback, &recvCallback);
    }

    /**
     * @brief Stops listening for incoming datagrams.
     */
    void stop() {
        invoke(&uv_udp_recv_stop, get());
    }

private:
    enum { DEFAULT, FLAGS } tag{DEFAULT};
    unsigned int flags{};
};

}
<|endoftext|>


<commit_before>
#define OPCODER_H

namespace CS {
class Opcode {
    public:

    private:
};
}
<commit_msg>designing stack model<commit_after>
#define OPCODE_H

namespace CS {

class Instruction {
    public:

    private:
};

class StackModel {
    public:
        StackModel() {}

        StackModel(const vector<Instruction>& instructions) {

        }

        OpCode Dump() const {

        }

        void Push(int i) {

        }

        void Pop() {

        }

        void Top() {

        }

        void Mov(int dst, int src) {
        }

        void Assign(int dst, int val) {

        }

        void Call() {

        }

        /* numeric operation */
        void Add() {

        }

        void Sub() {

        }

        void Mul() {

        }

        void Div() {

        }

        void Mod() {

        }

    private:
        vector<Instruction>
};
}
<|endoftext|>


<commit_before>#include "version.hpp"

// Get the git version macro from the build system
#include "vg_git_version.hpp"

// Do the same for the build environment info
#include "vg_environment_version.hpp"

#include <iostream>
#include <sstream>

namespace vg {

using namespace std;

// Define all the strings as the macros' values
const string Version::VERSION = VG_GIT_VERSION;
const string Version::COMPILER = VG_COMPILER_VERSION;
const string Version::OS = VG_OS;
const string Version::BUILD_USER = VG_BUILD_USER;
const string Version::BUILD_HOST = VG_BUILD_HOST;

// Keep the list of codenames.
// Add new codenames here
const unordered_map<string, string> Version::codenames = {
    {"v1.8.0", "Vallata"},
    {"v1.9.0", "Miglionico"},
    {"v1.10.0", "Rionero"}
    // Add more codenames here
};

string Version::get_version() {
    return VERSION;
}

string Version::get_release() {
    auto dash = VERSION.find('-');
    if (dash == -1) {
        // Pure tag versions have no dash
        return VERSION;
    } else {
        // Otherwise it is tag-count-ghash and the tag describes the release
        return VERSION.substr(0, dash);
    }
}

string Version::get_codename() {
    auto release = get_release();

    auto found = codenames.find(release);

    if (found == codenames.end()) {
        // No known codename for this release.
        // Return an empty string so we can just not show it.
        return "";
    } else {
        // We have a known codename!
        return found->second;
    }
}

string Version::get_short() {
    stringstream s;
    s << VERSION;

    auto codename = get_codename();
    if (!codename.empty()) {
        // Add the codename if we have one
        s << " \"" << codename << "\"";
    }

    return s.str();
}

string Version::get_long() {
    stringstream s;
    s << "vg version " << get_short() << endl;
    s << "Compiled with " << COMPILER << " on " << OS << endl;
    s << "Built by " << BUILD_USER << "@" << BUILD_HOST;
    return s.str();
}

}
<commit_msg>Add new codename<commit_after>#include "version.hpp"

// Get the git version macro from the build system
#include "vg_git_version.hpp"

// Do the same for the build environment info
#include "vg_environment_version.hpp"

#include <iostream>
#include <sstream>

namespace vg {

using namespace std;

// Define all the strings as the macros' values
const string Version::VERSION = VG_GIT_VERSION;
const string Version::COMPILER = VG_COMPILER_VERSION;
const string Version::OS = VG_OS;
const string Version::BUILD_USER = VG_BUILD_USER;
const string Version::BUILD_HOST = VG_BUILD_HOST;

// Keep the list of codenames.
// Add new codenames here
const unordered_map<string, string> Version::codenames = {
    {"v1.8.0", "Vallata"},
    {"v1.9.0", "Miglionico"},
    {"v1.10.0", "Rionero"},
    {"v1.11.0", "Cairano"}
    // Add more codenames here
};

string Version::get_version() {
    return VERSION;
}

string Version::get_release() {
    auto dash = VERSION.find('-');
    if (dash == -1) {
        // Pure tag versions have no dash
        return VERSION;
    } else {
        // Otherwise it is tag-count-ghash and the tag describes the release
        return VERSION.substr(0, dash);
    }
}

string Version::get_codename() {
    auto release = get_release();

    auto found = codenames.find(release);

    if (found == codenames.end()) {
        // No known codename for this release.
        // Return an empty string so we can just not show it.
        return "";
    } else {
        // We have a known codename!
        return found->second;
    }
}

string Version::get_short() {
    stringstream s;
    s << VERSION;

    auto codename = get_codename();
    if (!codename.empty()) {
        // Add the codename if we have one
        s << " \"" << codename << "\"";
    }

    return s.str();
}

string Version::get_long() {
    stringstream s;
    s << "vg version " << get_short() << endl;
    s << "Compiled with " << COMPILER << " on " << OS << endl;
    s << "Built by " << BUILD_USER << "@" << BUILD_HOST;
    return s.str();
}

}
<|endoftext|>


<commit_before>#ifndef LYZA_JSON_PARSER__
# define LYZA_JSON_PARSER__

# include <istream>
# include <sstream>
# include <stdexcept>
# include <cmath>

# include "value.hpp"

namespace lyza { namespace json { namespace parser {

namespace lj = lyza::json;

void error(lj::producer& p, std::string const& msg)
{
	std::string until_eof;

	while (!p.eof()) {
		until_eof += p.nextc();
	}

	throw std::runtime_error(msg + "(" + until_eof + ")");
}

bool has(lj::producer& p, char c)
{
	if (p.peekc() == c) {
		return true;
	}
	return false;
}

bool may_have(lj::producer& p, char c)
{
	if (p.peekc() == c) {
		p.nextc();
		return true;
	}
	return false;
}

void expects(lj::producer& p, char c)
{
	char cc = p.nextc();
	if (cc != c) {
		throw std::runtime_error(
				"parse error: '"
				+ std::string(c, 1)
				+ "' expected, got: '"
				+ std::string(cc, 1)
				+ "'");
	}
}

void match_string(lj::producer&p, std::string const& s)
{
	for (auto c : s) {
		expects(p, c);
	}
}

lj::string parse_string(lj::producer& p);
lj::array parse_array(lj::producer& p);
lj::boolean parse_bool(lj::producer& p);
lj::null parse_null(lj::producer& p);
lj::object parse_object(lj::producer& p);
lj::value parse_value(lj::producer& p);

lj::string parse_string(lj::producer& p)
{
	std::cout << "parse_string" << std::endl;
	std::string acc;

	expects(p, '"');
	p.skip_ws(false);
	while (!has(p, '"')) {
		if (may_have(p, '\\')) {
			acc += p.nextc();
		} else {
			acc += p.nextc();
		}
	}
	expects(p, '"');
	p.skip_ws(true);

	return acc;
}

lj::boolean parse_bool(lj::producer& p)
{
	lj::boolean b;

	if (has(p, 't')) {
		match_string(p, "true");
		b = true;
	} else if (has(p, 'f')) {
		match_string(p, "false");
		b = false;
	} else {
		error(p, "expected boolean");
	}
	return b;
}

lj::null parse_null(lj::producer& p)
{
	if (has(p, 'n')) {
		match_string(p, "null");
	} else {
		error(p, "expected null");
	}
	return lj::null();
}

lj::array parse_array(lj::producer& p)
{
	lj::array a;

	expects(p, '[');
	if (has(p, ']')) { // empty
		p.nextc();
		return a;
	}

	bool more = true;
	while (more) {
		a.push_back(parse_value(p));
		if (has(p, ','))  {
			p.nextc();
			more = true;
		} else
			more = false;
	}
	expects(p, ']');

	return a;
}

lj::object parse_object(lj::producer& p)
{
	lj::object o;

	expects(p, '{');
	if (has(p, '}')) { // empty
		p.nextc();
		return o;
	}

	bool more = true;
	while (more) {
		if (has(p, '"')) {
			lj::string key = parse_string(p);
			expects(p, ':');
			o[key] = parse_value(p);
			if (has(p, ','))  {
				p.nextc();
				more = true;
			} else
				more = false;
		} else {
			error(p, "expected key");
		}
	}
	expects(p, '}');
	return o;
}

lj::number parse_number(lj::producer& p)
{
	bool neg = false;

	if (has(p, '-')) {
		p.nextc();
		neg = true;
	}

	std::string num;
	if (has(p, '0')) {
		num += p.nextc();
	} else {
		if ('0' < p.peekc() && p.peekc() <= '9') {
			num += p.nextc();
		} else {
			error(p, "expected number");
		}
		while (std::isdigit(p.peekc())) {
			num += p.nextc();
		}
	}

	if (has(p, '.')) {
		num += p.nextc();
	}

	if (std::isdigit(p.peekc())) {
		num += p.nextc();
	} else {
		error(p, "expected number");
	}

	while (std::isdigit(p.peekc())) {
		num += p.nextc();
	}

	double dnum = std::stod(num);

	if (has(p, 'e') || has(p, 'E')) {
		p.nextc();

		char op = '+';
		if (has(p, '-') || has(p, '+')) {
			op = p.nextc();
		}

		std::string exp;
		while (std::isdigit(p.peekc())) {
			exp += p.nextc();
		}

		double dexp = std::pow(10, (op == '-' ? -1. : 1.) * std::stod(exp));

		dnum = dnum * dexp;
	}

	return dnum;
}

lj::value parse_value(lj::producer& p)
{
	std::cout << "parse_value" << std::endl;
	lj::value val;

	if (has(p, '"')) {
		val = parse_string(p);
	} else if (has(p, '[')) {
		val = parse_array(p);
	} else if (has(p, '{')) {
		val = parse_object(p);
	} else if (has(p, 't') || has(p, 'f')) {
		val = parse_bool(p);
	} else if (has(p, 'n')) {
		val = parse_null(p);
	} else if (has(p, '-') || std::isdigit(p.peekc())) {
		val = parse_number(p);
	} else {
		error(p, "expected value");
	}
	return val;
}

lj::object parse(lj::producer& p)
{
	p.skip_ws(true);
	return parse_object(p);
}

}}}

#endif
<commit_msg>Silent unitialized variable warning<commit_after>#ifndef LYZA_JSON_PARSER__
# define LYZA_JSON_PARSER__

# include <istream>
# include <sstream>
# include <stdexcept>
# include <cmath>

# include "value.hpp"

namespace lyza { namespace json { namespace parser {

namespace lj = lyza::json;

void error(lj::producer& p, std::string const& msg)
{
	std::string until_eof;

	while (!p.eof()) {
		until_eof += p.nextc();
	}

	throw std::runtime_error(msg + "(" + until_eof + ")");
}

bool has(lj::producer& p, char c)
{
	if (p.peekc() == c) {
		return true;
	}
	return false;
}

bool may_have(lj::producer& p, char c)
{
	if (p.peekc() == c) {
		p.nextc();
		return true;
	}
	return false;
}

void expects(lj::producer& p, char c)
{
	char cc = p.nextc();
	if (cc != c) {
		throw std::runtime_error(
				"parse error: '"
				+ std::string(c, 1)
				+ "' expected, got: '"
				+ std::string(cc, 1)
				+ "'");
	}
}

void match_string(lj::producer&p, std::string const& s)
{
	for (auto c : s) {
		expects(p, c);
	}
}

lj::string parse_string(lj::producer& p);
lj::array parse_array(lj::producer& p);
lj::boolean parse_bool(lj::producer& p);
lj::null parse_null(lj::producer& p);
lj::object parse_object(lj::producer& p);
lj::value parse_value(lj::producer& p);

lj::string parse_string(lj::producer& p)
{
	std::cout << "parse_string" << std::endl;
	std::string acc;

	expects(p, '"');
	p.skip_ws(false);
	while (!has(p, '"')) {
		if (may_have(p, '\\')) {
			acc += p.nextc();
		} else {
			acc += p.nextc();
		}
	}
	expects(p, '"');
	p.skip_ws(true);

	return acc;
}

lj::boolean parse_bool(lj::producer& p)
{
	lj::boolean b = false;

	if (has(p, 't')) {
		match_string(p, "true");
		b = true;
	} else if (has(p, 'f')) {
		match_string(p, "false");
		b = false;
	} else {
		error(p, "expected boolean");
	}
	return b;
}

lj::null parse_null(lj::producer& p)
{
	if (has(p, 'n')) {
		match_string(p, "null");
	} else {
		error(p, "expected null");
	}
	return lj::null();
}

lj::array parse_array(lj::producer& p)
{
	lj::array a;

	expects(p, '[');
	if (has(p, ']')) { // empty
		p.nextc();
		return a;
	}

	bool more = true;
	while (more) {
		a.push_back(parse_value(p));
		if (has(p, ','))  {
			p.nextc();
			more = true;
		} else
			more = false;
	}
	expects(p, ']');

	return a;
}

lj::object parse_object(lj::producer& p)
{
	lj::object o;

	expects(p, '{');
	if (has(p, '}')) { // empty
		p.nextc();
		return o;
	}

	bool more = true;
	while (more) {
		if (has(p, '"')) {
			lj::string key = parse_string(p);
			expects(p, ':');
			o[key] = parse_value(p);
			if (has(p, ','))  {
				p.nextc();
				more = true;
			} else
				more = false;
		} else {
			error(p, "expected key");
		}
	}
	expects(p, '}');
	return o;
}

lj::number parse_number(lj::producer& p)
{
	bool neg = false;

	if (has(p, '-')) {
		p.nextc();
		neg = true;
	}

	std::string num;
	if (has(p, '0')) {
		num += p.nextc();
	} else {
		if ('0' < p.peekc() && p.peekc() <= '9') {
			num += p.nextc();
		} else {
			error(p, "expected number");
		}
		while (std::isdigit(p.peekc())) {
			num += p.nextc();
		}
	}

	if (has(p, '.')) {
		num += p.nextc();
	}

	if (std::isdigit(p.peekc())) {
		num += p.nextc();
	} else {
		error(p, "expected number");
	}

	while (std::isdigit(p.peekc())) {
		num += p.nextc();
	}

	double dnum = std::stod(num);

	if (has(p, 'e') || has(p, 'E')) {
		p.nextc();

		char op = '+';
		if (has(p, '-') || has(p, '+')) {
			op = p.nextc();
		}

		std::string exp;
		while (std::isdigit(p.peekc())) {
			exp += p.nextc();
		}

		double dexp = std::pow(10, (op == '-' ? -1. : 1.) * std::stod(exp));

		dnum = dnum * dexp;
	}

	return dnum;
}

lj::value parse_value(lj::producer& p)
{
	std::cout << "parse_value" << std::endl;
	lj::value val;

	if (has(p, '"')) {
		val = parse_string(p);
	} else if (has(p, '[')) {
		val = parse_array(p);
	} else if (has(p, '{')) {
		val = parse_object(p);
	} else if (has(p, 't') || has(p, 'f')) {
		val = parse_bool(p);
	} else if (has(p, 'n')) {
		val = parse_null(p);
	} else if (has(p, '-') || std::isdigit(p.peekc())) {
		val = parse_number(p);
	} else {
		error(p, "expected value");
	}
	return val;
}

lj::object parse(lj::producer& p)
{
	p.skip_ws(true);
	return parse_object(p);
}

}}}

#endif
<|endoftext|>


<commit_before>#include "pbview.h"

#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <curses.h>
#include <iostream>
#include <sstream>

#include "config.h"
#include "configcontainer.h"
#include "dllist.h"
#include "download.h"
#include "fmtstrformatter.h"
#include "help.h"
#include "listformatter.h"
#include "logger.h"
#include "pbcontroller.h"
#include "poddlthread.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

PbView::PbView(PbController* c)
	: ctrl(c)
	, dllist_form(dllist_str)
	, help_form(help_str)
	, keys(0)
	, colorman(ctrl->get_colormanager())
	, downloads_list("dls", dllist_form,
		  ctrl->get_cfgcont()->get_configvalue_as_int("scrolloff"))
	, help_textview("helptext", help_form)
{
	if (getenv("ESCDELAY") == nullptr) {
		set_escdelay(25);
	}
}

PbView::~PbView()
{
	Stfl::reset();
}

void PbView::run(bool auto_download, bool wrap_scroll)
{
	bool quit = false;

	// Make sure curses is initialized
	dllist_form.run(-3);
	// Hide cursor using curses
	curs_set(0);

	set_dllist_keymap_hint();

	do {
		if (ctrl->view_update_necessary()) {
			const double total_kbps = ctrl->get_total_kbps();
			const auto speed = get_speed_human_readable(total_kbps);

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf,
					sizeof(parbuf),
					_(" - %u parallel downloads"),
					ctrl->get_maxdownloads());
			}

			char buf[1024];
			snprintf(buf,
				sizeof(buf),
				_("Queue (%u downloads in progress, %u total) "
					"- %.2f %s total%s"),
				static_cast<unsigned int>(
					ctrl->downloads_in_progress()),
				static_cast<unsigned int>(
					ctrl->downloads().size()),
				speed.first,
				speed.second.c_str(),
				parbuf);

			dllist_form.set("head", buf);

			LOG(Level::DEBUG,
				"PbView::run: updating view... "
				"downloads().size() "
				"= %" PRIu64,
				static_cast<uint64_t>(ctrl->downloads().size()));

			ListFormatter listfmt;
			std::string formatstring = ctrl->get_formatstr();

			dllist_form.run(-3); // compute all widget dimensions
			const unsigned int width = downloads_list.get_width();

			unsigned int i = 0;
			for (const auto& dl : ctrl->downloads()) {
				auto lbuf = format_line(formatstring, dl, i, width);
				listfmt.add_line(lbuf);
				i++;
			}

			if (i >= 1 && dllist_form.get("msg").length() == 0) {
				const auto idx = downloads_list.get_position();
				dllist_form.set("msg", ctrl->downloads()[idx].status_msg());
			}

			downloads_list.stfl_replace_lines(listfmt);

			ctrl->set_view_update_necessary(false);
		}

		const char* event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() >
				ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event, "TIMEOUT") == 0) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "podboat");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			ctrl->set_view_update_necessary(true);
		}

		switch (op) {
		case OP_REDRAW:
			Stfl::reset();
			break;
		case OP_PREV:
		case OP_SK_UP:
			downloads_list.move_up(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_NEXT:
		case OP_SK_DOWN:
			downloads_list.move_down(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_HOME:
			downloads_list.move_to_first();
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_END:
			downloads_list.move_to_last();
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_PGUP:
			downloads_list.move_page_up(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_PGDOWN:
			downloads_list.move_page_down(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_PB_TOGGLE_DLALL:
			auto_download = !auto_download;
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: can't quit: download(s) in "
						"progress."));
				ctrl->set_view_update_necessary(true);
			} else {
				quit = true;
			}
			break;
		case OP_PB_MOREDL:
			ctrl->increase_parallel_downloads();
			break;
		case OP_PB_LESSDL:
			ctrl->decrease_parallel_downloads();
			break;
		case OP_PB_DOWNLOAD: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					std::thread t{PodDlThread(
							&ctrl->downloads()[idx],
							ctrl->get_cfgcont())};
					t.detach();
				}
			}
		}
		break;
		case OP_PB_PLAY: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::FINISHED ||
					status == DlStatus::PLAYED ||
					status == DlStatus::READY) {
					ctrl->play_file(ctrl->downloads()[idx]
						.filename());
					ctrl->downloads()[idx].set_status(
						DlStatus::PLAYED);
				} else {
					dllist_form.set("msg",
						_("Error: download needs to be "
							"finished before the file "
							"can be played."));
				}
			}
		}
		break;
		case OP_PB_MARK_FINISHED: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::PLAYED) {
					ctrl->downloads()[idx].set_status(
						DlStatus::FINISHED);
				}
			}
		}
		break;
		case OP_PB_CANCEL: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() ==
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::CANCELLED);
				}
			}
		}
		break;
		case OP_PB_DELETE: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::DELETED);
				}
			}
		}
		break;
		case OP_PB_PURGE:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: unable to perform operation: "
						"download(s) in progress."));
			} else {
				ctrl->purge_queue();
			}
			ctrl->set_view_update_necessary(true);
			break;
		case OP_HELP:
			run_help();
			break;
		default:
			break;
		}

	} while (!quit);
}

void PbView::handle_resize()
{
	std::vector<std::reference_wrapper<newsboat::Stfl::Form>> forms = {dllist_form, help_form};
	for (const auto form : forms) {
		form.get().run(-3);
	}
	ctrl->set_view_update_necessary(true);
}

void PbView::apply_colors_to_all_forms()
{
	colorman.apply_colors(dllist_form);
	colorman.apply_colors(help_form);
}

std::pair<double, std::string> PbView::get_speed_human_readable(double kbps)
{
	if (kbps < 1024) {
		return std::make_pair(kbps, _("KB/s"));
	} else if (kbps < 1024 * 1024) {
		return std::make_pair(kbps / 1024, _("MB/s"));
	} else {
		return std::make_pair(kbps / 1024 / 1024, _("GB/s"));
	}
}

void PbView::run_help()
{
	set_help_keymap_hint();

	help_form.set("head", _("Help"));

	const auto descs = keys->get_keymap_descriptions("podboat");

	ListFormatter listfmt;

	for (const auto& desc : descs) {
		std::string descline;
		descline.append(desc.key);
		descline.append(8 - desc.key.length(), ' ');
		descline.append(desc.cmd);
		descline.append(24 - desc.cmd.length(), ' ');
		descline.append(desc.desc);

		listfmt.add_line(descline);
	}

	help_textview.stfl_replace_lines(listfmt.get_lines_count(),
		listfmt.format_list());

	bool quit = false;

	do {
		const char* event = help_form.run(0);
		if (!event) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "help");

		switch (op) {
		case OP_SK_UP:
			help_textview.scroll_up();
			break;
		case OP_SK_DOWN:
			help_textview.scroll_down();
			break;
		case OP_SK_HOME:
			help_textview.scroll_to_top();
			break;
		case OP_SK_END:
			help_textview.scroll_to_bottom();
			break;
		case OP_SK_PGUP:
			help_textview.scroll_page_up();
			break;
		case OP_SK_PGDOWN:
			help_textview.scroll_page_down();
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			quit = true;
			break;
		default:
			break;
		}
	} while (!quit);
}

std::string PbView::prepare_keymaphint(KeyMapHintEntry* hints)
{
	std::string keymap_hint;
	for (int i = 0; hints[i].op != OP_NIL; ++i) {
		std::string bound_keys = utils::join(keys->get_keys(hints[i].op, "podboat"),
				",");
		if (bound_keys.empty()) {
			bound_keys = "<none>";
		}
		keymap_hint.append(bound_keys);
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void PbView::set_help_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")}, {OP_NIL, nullptr}};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void PbView::set_dllist_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_PB_DOWNLOAD, _("Download")},
		{OP_PB_CANCEL, _("Cancel")},
		{OP_PB_DELETE, _("Delete")},
		{OP_PB_PURGE, _("Purge Finished")},
		{OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download")},
		{OP_PB_PLAY, _("Play")},
		{OP_PB_MARK_FINISHED, _("Mark as Finished")},
		{OP_HELP, _("Help")},
		{OP_NIL, nullptr}
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

std::string PbView::format_line(const std::string& podlist_format,
	const Download& dl,
	unsigned int pos,
	unsigned int width)
{
	FmtStrFormatter fmt;

	const double speed_kbps = dl.kbps();
	const auto speed = get_speed_human_readable(speed_kbps);

	fmt.register_fmt('i', strprintf::fmt("%u", pos + 1));
	fmt.register_fmt('d',
		strprintf::fmt("%.1f", dl.current_size() / (1024 * 1024)));
	fmt.register_fmt(
		't', strprintf::fmt("%.1f", dl.total_size() / (1024 * 1024)));
	fmt.register_fmt('p', strprintf::fmt("%.1f", dl.percents_finished()));
	fmt.register_fmt('k', strprintf::fmt("%.2f", speed_kbps));
	fmt.register_fmt('K', strprintf::fmt("%.2f %s", speed.first, speed.second));
	fmt.register_fmt('S', strprintf::fmt("%s", dl.status_text()));
	fmt.register_fmt('u', strprintf::fmt("%s", dl.url()));
	fmt.register_fmt('F', strprintf::fmt("%s", dl.filename()));
	fmt.register_fmt('b', strprintf::fmt("%s", dl.basename()));

	auto formattedLine = fmt.do_format(podlist_format, width);
	formattedLine = utils::quote_for_stfl(formattedLine);
	return formattedLine;
}

} // namespace podboat
<commit_msg>Modified condition from .length() == 0 to .empty()<commit_after>#include "pbview.h"

#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <curses.h>
#include <iostream>
#include <sstream>

#include "config.h"
#include "configcontainer.h"
#include "dllist.h"
#include "download.h"
#include "fmtstrformatter.h"
#include "help.h"
#include "listformatter.h"
#include "logger.h"
#include "pbcontroller.h"
#include "poddlthread.h"
#include "strprintf.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

PbView::PbView(PbController* c)
	: ctrl(c)
	, dllist_form(dllist_str)
	, help_form(help_str)
	, keys(0)
	, colorman(ctrl->get_colormanager())
	, downloads_list("dls", dllist_form,
		  ctrl->get_cfgcont()->get_configvalue_as_int("scrolloff"))
	, help_textview("helptext", help_form)
{
	if (getenv("ESCDELAY") == nullptr) {
		set_escdelay(25);
	}
}

PbView::~PbView()
{
	Stfl::reset();
}

void PbView::run(bool auto_download, bool wrap_scroll)
{
	bool quit = false;

	// Make sure curses is initialized
	dllist_form.run(-3);
	// Hide cursor using curses
	curs_set(0);

	set_dllist_keymap_hint();

	do {
		if (ctrl->view_update_necessary()) {
			const double total_kbps = ctrl->get_total_kbps();
			const auto speed = get_speed_human_readable(total_kbps);

			char parbuf[128] = "";
			if (ctrl->get_maxdownloads() > 1) {
				snprintf(parbuf,
					sizeof(parbuf),
					_(" - %u parallel downloads"),
					ctrl->get_maxdownloads());
			}

			char buf[1024];
			snprintf(buf,
				sizeof(buf),
				_("Queue (%u downloads in progress, %u total) "
					"- %.2f %s total%s"),
				static_cast<unsigned int>(
					ctrl->downloads_in_progress()),
				static_cast<unsigned int>(
					ctrl->downloads().size()),
				speed.first,
				speed.second.c_str(),
				parbuf);

			dllist_form.set("head", buf);

			LOG(Level::DEBUG,
				"PbView::run: updating view... "
				"downloads().size() "
				"= %" PRIu64,
				static_cast<uint64_t>(ctrl->downloads().size()));

			ListFormatter listfmt;
			std::string formatstring = ctrl->get_formatstr();

			dllist_form.run(-3); // compute all widget dimensions
			const unsigned int width = downloads_list.get_width();

			unsigned int i = 0;
			for (const auto& dl : ctrl->downloads()) {
				auto lbuf = format_line(formatstring, dl, i, width);
				listfmt.add_line(lbuf);
				i++;
			}

			// If there's no status message, we know there's no error to show
			// Thus, it's safe to replace with the download's status
			if (i >= 1 && dllist_form.get("msg").empty()) {
				const auto idx = downloads_list.get_position();
				dllist_form.set("msg", ctrl->downloads()[idx].status_msg());
			}

			downloads_list.stfl_replace_lines(listfmt);

			ctrl->set_view_update_necessary(false);
		}

		const char* event = dllist_form.run(500);

		if (auto_download) {
			if (ctrl->get_maxdownloads() >
				ctrl->downloads_in_progress()) {
				ctrl->start_downloads();
			}
		}

		if (!event || strcmp(event, "TIMEOUT") == 0) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "podboat");

		if (dllist_form.get("msg").length() > 0) {
			dllist_form.set("msg", "");
			ctrl->set_view_update_necessary(true);
		}

		switch (op) {
		case OP_REDRAW:
			Stfl::reset();
			break;
		case OP_PREV:
		case OP_SK_UP:
			downloads_list.move_up(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_NEXT:
		case OP_SK_DOWN:
			downloads_list.move_down(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_HOME:
			downloads_list.move_to_first();
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_END:
			downloads_list.move_to_last();
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_PGUP:
			downloads_list.move_page_up(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_SK_PGDOWN:
			downloads_list.move_page_down(wrap_scroll);
			ctrl->set_view_update_necessary(true);
			break;
		case OP_PB_TOGGLE_DLALL:
			auto_download = !auto_download;
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: can't quit: download(s) in "
						"progress."));
				ctrl->set_view_update_necessary(true);
			} else {
				quit = true;
			}
			break;
		case OP_PB_MOREDL:
			ctrl->increase_parallel_downloads();
			break;
		case OP_PB_LESSDL:
			ctrl->decrease_parallel_downloads();
			break;
		case OP_PB_DOWNLOAD: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					std::thread t{PodDlThread(
							&ctrl->downloads()[idx],
							ctrl->get_cfgcont())};
					t.detach();
				}
			}
		}
		break;
		case OP_PB_PLAY: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::FINISHED ||
					status == DlStatus::PLAYED ||
					status == DlStatus::READY) {
					ctrl->play_file(ctrl->downloads()[idx]
						.filename());
					ctrl->downloads()[idx].set_status(
						DlStatus::PLAYED);
				} else {
					dllist_form.set("msg",
						_("Error: download needs to be "
							"finished before the file "
							"can be played."));
				}
			}
		}
		break;
		case OP_PB_MARK_FINISHED: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				DlStatus status =
					ctrl->downloads()[idx].status();
				if (status == DlStatus::PLAYED) {
					ctrl->downloads()[idx].set_status(
						DlStatus::FINISHED);
				}
			}
		}
		break;
		case OP_PB_CANCEL: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() ==
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::CANCELLED);
				}
			}
		}
		break;
		case OP_PB_DELETE: {
			if (ctrl->downloads().size() >= 1) {
				const auto idx = downloads_list.get_position();
				if (ctrl->downloads()[idx].status() !=
					DlStatus::DOWNLOADING) {
					ctrl->downloads()[idx].set_status(
						DlStatus::DELETED);
				}
			}
		}
		break;
		case OP_PB_PURGE:
			if (ctrl->downloads_in_progress() > 0) {
				dllist_form.set("msg",
					_("Error: unable to perform operation: "
						"download(s) in progress."));
			} else {
				ctrl->purge_queue();
			}
			ctrl->set_view_update_necessary(true);
			break;
		case OP_HELP:
			run_help();
			break;
		default:
			break;
		}

	} while (!quit);
}

void PbView::handle_resize()
{
	std::vector<std::reference_wrapper<newsboat::Stfl::Form>> forms = {dllist_form, help_form};
	for (const auto form : forms) {
		form.get().run(-3);
	}
	ctrl->set_view_update_necessary(true);
}

void PbView::apply_colors_to_all_forms()
{
	colorman.apply_colors(dllist_form);
	colorman.apply_colors(help_form);
}

std::pair<double, std::string> PbView::get_speed_human_readable(double kbps)
{
	if (kbps < 1024) {
		return std::make_pair(kbps, _("KB/s"));
	} else if (kbps < 1024 * 1024) {
		return std::make_pair(kbps / 1024, _("MB/s"));
	} else {
		return std::make_pair(kbps / 1024 / 1024, _("GB/s"));
	}
}

void PbView::run_help()
{
	set_help_keymap_hint();

	help_form.set("head", _("Help"));

	const auto descs = keys->get_keymap_descriptions("podboat");

	ListFormatter listfmt;

	for (const auto& desc : descs) {
		std::string descline;
		descline.append(desc.key);
		descline.append(8 - desc.key.length(), ' ');
		descline.append(desc.cmd);
		descline.append(24 - desc.cmd.length(), ' ');
		descline.append(desc.desc);

		listfmt.add_line(descline);
	}

	help_textview.stfl_replace_lines(listfmt.get_lines_count(),
		listfmt.format_list());

	bool quit = false;

	do {
		const char* event = help_form.run(0);
		if (!event) {
			continue;
		}

		if (strcmp(event, "RESIZE") == 0) {
			handle_resize();
			continue;
		}

		Operation op = keys->get_operation(event, "help");

		switch (op) {
		case OP_SK_UP:
			help_textview.scroll_up();
			break;
		case OP_SK_DOWN:
			help_textview.scroll_down();
			break;
		case OP_SK_HOME:
			help_textview.scroll_to_top();
			break;
		case OP_SK_END:
			help_textview.scroll_to_bottom();
			break;
		case OP_SK_PGUP:
			help_textview.scroll_page_up();
			break;
		case OP_SK_PGDOWN:
			help_textview.scroll_page_down();
			break;
		case OP_HARDQUIT:
		case OP_QUIT:
			quit = true;
			break;
		default:
			break;
		}
	} while (!quit);
}

std::string PbView::prepare_keymaphint(KeyMapHintEntry* hints)
{
	std::string keymap_hint;
	for (int i = 0; hints[i].op != OP_NIL; ++i) {
		std::string bound_keys = utils::join(keys->get_keys(hints[i].op, "podboat"),
				",");
		if (bound_keys.empty()) {
			bound_keys = "<none>";
		}
		keymap_hint.append(bound_keys);
		keymap_hint.append(":");
		keymap_hint.append(hints[i].text);
		keymap_hint.append(" ");
	}
	return keymap_hint;
}

void PbView::set_help_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")}, {OP_NIL, nullptr}};
	std::string keymap_hint = prepare_keymaphint(hints);
	help_form.set("help", keymap_hint);
}

void PbView::set_dllist_keymap_hint()
{
	KeyMapHintEntry hints[] = {{OP_QUIT, _("Quit")},
		{OP_PB_DOWNLOAD, _("Download")},
		{OP_PB_CANCEL, _("Cancel")},
		{OP_PB_DELETE, _("Delete")},
		{OP_PB_PURGE, _("Purge Finished")},
		{OP_PB_TOGGLE_DLALL, _("Toggle Automatic Download")},
		{OP_PB_PLAY, _("Play")},
		{OP_PB_MARK_FINISHED, _("Mark as Finished")},
		{OP_HELP, _("Help")},
		{OP_NIL, nullptr}
	};

	std::string keymap_hint = prepare_keymaphint(hints);
	dllist_form.set("help", keymap_hint);
}

std::string PbView::format_line(const std::string& podlist_format,
	const Download& dl,
	unsigned int pos,
	unsigned int width)
{
	FmtStrFormatter fmt;

	const double speed_kbps = dl.kbps();
	const auto speed = get_speed_human_readable(speed_kbps);

	fmt.register_fmt('i', strprintf::fmt("%u", pos + 1));
	fmt.register_fmt('d',
		strprintf::fmt("%.1f", dl.current_size() / (1024 * 1024)));
	fmt.register_fmt(
		't', strprintf::fmt("%.1f", dl.total_size() / (1024 * 1024)));
	fmt.register_fmt('p', strprintf::fmt("%.1f", dl.percents_finished()));
	fmt.register_fmt('k', strprintf::fmt("%.2f", speed_kbps));
	fmt.register_fmt('K', strprintf::fmt("%.2f %s", speed.first, speed.second));
	fmt.register_fmt('S', strprintf::fmt("%s", dl.status_text()));
	fmt.register_fmt('u', strprintf::fmt("%s", dl.url()));
	fmt.register_fmt('F', strprintf::fmt("%s", dl.filename()));
	fmt.register_fmt('b', strprintf::fmt("%s", dl.basename()));

	auto formattedLine = fmt.do_format(podlist_format, width);
	formattedLine = utils::quote_for_stfl(formattedLine);
	return formattedLine;
}

} // namespace podboat
<|endoftext|>