<commit_before>#include "libs/catch/catch.hpp"
#include "libs/exceptionpp/exception.h"

#include <iostream>

#include "src/file.h"

TEST_CASE("giga|config-probe") {
	giga::Config c = giga::Config(100, 200);
	std::shared_ptr<giga::Page> p (new giga::Page(1, "", 0, 100, false));
	REQUIRE(c.probe(p, 0, 1000) == 200);
	REQUIRE(c.probe(p, 0, 10) == 10);
	REQUIRE(c.probe(p, 10, 10) == 10);
	REQUIRE(c.probe(p, 10, 90) == 90);
	REQUIRE(c.probe(p, 10, 1000) == 190);
	REQUIRE(c.probe(p, 100, 1000) == 100);
	REQUIRE(c.probe(p, 100, 10) == 10);
	REQUIRE(c.probe(p, 100, 0) == 0);
}

TEST_CASE("giga|file") {
	REQUIRE_THROWS_AS(giga::File("tests/files/nonexistent", "r"), exceptionpp::InvalidOperation);

	giga::File f = giga::File("tests/files/foo", "r", giga::Config(3, 4));
	REQUIRE(f.get_filename().compare("tests/files/foo") == 0);
	REQUIRE(f.get_mode().compare("r") == 0);
}

TEST_CASE("giga|file-open") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r"));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(c->get_pos() == 0);
	c->close();
}

TEST_CASE("giga|file-seek") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 2)));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(f->get_size() == 13);
	REQUIRE(c->seek(2, true) == 2);
	REQUIRE(c->seek(1, false) == 1);
	REQUIRE(c->seek(1, false) == 0);
	REQUIRE(c->seek(9, true) == 9);
	REQUIRE(c->seek(3, false) == 6);
	REQUIRE(c->seek(100, true) == 13);
	REQUIRE(c->seek(100, false) == 0);
	REQUIRE(c->seek(100, true) == 13);
	c->close();
}

TEST_CASE("giga|file-read") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 2)));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(c->read(0).compare("") == 0);
	REQUIRE(c->read(1).compare("h") == 0);
	REQUIRE(c->read(2).compare("el") == 0);
	REQUIRE(c->read(100).compare("lo world!\n") == 0);
	c->close();
}

TEST_CASE("giga|file-erase") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 5)));
	std::shared_ptr<giga::Client> c_1 = f->open();
	std::shared_ptr<giga::Client> c_2 = f->open();

	REQUIRE(c_2->seek(1, true) == 1);

	REQUIRE(c_2->erase(1) == 1);
	REQUIRE(f->get_size() == 12);
	REQUIRE(c_1->get_pos() == 0);
	REQUIRE(c_2->get_pos() == 1);
	REQUIRE(c_1->read(100).compare("hllo world!\n") == 0);
	REQUIRE(c_2->read(100).compare("llo world!\n") == 0);

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_1->seek(1, true) == 1);
	REQUIRE(c_2->seek(2, true) == 2);
	REQUIRE(c_1->get_pos() == 1);
	REQUIRE(c_2->get_pos() == 2);

	REQUIRE(c_1->erase(1) == 1);
	REQUIRE(f->get_size() == 11);
	REQUIRE(c_1->get_pos() == 1);
	REQUIRE(c_2->get_pos() == 1);
	REQUIRE(c_1->read(100).compare("lo world!\n") == 0);
	REQUIRE(c_2->read(100).compare("lo world!\n") == 0);
	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_2->read(100).compare("hlo world!\n") == 0);
	REQUIRE(c_1->read(100).compare("hlo world!\n") == 0);

	c_1->close();
	c_2->close();

	f.reset();
	f = std::shared_ptr<giga::File> (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 5)));
	c_1 = f->open();
	c_2 = f->open();

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_2->seek(2, true) == 2);

	REQUIRE(c_1->erase(2) == 2);
	REQUIRE(c_1->get_pos() == 0);
	REQUIRE(c_2->get_pos() == 0);
	REQUIRE(c_1->read(100).compare("llo world!\n") == 0);
	REQUIRE(c_2->read(100).compare("llo world!\n") == 0);

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_2->seek(3, true) == 3);

	REQUIRE(c_1->erase(2) == 2);
	REQUIRE(c_1->get_pos() == 0);
	REQUIRE(c_2->get_pos() == 1);

	REQUIRE(c_1->read(100).compare("o world!\n") == 0);
	REQUIRE(c_2->read(100).compare(" world!\n") == 0);

	REQUIRE(c_1->erase(100) == 0);

	REQUIRE(c_1->seek(100, false) == 0);

	std::cout << "REQUIRE(c_1->erase(100))" << std::endl;
	REQUIRE(c_1->erase(100) == 8);
	REQUIRE(c_1->read(100).compare("") == 0);
	REQUIRE(c_2->read(100).compare("") == 0);

	// REQUIRE(f->get_size() == 0);

	// REQUIRE(c_1->get_pos() == 0);
	// REQUIRE(c_2->get_pos() == 0);

	c_1->close();
	c_2->close();
}

TEST_CASE("giga|file-insert") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 3)));
	std::shared_ptr<giga::Client> c_1 = f->open();
	std::shared_ptr<giga::Client> c_2 = f->open();

	REQUIRE(c_1->seek(1, true) == 1);
	REQUIRE(f->get_size() == 13);
	REQUIRE(c_2->write("foo", true) == 3);
	REQUIRE(f->get_size() == 16);
	REQUIRE(c_2->get_pos() == 3);
	REQUIRE(c_1->get_pos() == 4);
	REQUIRE(c_1->read(100).compare("ello world!\n") == 0);

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_1->seek(1, true) == 1);
	REQUIRE(c_1->write("foo", true) == 3);
	REQUIRE(f->get_size() == 19);
	REQUIRE(c_2->get_pos() == 0);
	REQUIRE(c_2->read(100).compare("ffoooohello world!\n") == 0);

	REQUIRE(c_2->write("addendum") == 8);
	REQUIRE(f->get_size() == 27);
	REQUIRE(c_1->read(100).compare("oohello world!\naddendum") == 0);

	c_1->close();
	c_2->close();
}

TEST_CASE("giga|file-write") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 3)));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(c->write("") == 0);
	REQUIRE(c->get_pos() == 0);
	REQUIRE(c->write("abcde") == 5);
	REQUIRE(c->get_pos() == 5);
	REQUIRE(c->write("|world!\nEXTRAEXTRA") == 18);
	REQUIRE(f->get_size() == 23);
	REQUIRE(c->get_pos() == 23);
	REQUIRE(c->seek(100, false) == 0);
	REQUIRE(c->read(100).compare("abcde|world!\nEXTRAEXTRA") == 0);
	c->close();
}
<commit_msg>added more tests<commit_after>#include "libs/catch/catch.hpp"
#include "libs/exceptionpp/exception.h"

#include <iostream>

#include "src/file.h"

TEST_CASE("giga|config-probe") {
	giga::Config c = giga::Config(100, 200);
	std::shared_ptr<giga::Page> p (new giga::Page(1, "", 0, 100, false));
	REQUIRE(c.probe(p, 0, 1000) == 200);
	REQUIRE(c.probe(p, 0, 10) == 10);
	REQUIRE(c.probe(p, 10, 10) == 10);
	REQUIRE(c.probe(p, 10, 90) == 90);
	REQUIRE(c.probe(p, 10, 1000) == 190);
	REQUIRE(c.probe(p, 100, 1000) == 100);
	REQUIRE(c.probe(p, 100, 10) == 10);
	REQUIRE(c.probe(p, 100, 0) == 0);
}

TEST_CASE("giga|file") {
	REQUIRE_THROWS_AS(giga::File("tests/files/nonexistent", "r"), exceptionpp::InvalidOperation);

	giga::File f = giga::File("tests/files/foo", "r", giga::Config(3, 4));
	REQUIRE(f.get_filename().compare("tests/files/foo") == 0);
	REQUIRE(f.get_mode().compare("r") == 0);
}

TEST_CASE("giga|file-open") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r"));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(c->get_pos() == 0);
	c->close();
}

TEST_CASE("giga|file-seek") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 2)));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(f->get_size() == 13);
	REQUIRE(c->seek(2, true) == 2);
	REQUIRE(c->seek(1, false) == 1);
	REQUIRE(c->seek(1, false) == 0);
	REQUIRE(c->seek(9, true) == 9);
	REQUIRE(c->seek(3, false) == 6);
	REQUIRE(c->seek(100, true) == 13);
	REQUIRE(c->seek(100, false) == 0);
	REQUIRE(c->seek(100, true) == 13);
	c->close();
}

TEST_CASE("giga|file-read") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 2)));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(c->read(0).compare("") == 0);
	REQUIRE(c->read(1).compare("h") == 0);
	REQUIRE(c->read(2).compare("el") == 0);
	REQUIRE(c->read(100).compare("lo world!\n") == 0);
	c->close();
}

TEST_CASE("giga|file-erase") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 5)));
	std::shared_ptr<giga::Client> c_1 = f->open();
	std::shared_ptr<giga::Client> c_2 = f->open();

	REQUIRE(c_2->seek(1, true) == 1);

	REQUIRE(c_2->erase(1) == 1);
	REQUIRE(f->get_size() == 12);
	REQUIRE(c_1->get_pos() == 0);
	REQUIRE(c_2->get_pos() == 1);
	REQUIRE(c_1->read(100).compare("hllo world!\n") == 0);
	REQUIRE(c_2->read(100).compare("llo world!\n") == 0);

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_1->seek(1, true) == 1);
	REQUIRE(c_2->seek(2, true) == 2);
	REQUIRE(c_1->get_pos() == 1);
	REQUIRE(c_2->get_pos() == 2);

	REQUIRE(c_1->erase(1) == 1);
	REQUIRE(f->get_size() == 11);
	REQUIRE(c_1->get_pos() == 1);
	REQUIRE(c_2->get_pos() == 1);
	REQUIRE(c_1->read(100).compare("lo world!\n") == 0);
	REQUIRE(c_2->read(100).compare("lo world!\n") == 0);
	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_2->read(100).compare("hlo world!\n") == 0);
	REQUIRE(c_1->read(100).compare("hlo world!\n") == 0);

	c_1->close();
	c_2->close();

	f.reset();
	f = std::shared_ptr<giga::File> (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 5)));
	c_1 = f->open();
	c_2 = f->open();

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_2->seek(2, true) == 2);

	REQUIRE(c_1->erase(2) == 2);
	REQUIRE(c_1->get_pos() == 0);
	REQUIRE(c_2->get_pos() == 0);
	REQUIRE(c_1->read(100).compare("llo world!\n") == 0);
	REQUIRE(c_2->read(100).compare("llo world!\n") == 0);

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_2->seek(3, true) == 3);

	REQUIRE(c_1->erase(2) == 2);
	REQUIRE(c_1->get_pos() == 0);
	REQUIRE(c_2->get_pos() == 1);

	REQUIRE(c_1->read(100).compare("o world!\n") == 0);
	REQUIRE(c_2->read(100).compare(" world!\n") == 0);

	REQUIRE(c_1->erase(100) == 0);

	REQUIRE(c_1->seek(100, false) == 0);

	std::cout << "REQUIRE(c_1->erase(100))" << std::endl;
	REQUIRE(c_1->erase(100) == 8);
	REQUIRE(c_1->read(100).compare("") == 0);
	REQUIRE(c_2->read(100).compare("") == 0);
	REQUIRE(f->get_size() == 0);
	REQUIRE(c_1->get_pos() == 0);
	REQUIRE(c_2->get_pos() == 0);

	c_1->close();
	c_2->close();
}

TEST_CASE("giga|file-insert") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 3)));
	std::shared_ptr<giga::Client> c_1 = f->open();
	std::shared_ptr<giga::Client> c_2 = f->open();

	REQUIRE(c_1->seek(1, true) == 1);
	REQUIRE(f->get_size() == 13);
	REQUIRE(c_2->write("foo", true) == 3);
	REQUIRE(f->get_size() == 16);
	REQUIRE(c_2->get_pos() == 3);
	REQUIRE(c_1->get_pos() == 4);
	REQUIRE(c_1->read(100).compare("ello world!\n") == 0);

	REQUIRE(c_1->seek(100, false) == 0);
	REQUIRE(c_2->seek(100, false) == 0);
	REQUIRE(c_1->seek(1, true) == 1);
	REQUIRE(c_1->write("foo", true) == 3);
	REQUIRE(f->get_size() == 19);
	REQUIRE(c_2->get_pos() == 0);
	REQUIRE(c_2->read(100).compare("ffoooohello world!\n") == 0);

	REQUIRE(c_2->write("addendum") == 8);
	REQUIRE(f->get_size() == 27);
	REQUIRE(c_1->read(100).compare("oohello world!\naddendum") == 0);

	c_1->close();
	c_2->close();
}

TEST_CASE("giga|file-write") {
	std::shared_ptr<giga::File> f (new giga::File("tests/files/giga-file-read", "r", giga::Config(2, 3)));
	std::shared_ptr<giga::Client> c = f->open();
	REQUIRE(c->write("") == 0);
	REQUIRE(c->get_pos() == 0);
	REQUIRE(c->write("abcde") == 5);
	REQUIRE(c->get_pos() == 5);
	REQUIRE(c->write("|world!\nEXTRAEXTRA") == 18);
	REQUIRE(f->get_size() == 23);
	REQUIRE(c->get_pos() == 23);
	REQUIRE(c->seek(100, false) == 0);
	REQUIRE(c->read(100).compare("abcde|world!\nEXTRAEXTRA") == 0);
	c->close();
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <iostream>
#include <map>
#include <rtl/string.hxx>
#include "po.hxx"

// Translated style names must be unique
static void checkStyleNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedStyleNames;
    std::map<OString,sal_uInt16> aLocalizedNumStyleNames;
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage + "/sw/source/ui/utlui.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" &&
            aPoEntry.getGroupId().startsWith("STR_POOLCOLL") )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() )
                aLocalizedStyleNames[aMsgStr] = 1;
            else
                aLocalizedStyleNames[aMsgStr]++;
        }
        if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" &&
            aPoEntry.getGroupId().startsWith("STR_POOLNUMRULE") )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() )
                aLocalizedNumStyleNames[aMsgStr] = 1;
            else
                aLocalizedNumStyleNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedStyleNames.begin(); it!=aLocalizedStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLCOLL_*\n\n";
        }
    }
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedNumStyleNames.begin(); it!=aLocalizedNumStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLNUMRULE_*\n\n";
        }
    }
}

// Translated spreadsheet function names must be unique
static void checkFunctionNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedFunctionNames;
    std::map<OString,sal_uInt16> aLocalizedCoreFunctionNames;
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/formula/source/core/resource.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_STRLIST_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) == aLocalizedCoreFunctionNames.end() )
                aLocalizedCoreFunctionNames[aMsgStr] = 1;
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
        "/translations/source/" +
        aLanguage +
        "/scaddins/source/analysis.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_ANALYSIS_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/datefunc.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_DATE_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/pricing.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_PRICING_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedFunctionNames.begin(); it!=aLocalizedFunctionNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Spreadsheet function name translations must be unique.\n" <<
                "Language: " << aLanguage <<
                "\nDuplicated translation is: " << it->first << "\n\n";
        }
    }
}

// In instsetoo_native/inc_openoffice/windows/msi_languages.po
// where an en-US string ends with '|', translation must end
// with '|', too.
static void checkVerticalBar(OString aLanguage)
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/instsetoo_native/inc_openoffice/windows/msi_languages.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|") &&
            !aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|") )
        {
            std::cout << "ERROR: Missing '|' character at the end of translated string.\n" <<
                "It causes runtime error in installer.\n" <<
                "File: " << aPoPath << std::endl <<
                "Language: " << aLanguage << std::endl <<
                "English:   " << aPoEntry.getMsgId() << std::endl <<
                "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl;
        }
    }
    aPoInput.close();
}

int main()
{
    OString aLanguages(getenv("ALL_LANGS"));
    if( aLanguages.isEmpty() )
    {
        std::cerr << "Usage: make cmd cmd=solver/*/bin/pocheck\n";
        return 1;
    }
    for(sal_Int32 i = 1;;++i) // skip en-US
    {
         OString aLanguage = aLanguages.getToken(i,' ');
         if( aLanguage.isEmpty() )
             break;
         if( aLanguage == "qtz" )
             continue;
         checkStyleNames(aLanguage);
         checkFunctionNames(aLanguage);
         checkVerticalBar(aLanguage);
    }
    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>pocheck: Math symbol names (from symbol.src) must not contain spaces<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <iostream>
#include <map>
#include <rtl/string.hxx>
#include "po.hxx"

// Translated style names must be unique
static void checkStyleNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedStyleNames;
    std::map<OString,sal_uInt16> aLocalizedNumStyleNames;
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage + "/sw/source/ui/utlui.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" &&
            aPoEntry.getGroupId().startsWith("STR_POOLCOLL") )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() )
                aLocalizedStyleNames[aMsgStr] = 1;
            else
                aLocalizedStyleNames[aMsgStr]++;
        }
        if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" &&
            aPoEntry.getGroupId().startsWith("STR_POOLNUMRULE") )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() )
                aLocalizedNumStyleNames[aMsgStr] = 1;
            else
                aLocalizedNumStyleNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedStyleNames.begin(); it!=aLocalizedStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLCOLL_*\n\n";
        }
    }
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedNumStyleNames.begin(); it!=aLocalizedNumStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLNUMRULE_*\n\n";
        }
    }
}

// Translated spreadsheet function names must be unique
static void checkFunctionNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedFunctionNames;
    std::map<OString,sal_uInt16> aLocalizedCoreFunctionNames;
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/formula/source/core/resource.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_STRLIST_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) == aLocalizedCoreFunctionNames.end() )
                aLocalizedCoreFunctionNames[aMsgStr] = 1;
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
        "/translations/source/" +
        aLanguage +
        "/scaddins/source/analysis.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_ANALYSIS_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/datefunc.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_DATE_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/pricing.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_PRICING_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedFunctionNames.begin(); it!=aLocalizedFunctionNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Spreadsheet function name translations must be unique.\n" <<
                "Language: " << aLanguage <<
                "\nDuplicated translation is: " << it->first << "\n\n";
        }
    }
}

// In instsetoo_native/inc_openoffice/windows/msi_languages.po
// where an en-US string ends with '|', translation must end
// with '|', too.
static void checkVerticalBar(OString aLanguage)
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/instsetoo_native/inc_openoffice/windows/msi_languages.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|") &&
            !aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|") )
        {
            std::cout << "ERROR: Missing '|' character at the end of translated string.\n" <<
                "It causes runtime error in installer.\n" <<
                "File: " << aPoPath << std::endl <<
                "Language: " << aLanguage << std::endl <<
                "English:   " << aPoEntry.getMsgId() << std::endl <<
                "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl;
        }
    }
    aPoInput.close();
}

// In starmath/source.po Math symbol names (from symbol.src)
// must not contain spaces
static void checkMathSymbolNames(OString aLanguage)
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/starmath/source.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_UI_SYMBOL_NAMES" &&
            !aPoEntry.getMsgStr().isEmpty() && (aPoEntry.getMsgStr().indexOf(" ") != -1) )
        {
            std::cout << "ERROR: Math symbol names must not contain spaces.\n" <<
                "File: " << aPoPath << std::endl <<
                "Language: " << aLanguage << std::endl <<
                "English:   " << aPoEntry.getMsgId() << std::endl <<
                "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl;
        }
    }
    aPoInput.close();
}

int main()
{
    OString aLanguages(getenv("ALL_LANGS"));
    if( aLanguages.isEmpty() )
    {
        std::cerr << "Usage: make cmd cmd=solver/*/bin/pocheck\n";
        return 1;
    }
    for(sal_Int32 i = 1;;++i) // skip en-US
    {
         OString aLanguage = aLanguages.getToken(i,' ');
         if( aLanguage.isEmpty() )
             break;
         if( aLanguage == "qtz" )
             continue;
         checkStyleNames(aLanguage);
         checkFunctionNames(aLanguage);
         checkVerticalBar(aLanguage);
         checkMathSymbolNames(aLanguage);
    }
    return 0;
}

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


<commit_before>/** \file   system_monitor.cc
 *  \brief  Collect a few basic system metrics
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <csignal>
#include <cstring>
#include <unistd.h>
#include "Compiler.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "SignalUtil.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage("[--foreground] output_filename\n"
            "       When --foreground has been specified the program does not daemonise.\n"
            "       The config file path is \"" + UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf\".");
}

volatile sig_atomic_t sigterm_seen = false;

void SigTermHandler(int /* signum */) {
    sigterm_seen = true;
}

void CheckForSigTermAndExitIfSeen() {
    if (sigterm_seen) {
        LOG_WARNING("caught SIGTERM, exiting...");
        std::exit(EXIT_SUCCESS);
    }
}

// Returns local time using an ISO 8601 format w/o time zone.
inline std::string GetLocalTime() {
    return TimeUtil::GetCurrentDateAndTime(TimeUtil::ISO_8601_FORMAT);
}

void CollectCPUStats(File * const log) {
    static auto proc_meminfo(FileUtil::OpenInputFileOrDie("/proc/stat"));
    const auto current_date_and_time(GetLocalTime());
    static uint64_t last_total, last_idle;
    std::string line;
    while (proc_meminfo->getline(&line) > 0) {
        if (StringUtil::StartsWith(line, "cpu ")) {
            std::vector<std::string> parts;
            StringUtil::Split(line, ' ', &parts, /* suppress_empty_components = */true);
            uint64_t total(0), idle(StringUtil::ToUInt64T(parts[4]));
            for (unsigned i(1); i < parts.size(); ++i)
                total += StringUtil::ToUInt64T(parts[i]);
            const uint64_t diff_idle(idle - last_idle);
            const uint64_t diff_total(total - last_total);
            const uint64_t diff_usage((1000ull * (diff_total - diff_idle) / diff_total + 5) / 10ull);
            (*log) << "CPU " << diff_usage << ' ' << current_date_and_time << '\n';
            log->flush();
            last_total = total;
            last_idle = idle;
            return;
        }
    }
}

void CollectMemoryStats(File * const log) {
    static const auto proc_meminfo(FileUtil::OpenInputFileOrDie("/proc/meminfo"));
    static const std::vector<std::string> COLLECTED_LABELS{ "MemAvailable", "SwapFree", "Unevictable" };

    const auto current_date_and_time(GetLocalTime());
    std::string line;
    while (proc_meminfo->getline(&line) > 0) {
        const auto first_colon_pos(line.find(':'));
        if (unlikely(first_colon_pos == std::string::npos))
            LOG_ERROR("missing colon in \"" + line + "\"!");
        const auto label(line.substr(0, first_colon_pos));
        if (std::find(COLLECTED_LABELS.cbegin(), COLLECTED_LABELS.cend(), label) == COLLECTED_LABELS.cend())
            continue;

        const auto rest(StringUtil::LeftTrim(line.substr(first_colon_pos + 1)));
        const auto first_space_pos(rest.find(' '));
        if (first_space_pos == std::string::npos)
            (*log) << label << ' ' << rest << ' ' << current_date_and_time << '\n';
        else
            (*log) << label << ' ' << rest.substr(0, first_space_pos) << ' ' << current_date_and_time << '\n';
    }
    log->flush();

    proc_meminfo->rewind();
}

void CollectDiscStats(File * const log) {
    const auto current_date_and_time(GetLocalTime());
    FileUtil::Directory directory("/sys/block", "sd?");
    for (const auto &entry : directory) {
        const auto block_device_path("/sys/block/" + entry.getName() + "/size");
        const auto proc_entry(FileUtil::OpenInputFileOrDie(block_device_path));
        std::string line;
        proc_entry->getline(&line);
        (*log) << block_device_path <<  ' ' << StringUtil::ToUnsignedLong(line) * 512 << ' ' << current_date_and_time << '\n';
    }
    log->flush();
}

const std::string PID_FILE("/usr/local/run/system_monitor.pid");

bool IsAlreadyRunning(std::string * const pid_as_string) {
    if (not FileUtil::Exists(PID_FILE))
        return false;

    FileUtil::ReadString(PID_FILE, pid_as_string);
    pid_t pid;
    if (not StringUtil::ToNumber(*pid_as_string, &pid))
        LOG_ERROR("\"" + *pid_as_string + "\" is not a valid PID!");

    return ::getpgid(pid) >= 0;
}

void CheckStats(const uint64_t ticks, const unsigned stats_interval, void (*stats_func)(File * const log), File * const log) {
    if ((ticks % stats_interval) == 0) {
        SignalUtil::SignalBlocker sighup_blocker(SIGHUP);
        stats_func(log);
    }
    CheckForSigTermAndExitIfSeen();
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc < 2)
        Usage();

    bool foreground(false);
    if (std::strcmp(argv[1], "--foreground") == 0) {
        foreground = true;
        --argc, ++argv;
    }
    if (argc != 2)
        Usage();

    std::string pid;
    if (IsAlreadyRunning(&pid)) {
        std::cerr << "system_monitor: This service may already be running! (PID: "<< pid << ")\n";
        return EXIT_FAILURE;
    }

    const IniFile ini_file(UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf");

    const unsigned memory_stats_interval(ini_file.getUnsigned("", "memory_stats_interval"));
    const unsigned disc_stats_interval(ini_file.getUnsigned("", "disc_stats_interval"));
    const unsigned cpu_stats_interval(ini_file.getUnsigned("", "cpu_stats_interval"));

    if (not foreground) {
        SignalUtil::InstallHandler(SIGTERM, SigTermHandler);

        if (::daemon(0, 1 /* do not close file descriptors and redirect to /dev/null */) != 0)
            LOG_ERROR("we failed to deamonize our process!");
    }
    if (not FileUtil::WriteString(PID_FILE, StringUtil::ToString(::getpid())))
        LOG_ERROR("failed to write our PID to " + PID_FILE + "!");

    const auto log(FileUtil::OpenForAppendingOrDie(argv[1]));

    uint64_t ticks(0);
    for (;;) {
        CheckStats(ticks, memory_stats_interval, CollectMemoryStats, log.get());
        CheckStats(ticks, disc_stats_interval, CollectDiscStats, log.get());
        CheckStats(ticks, cpu_stats_interval, CollectCPUStats, log.get());

        ::sleep(1);
        ++ticks;
    }
}
<commit_msg>Fixed a typo.<commit_after>/** \file   system_monitor.cc
 *  \brief  Collect a few basic system metrics
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <iostream>
#include <csignal>
#include <cstring>
#include <unistd.h>
#include "Compiler.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "SignalUtil.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage("[--foreground] output_filename\n"
            "       When --foreground has been specified the program does not daemonise.\n"
            "       The config file path is \"" + UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf\".");
}

volatile sig_atomic_t sigterm_seen = false;

void SigTermHandler(int /* signum */) {
    sigterm_seen = true;
}

void CheckForSigTermAndExitIfSeen() {
    if (sigterm_seen) {
        LOG_WARNING("caught SIGTERM, exiting...");
        std::exit(EXIT_SUCCESS);
    }
}

// Returns local time using an ISO 8601 format w/o time zone.
inline std::string GetLocalTime() {
    return TimeUtil::GetCurrentDateAndTime(TimeUtil::ISO_8601_FORMAT);
}

void CollectCPUStats(File * const log) {
    static auto proc_meminfo(FileUtil::OpenInputFileOrDie("/proc/stat"));
    const auto current_date_and_time(GetLocalTime());
    static uint64_t last_total, last_idle;
    std::string line;
    while (proc_meminfo->getline(&line) > 0) {
        if (StringUtil::StartsWith(line, "cpu ")) {
            std::vector<std::string> parts;
            StringUtil::Split(line, ' ', &parts, /* suppress_empty_components = */true);
            uint64_t total(0), idle(StringUtil::ToUInt64T(parts[4]));
            for (unsigned i(1); i < parts.size(); ++i)
                total += StringUtil::ToUInt64T(parts[i]);
            const uint64_t diff_idle(idle - last_idle);
            const uint64_t diff_total(total - last_total);
            const uint64_t diff_usage((1000ull * (diff_total - diff_idle) / diff_total + 5) / 10ull);
            (*log) << "CPU " << diff_usage << ' ' << current_date_and_time << '\n';
            log->flush();
            last_total = total;
            last_idle = idle;
            return;
        }
    }
}

void CollectMemoryStats(File * const log) {
    static const auto proc_meminfo(FileUtil::OpenInputFileOrDie("/proc/meminfo"));
    static const std::vector<std::string> COLLECTED_LABELS{ "MemAvailable", "SwapFree", "Unevictable" };

    const auto current_date_and_time(GetLocalTime());
    std::string line;
    while (proc_meminfo->getline(&line) > 0) {
        const auto first_colon_pos(line.find(':'));
        if (unlikely(first_colon_pos == std::string::npos))
            LOG_ERROR("missing colon in \"" + line + "\"!");
        const auto label(line.substr(0, first_colon_pos));
        if (std::find(COLLECTED_LABELS.cbegin(), COLLECTED_LABELS.cend(), label) == COLLECTED_LABELS.cend())
            continue;

        const auto rest(StringUtil::LeftTrim(line.substr(first_colon_pos + 1)));
        const auto first_space_pos(rest.find(' '));
        if (first_space_pos == std::string::npos)
            (*log) << label << ' ' << rest << ' ' << current_date_and_time << '\n';
        else
            (*log) << label << ' ' << rest.substr(0, first_space_pos) << ' ' << current_date_and_time << '\n';
    }
    log->flush();

    proc_meminfo->rewind();
}

void CollectDiscStats(File * const log) {
    const auto current_date_and_time(GetLocalTime());
    FileUtil::Directory directory("/sys/block", "sd?");
    for (const auto &entry : directory) {
        const auto block_device_path("/sys/block/" + entry.getName() + "/size");
        const auto proc_entry(FileUtil::OpenInputFileOrDie(block_device_path));
        std::string line;
        proc_entry->getline(&line);
        (*log) << block_device_path <<  ' ' << StringUtil::ToUnsignedLong(line) * 512 << ' ' << current_date_and_time << '\n';
    }
    log->flush();
}

const std::string PID_FILE("/usr/local/run/system_monitor.pid");

bool IsAlreadyRunning(std::string * const pid_as_string) {
    if (not FileUtil::Exists(PID_FILE))
        return false;

    FileUtil::ReadString(PID_FILE, pid_as_string);
    pid_t pid;
    if (not StringUtil::ToNumber(*pid_as_string, &pid))
        LOG_ERROR("\"" + *pid_as_string + "\" is not a valid PID!");

    return ::getpgid(pid) >= 0;
}

void CheckStats(const uint64_t ticks, const unsigned stats_interval, void (*stats_func)(File * const log), File * const log) {
    if ((ticks % stats_interval) == 0) {
        SignalUtil::SignalBlocker sighup_blocker(SIGHUP);
        stats_func(log);
    }
    CheckForSigTermAndExitIfSeen();
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc < 2)
        Usage();

    bool foreground(false);
    if (std::strcmp(argv[1], "--foreground") == 0) {
        foreground = true;
        --argc, ++argv;
    }
    if (argc != 2)
        Usage();

    std::string pid;
    if (IsAlreadyRunning(&pid)) {
        std::cerr << "system_monitor: This service may already be running! (PID: "<< pid << ")\n";
        return EXIT_FAILURE;
    }

    const IniFile ini_file(UBTools::GetTuelibPath() + FileUtil::GetBasename(::progname) + ".conf");

    const unsigned memory_stats_interval(ini_file.getUnsigned("", "memory_stats_interval"));
    const unsigned disc_stats_interval(ini_file.getUnsigned("", "disc_stats_interval"));
    const unsigned cpu_stats_interval(ini_file.getUnsigned("", "cpu_stats_interval"));

    if (not foreground) {
        SignalUtil::InstallHandler(SIGTERM, SigTermHandler);

        if (::daemon(0, 1 /* do not close file descriptors and redirect to /dev/null */) != 0)
            LOG_ERROR("we failed to daemonize our process!");
    }
    if (not FileUtil::WriteString(PID_FILE, StringUtil::ToString(::getpid())))
        LOG_ERROR("failed to write our PID to " + PID_FILE + "!");

    const auto log(FileUtil::OpenForAppendingOrDie(argv[1]));

    uint64_t ticks(0);
    for (;;) {
        CheckStats(ticks, memory_stats_interval, CollectMemoryStats, log.get());
        CheckStats(ticks, disc_stats_interval, CollectDiscStats, log.get());
        CheckStats(ticks, cpu_stats_interval, CollectCPUStats, log.get());

        ::sleep(1);
        ++ticks;
    }
}
<|endoftext|>


<commit_before>#include "Exception.h"

using namespace CppUnit;

const std::string
CppUnit::Exception::UNKNOWNFILENAME =
  "<unknown>";
const int CppUnit::Exception::UNKNOWNLINENUMBER = -1;

/// Construct the exception
CppUnit::Exception::Exception (const Exception& other)
  : exception (other)
{
  m_message       = other.m_message;
  m_lineNumber    = other.m_lineNumber;
  m_fileName      = other.m_fileName;
}

CppUnit::Exception::Exception (std::string message, long lineNumber, std::string fileName)
  : m_message (message), m_lineNumber (lineNumber), m_fileName (fileName)
{}

/// Destruct the exception
CppUnit::Exception::~Exception ()
{}

/// Perform an assignment
Exception&
CppUnit::Exception::operator= (const Exception& other)
{
  exception::operator= (other);

  if (&other != this) {
    m_message       = other.m_message;
    m_lineNumber    = other.m_lineNumber;
    m_fileName      = other.m_fileName;
  }

  return *this;
}

/// Return descriptive message
const char*
CppUnit::Exception::what() const throw ()
{ return m_message.c_str (); }

/// The line on which the error occurred
long
CppUnit::Exception::lineNumber ()
{ return m_lineNumber; }

/// The file in which the error occurred
std::string
CppUnit::Exception::fileName ()
{ return m_fileName; }

<commit_msg>code beautification.<commit_after>#include "Exception.h"

using namespace CppUnit;

const std::string
CppUnit::Exception::UNKNOWNFILENAME =
  "<unknown>";
const int CppUnit::Exception::UNKNOWNLINENUMBER = -1;

/// Construct the exception
CppUnit::Exception::Exception (const Exception& other)
  : exception (other)
{
  m_message       = other.m_message;
  m_lineNumber    = other.m_lineNumber;
  m_fileName      = other.m_fileName;
}

CppUnit::Exception::Exception (std::string message, long lineNumber, std::string fileName)
  : m_message (message), m_lineNumber (lineNumber), m_fileName (fileName)
{
}

/// Destruct the exception
CppUnit::Exception::~Exception ()
{}

/// Perform an assignment
Exception&
CppUnit::Exception::operator= (const Exception& other)
{
  exception::operator= (other);

  if (&other != this) {
    m_message       = other.m_message;
    m_lineNumber    = other.m_lineNumber;
    m_fileName      = other.m_fileName;
  }

  return *this;
}

/// Return descriptive message
const char*
CppUnit::Exception::what() const throw ()
{ return m_message.c_str (); }

/// The line on which the error occurred
long
CppUnit::Exception::lineNumber ()
{ return m_lineNumber; }

/// The file in which the error occurred
std::string
CppUnit::Exception::fileName ()
{ return m_fileName; }

<|endoftext|>


<commit_before>#include <qt/mintingtablemodel.h>
#include <qt/mintingfilterproxy.h>

#include <kernelrecord.h>
#include <qt/transactiondesc.h>

//#include <qt/guiutil.h>
#include <qt/walletmodel.h>
#include <qt/guiconstants.h>
#include <qt/bitcoinunits.h>
#include <qt/optionsmodel.h>
#include <qt/addresstablemodel.h>

#include <util.h>
#include <wallet/wallet.h>
#include <validation.h>

#include <ui_interface.h>

#include <QColor>
#include <QTimer>

// Amount column is right-aligned it contains numbers
static int column_alignments[] = {
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter
    };

// Comparison operator for sort/binary search of model tx list
struct TxLessThan
{
    bool operator()(const KernelRecord &a, const KernelRecord &b) const
    {
        return a.hash < b.hash;
    }
    bool operator()(const KernelRecord &a, const uint256 &b) const
    {
        return a.hash < b;
    }
    bool operator()(const uint256 &a, const KernelRecord &b) const
    {
        return a < b.hash;
    }
};

// Private implementation
class MintingTablePriv
{
public:
    MintingTablePriv(CWallet *wallet, MintingTableModel *parent):
            wallet(wallet),
            parent(parent)
    {
    }
    CWallet *wallet;
    MintingTableModel *parent;

    /* Local cache of wallet.
     * As it is in the same order as the CWallet, by definition
     * this is sorted by sha256.
     */
    QList<KernelRecord> cachedWallet;

    /* Query entire wallet anew from core.
     */
    void refreshWallet()
    {
        LogPrintf("refreshWallet\n");
        cachedWallet.clear();
        {
            LOCK(wallet->cs_wallet);
            for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
            {
                std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(wallet, it->second);
                if(KernelRecord::showTransaction(it->second))
                    for(const KernelRecord& kr : txList) {
                        if(!kr.spent) {
                            cachedWallet.append(kr);
                        }
                    }
            }
        }
    }

    /* Update our model of the wallet incrementally, to synchronize our model of the wallet
       with that of the core.

       Call with transaction that was added, removed or changed.
     */
    void updateWallet(const uint256 &hash, int status)
    {
        LogPrintf("minting updateWallet %s %i\n", hash.ToString(), status);
        {
            LOCK(wallet->cs_wallet);

            // Find transaction in wallet
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
            bool inWallet = mi != wallet->mapWallet.end();

            // Find bounds of this transaction in model
            QList<KernelRecord>::iterator lower = qLowerBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            QList<KernelRecord>::iterator upper = qUpperBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            int lowerIndex = (lower - cachedWallet.begin());
            int upperIndex = (upper - cachedWallet.begin());
            bool inModel = (lower != upper);

            // Determine whether to show transaction or not
            bool showTransaction = (inWallet && KernelRecord::showTransaction(mi->second));

            if(status == CT_UPDATED)
            {
                if(showTransaction && !inModel)
                    status = CT_NEW; /* Not in model, but want to show, treat as new */
                if(!showTransaction && inModel)
                    status = CT_DELETED; /* In model, but want to hide, treat as deleted */
            }

            LogPrintf("   inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
                     inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);

            switch(status)
            {
            case CT_NEW:
                if(inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
                    break;
                }
                if(!inWallet)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
                    break;
                }
                if(showTransaction)
                {
                    // Added -- insert at the right position
                    std::vector<KernelRecord> toInsert =
                            KernelRecord::decomposeOutput(wallet, mi->second);
                    if(toInsert.size() != 0) /* only if something to insert */
                    {
                        parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
                        int insert_idx = lowerIndex;
                        for (const KernelRecord &rec : toInsert)
                        {
                            if(!rec.spent)
                            {
                                cachedWallet.insert(insert_idx, rec);
                                insert_idx += 1;
                            }
                        }
                        parent->endInsertRows();
                    }
                }
                break;
            case CT_DELETED:
                if(!inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n");
                    break;
                }
                // Removed -- remove entire transaction from table
                parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
                cachedWallet.erase(lower, upper);
                parent->endRemoveRows();
                break;
            case CT_UPDATED:
                // Updated -- remove spent coins from table
                std::vector<KernelRecord> toCheck = KernelRecord::decomposeOutput(wallet, mi->second);
                if(!toCheck.empty())
                {
                    for(const KernelRecord &rec : toCheck)
                    {
                        if(rec.spent)
                        {
                            for(int i = lowerIndex; i < upperIndex; i++)
                            {
                                KernelRecord cachedRec = cachedWallet.at(i);
                                if((rec.address == cachedRec.address)
                                   && (rec.nValue == cachedRec.nValue)
                                   && (rec.idx == cachedRec.idx))
                                {
                                    parent->beginRemoveRows(QModelIndex(), i, i);
                                    cachedWallet.removeAt(i);
                                    parent->endRemoveRows();
                                    break;
                                }
                            }
                        }
                    }
                }
                break;
            }
        }
    }

    int size()
    {
        return cachedWallet.size();
    }

    KernelRecord *index(int idx)
    {
        if(idx >= 0 && idx < cachedWallet.size())
        {
            KernelRecord *rec = &cachedWallet[idx];
            return rec;
        }
        else
        {
            return 0;
        }
    }

    QString describe(KernelRecord *rec)
    {
        {
            LOCK(wallet->cs_wallet);
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
            if(mi != wallet->mapWallet.end())
            {
                return TransactionDesc::toHTML(wallet, mi->second, nullptr, 0);  //ppcTODO - fix the last 2 parameters
            }
        }
        return QString("");
    }

};

MintingTableModel::MintingTableModel(CWallet* wallet, WalletModel *parent) :
        QAbstractTableModel(parent),
        wallet(wallet),
        walletModel(parent),
        mintingInterval(10),
        priv(new MintingTablePriv(wallet, this)),
        cachedNumBlocks(0)
{
    columns << tr("Transaction") <<  tr("Address") << tr("Age") << tr("Balance") << tr("CoinDay") << tr("MintProbability");

    priv->refreshWallet();

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateAge()));
    timer->start(MODEL_UPDATE_DELAY);

    connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
}

MintingTableModel::~MintingTableModel()
{
    delete priv;
}

void MintingTableModel::updateTransaction(const QString &hash, int status)
{
    uint256 updated;
    updated.SetHex(hash.toStdString());

    priv->updateWallet(updated, status);
    mintingProxyModel->invalidate(); // Force deletion of empty rows
}

void MintingTableModel::updateAge()
{
    Q_EMIT dataChanged(index(0, Age), index(priv->size()-1, Age));
    Q_EMIT dataChanged(index(0, CoinDay), index(priv->size()-1, CoinDay));
    Q_EMIT dataChanged(index(0, MintProbability), index(priv->size()-1, MintProbability));
}

void MintingTableModel::setMintingProxyModel(MintingFilterProxy *mintingProxy)
{
    mintingProxyModel = mintingProxy;
}

int MintingTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MintingTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MintingTableModel::data(const QModelIndex &index, int role) const
{
    const Consensus::Params& params = Params().GetConsensus();
    if(!index.isValid())
        return QVariant();
    KernelRecord *rec = static_cast<KernelRecord*>(index.internalPointer());

    switch(role)
    {
      case Qt::DisplayRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return formatTxAge(rec);
        case Balance:
            return formatTxBalance(rec);
        case CoinDay:
            return formatTxCoinDay(rec);
        case MintProbability:
            return formatDayToMint(rec);
        }
        break;
      case Qt::TextAlignmentRole:
        return column_alignments[index.column()];
        break;
      case Qt::ToolTipRole:
        switch(index.column())
        {
        case MintProbability:
            int interval = this->mintingInterval;
            QString unit = tr("minutes");

            int hours = interval / 60;
            int days = hours  / 24;

            if(hours > 1) {
                interval = hours;
                unit = tr("hours");
            }
            if(days > 1) {
                interval = days;
                unit = tr("days");
            }

            QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
            return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
        }
        break;
      case Qt::EditRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return qint64(rec->getAge());
        case CoinDay:
            return qint64(rec->coinAge);
        case Balance:
            return qint64(rec->nValue);
        case MintProbability:
            return getDayToMint(rec);
        }
        break;
      case Qt::BackgroundColorRole:
        int minAge = params.nStakeMinAge / 60 / 60 / 24;
        int maxAge = params.nStakeMaxAge / 60 / 60 / 24;
        if(rec->getAge() < minAge)
        {
            return COLOR_MINT_YOUNG;
        }
        else if (rec->getAge() >= minAge && rec->getAge() < maxAge)
        {
            return COLOR_MINT_MATURE;
        }
        else
        {
            return COLOR_MINT_OLD;
        }
        break;

    }
    return QVariant();
}

void MintingTableModel::setMintingInterval(int interval)
{
    mintingInterval = interval;
}

QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
{
    QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
    QString description;
    if(!label.isEmpty())
    {
        description += label + QString(" ");
    }
    if(label.isEmpty() || tooltip)
    {
        description += QString(" (") + QString::fromStdString(address) + QString(")");
    }
    return description;
}

double MintingTableModel::getDayToMint(KernelRecord *wtx) const
{
    const CBlockIndex *p = GetLastBlockIndex(chainActive.Tip(), true);
    double difficulty = p->GetBlockDifficulty();

    double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
    prob = prob * 100;
    return prob;
}

QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
{
    double prob = getDayToMint(wtx);
    return QString::number(prob, 'f', 6) + "%";
}

QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
{
    return QString::fromStdString(wtx->address);
}

QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
{
    return QString::fromStdString(wtx->hash.ToString());
}

QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
{
    return QString::number(wtx->coinAge);
}

QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
{
    int64_t nAge = wtx->getAge();
    return QString::number(nAge);
}

QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
{
    return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
}

QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
        else if (role == Qt::TextAlignmentRole)
        {
            return column_alignments[section];
        } else if (role == Qt::ToolTipRole)
        {
            switch(section)
            {
            case Address:
                return tr("Destination address of the output.");
            case TxHash:
                return tr("Original transaction id.");
            case Age:
                return tr("Age of the transaction in days.");
            case Balance:
                return tr("Balance of the output.");
            case CoinDay:
                return tr("Coin age in the output.");
            case MintProbability:
                return tr("Chance to mint a block within given time interval.");
            }
        }
    }
    return QVariant();
}

QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    KernelRecord *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MintingTableModel::updateDisplayUnit()
{
    // emit dataChanged to update Balance column with the current unit
    Q_EMIT dataChanged(index(0, Balance), index(priv->size()-1, Balance));
}
<commit_msg>fix loading wallet<commit_after>#include <qt/mintingtablemodel.h>
#include <qt/mintingfilterproxy.h>

#include <kernelrecord.h>
#include <qt/transactiondesc.h>

//#include <qt/guiutil.h>
#include <qt/walletmodel.h>
#include <qt/guiconstants.h>
#include <qt/bitcoinunits.h>
#include <qt/optionsmodel.h>
#include <qt/addresstablemodel.h>

#include <util.h>
#include <wallet/wallet.h>
#include <validation.h>

#include <ui_interface.h>

#include <QColor>
#include <QTimer>

// Amount column is right-aligned it contains numbers
static int column_alignments[] = {
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignLeft|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter,
        Qt::AlignRight|Qt::AlignVCenter
    };

// Comparison operator for sort/binary search of model tx list
struct TxLessThan
{
    bool operator()(const KernelRecord &a, const KernelRecord &b) const
    {
        return a.hash < b.hash;
    }
    bool operator()(const KernelRecord &a, const uint256 &b) const
    {
        return a.hash < b;
    }
    bool operator()(const uint256 &a, const KernelRecord &b) const
    {
        return a < b.hash;
    }
};

// Private implementation
class MintingTablePriv
{
public:
    MintingTablePriv(CWallet *wallet, MintingTableModel *parent):
            wallet(wallet),
            parent(parent)
    {
    }
    CWallet *wallet;
    MintingTableModel *parent;

    /* Local cache of wallet.
     * As it is in the same order as the CWallet, by definition
     * this is sorted by sha256.
     */
    QList<KernelRecord> cachedWallet;

    /* Query entire wallet anew from core.
     */
    void refreshWallet()
    {
        LogPrintf("refreshWallet\n");
        cachedWallet.clear();
        {
            // cs_main lock was added because GetDepthInMainChain requires it
            LOCK2(cs_main, wallet->cs_wallet);
            for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
            {
                std::vector<KernelRecord> txList = KernelRecord::decomposeOutput(wallet, it->second);
                if(KernelRecord::showTransaction(it->second))
                    for(const KernelRecord& kr : txList) {
                        if(!kr.spent) {
                            cachedWallet.append(kr);
                        }
                    }
            }
        }
    }

    /* Update our model of the wallet incrementally, to synchronize our model of the wallet
       with that of the core.

       Call with transaction that was added, removed or changed.
     */
    void updateWallet(const uint256 &hash, int status)
    {
        LogPrintf("minting updateWallet %s %i\n", hash.ToString(), status);
        {
            LOCK(wallet->cs_wallet);

            // Find transaction in wallet
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
            bool inWallet = mi != wallet->mapWallet.end();

            // Find bounds of this transaction in model
            QList<KernelRecord>::iterator lower = qLowerBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            QList<KernelRecord>::iterator upper = qUpperBound(
                cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
            int lowerIndex = (lower - cachedWallet.begin());
            int upperIndex = (upper - cachedWallet.begin());
            bool inModel = (lower != upper);

            // Determine whether to show transaction or not
            bool showTransaction = (inWallet && KernelRecord::showTransaction(mi->second));

            if(status == CT_UPDATED)
            {
                if(showTransaction && !inModel)
                    status = CT_NEW; /* Not in model, but want to show, treat as new */
                if(!showTransaction && inModel)
                    status = CT_DELETED; /* In model, but want to hide, treat as deleted */
            }

            LogPrintf("   inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n",
                     inWallet, inModel, lowerIndex, upperIndex, showTransaction, status);

            switch(status)
            {
            case CT_NEW:
                if(inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is already in model\n");
                    break;
                }
                if(!inWallet)
                {
                    LogPrintf("Warning: updateWallet: Got CT_NEW, but transaction is not in wallet\n");
                    break;
                }
                if(showTransaction)
                {
                    // Added -- insert at the right position
                    std::vector<KernelRecord> toInsert =
                            KernelRecord::decomposeOutput(wallet, mi->second);
                    if(toInsert.size() != 0) /* only if something to insert */
                    {
                        parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
                        int insert_idx = lowerIndex;
                        for (const KernelRecord &rec : toInsert)
                        {
                            if(!rec.spent)
                            {
                                cachedWallet.insert(insert_idx, rec);
                                insert_idx += 1;
                            }
                        }
                        parent->endInsertRows();
                    }
                }
                break;
            case CT_DELETED:
                if(!inModel)
                {
                    LogPrintf("Warning: updateWallet: Got CT_DELETED, but transaction is not in model\n");
                    break;
                }
                // Removed -- remove entire transaction from table
                parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
                cachedWallet.erase(lower, upper);
                parent->endRemoveRows();
                break;
            case CT_UPDATED:
                // Updated -- remove spent coins from table
                std::vector<KernelRecord> toCheck = KernelRecord::decomposeOutput(wallet, mi->second);
                if(!toCheck.empty())
                {
                    for(const KernelRecord &rec : toCheck)
                    {
                        if(rec.spent)
                        {
                            for(int i = lowerIndex; i < upperIndex; i++)
                            {
                                KernelRecord cachedRec = cachedWallet.at(i);
                                if((rec.address == cachedRec.address)
                                   && (rec.nValue == cachedRec.nValue)
                                   && (rec.idx == cachedRec.idx))
                                {
                                    parent->beginRemoveRows(QModelIndex(), i, i);
                                    cachedWallet.removeAt(i);
                                    parent->endRemoveRows();
                                    break;
                                }
                            }
                        }
                    }
                }
                break;
            }
        }
    }

    int size()
    {
        return cachedWallet.size();
    }

    KernelRecord *index(int idx)
    {
        if(idx >= 0 && idx < cachedWallet.size())
        {
            KernelRecord *rec = &cachedWallet[idx];
            return rec;
        }
        else
        {
            return 0;
        }
    }

    QString describe(KernelRecord *rec)
    {
        {
            LOCK(wallet->cs_wallet);
            std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
            if(mi != wallet->mapWallet.end())
            {
                return TransactionDesc::toHTML(wallet, mi->second, nullptr, 0);  //ppcTODO - fix the last 2 parameters
            }
        }
        return QString("");
    }

};

MintingTableModel::MintingTableModel(CWallet* wallet, WalletModel *parent) :
        QAbstractTableModel(parent),
        wallet(wallet),
        walletModel(parent),
        mintingInterval(10),
        priv(new MintingTablePriv(wallet, this)),
        cachedNumBlocks(0)
{
    columns << tr("Transaction") <<  tr("Address") << tr("Age") << tr("Balance") << tr("CoinDay") << tr("MintProbability");

    priv->refreshWallet();

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateAge()));
    timer->start(MODEL_UPDATE_DELAY);

    connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
}

MintingTableModel::~MintingTableModel()
{
    delete priv;
}

void MintingTableModel::updateTransaction(const QString &hash, int status)
{
    uint256 updated;
    updated.SetHex(hash.toStdString());

    priv->updateWallet(updated, status);
    mintingProxyModel->invalidate(); // Force deletion of empty rows
}

void MintingTableModel::updateAge()
{
    Q_EMIT dataChanged(index(0, Age), index(priv->size()-1, Age));
    Q_EMIT dataChanged(index(0, CoinDay), index(priv->size()-1, CoinDay));
    Q_EMIT dataChanged(index(0, MintProbability), index(priv->size()-1, MintProbability));
}

void MintingTableModel::setMintingProxyModel(MintingFilterProxy *mintingProxy)
{
    mintingProxyModel = mintingProxy;
}

int MintingTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MintingTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MintingTableModel::data(const QModelIndex &index, int role) const
{
    const Consensus::Params& params = Params().GetConsensus();
    if(!index.isValid())
        return QVariant();
    KernelRecord *rec = static_cast<KernelRecord*>(index.internalPointer());

    switch(role)
    {
      case Qt::DisplayRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return formatTxAge(rec);
        case Balance:
            return formatTxBalance(rec);
        case CoinDay:
            return formatTxCoinDay(rec);
        case MintProbability:
            return formatDayToMint(rec);
        }
        break;
      case Qt::TextAlignmentRole:
        return column_alignments[index.column()];
        break;
      case Qt::ToolTipRole:
        switch(index.column())
        {
        case MintProbability:
            int interval = this->mintingInterval;
            QString unit = tr("minutes");

            int hours = interval / 60;
            int days = hours  / 24;

            if(hours > 1) {
                interval = hours;
                unit = tr("hours");
            }
            if(days > 1) {
                interval = days;
                unit = tr("days");
            }

            QString str = QString(tr("You have %1 chance to find a POS block if you mint %2 %3 at current difficulty."));
            return str.arg(index.data().toString().toUtf8().constData()).arg(interval).arg(unit);
        }
        break;
      case Qt::EditRole:
        switch(index.column())
        {
        case Address:
            return formatTxAddress(rec, false);
        case TxHash:
            return formatTxHash(rec);
        case Age:
            return qint64(rec->getAge());
        case CoinDay:
            return qint64(rec->coinAge);
        case Balance:
            return qint64(rec->nValue);
        case MintProbability:
            return getDayToMint(rec);
        }
        break;
      case Qt::BackgroundColorRole:
        int minAge = params.nStakeMinAge / 60 / 60 / 24;
        int maxAge = params.nStakeMaxAge / 60 / 60 / 24;
        if(rec->getAge() < minAge)
        {
            return COLOR_MINT_YOUNG;
        }
        else if (rec->getAge() >= minAge && rec->getAge() < maxAge)
        {
            return COLOR_MINT_MATURE;
        }
        else
        {
            return COLOR_MINT_OLD;
        }
        break;

    }
    return QVariant();
}

void MintingTableModel::setMintingInterval(int interval)
{
    mintingInterval = interval;
}

QString MintingTableModel::lookupAddress(const std::string &address, bool tooltip) const
{
    QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
    QString description;
    if(!label.isEmpty())
    {
        description += label + QString(" ");
    }
    if(label.isEmpty() || tooltip)
    {
        description += QString(" (") + QString::fromStdString(address) + QString(")");
    }
    return description;
}

double MintingTableModel::getDayToMint(KernelRecord *wtx) const
{
    const CBlockIndex *p = GetLastBlockIndex(chainActive.Tip(), true);
    double difficulty = p->GetBlockDifficulty();

    double prob = wtx->getProbToMintWithinNMinutes(difficulty, mintingInterval);
    prob = prob * 100;
    return prob;
}

QString MintingTableModel::formatDayToMint(KernelRecord *wtx) const
{
    double prob = getDayToMint(wtx);
    return QString::number(prob, 'f', 6) + "%";
}

QString MintingTableModel::formatTxAddress(const KernelRecord *wtx, bool tooltip) const
{
    return QString::fromStdString(wtx->address);
}

QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
{
    return QString::fromStdString(wtx->hash.ToString());
}

QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
{
    return QString::number(wtx->coinAge);
}

QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
{
    int64_t nAge = wtx->getAge();
    return QString::number(nAge);
}

QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
{
    return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
}

QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
        else if (role == Qt::TextAlignmentRole)
        {
            return column_alignments[section];
        } else if (role == Qt::ToolTipRole)
        {
            switch(section)
            {
            case Address:
                return tr("Destination address of the output.");
            case TxHash:
                return tr("Original transaction id.");
            case Age:
                return tr("Age of the transaction in days.");
            case Balance:
                return tr("Balance of the output.");
            case CoinDay:
                return tr("Coin age in the output.");
            case MintProbability:
                return tr("Chance to mint a block within given time interval.");
            }
        }
    }
    return QVariant();
}

QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    KernelRecord *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MintingTableModel::updateDisplayUnit()
{
    // emit dataChanged to update Balance column with the current unit
    Q_EMIT dataChanged(index(0, Balance), index(priv->size()-1, Balance));
}
<|endoftext|>


<commit_before>#include <osgAnimation/RigGeometry>
#include <osgDB/ObjectWrapper>
#include <osgDB/InputStream>
#include <osgDB/OutputStream>

static bool checkInfluenceMap( const osgAnimation::RigGeometry& geom )
{
    return geom.getInfluenceMap()->size()>0;
}

static bool readInfluenceMap( osgDB::InputStream& is, osgAnimation::RigGeometry& geom )
{
    osgAnimation::VertexInfluenceMap* map = new osgAnimation::VertexInfluenceMap;
    unsigned int size = is.readSize(); is >> is.BEGIN_BRACKET;
    for ( unsigned int i=0; i<size; ++i )
    {
        std::string name;
        unsigned int viSize = 0;
        is >> is.PROPERTY("VertexInfluence") >> name; viSize = is.readSize(); is >> is.BEGIN_BRACKET;

        osgAnimation::VertexInfluence vi;
        vi.setName( name );
        vi.reserve( viSize );
        for ( unsigned int j=0; j<viSize; ++j )
        {
            int index = 0;
            float weight = 0.0f;
            is >> index >> weight;
            vi.push_back( osgAnimation::VertexIndexWeight(index, weight) );
        }
        (*map)[name] = vi;
        is >> is.END_BRACKET;
    }
    is >> is.END_BRACKET;

    if ( !map->empty() ) geom.setInfluenceMap( map );
    return true;
}

static bool writeInfluenceMap( osgDB::OutputStream& os, const osgAnimation::RigGeometry& geom )
{
    const osgAnimation::VertexInfluenceMap* map = geom.getInfluenceMap();
    os.writeSize(map->size()); os << os.BEGIN_BRACKET << std::endl;
    for ( osgAnimation::VertexInfluenceMap::const_iterator itr=map->begin();
          itr!=map->end(); ++itr )
    {
        std::string name = itr->first;
        const osgAnimation::VertexInfluence& vi = itr->second;
        if ( name.empty() ) name = "Empty";

        os << os.PROPERTY("VertexInfluence") << name; os.writeSize(vi.size()) ; os << os.BEGIN_BRACKET << std::endl;

        for ( osgAnimation::VertexInfluence::const_iterator vitr=vi.begin();
              vitr != vi.end(); ++vitr )
        {
            os << vitr->first << vitr->second << std::endl;
        }
        os << os.END_BRACKET << std::endl;
    }
    os << os.END_BRACKET << std::endl;
    return true;
}

REGISTER_OBJECT_WRAPPER( osgAnimation_RigGeometry,
                         new osgAnimation::RigGeometry,
                         osgAnimation::RigGeometry,
                         "osg::Object osg::Drawable osg::Geometry osgAnimation::RigGeometry" )
{
    ADD_USER_SERIALIZER( InfluenceMap );  // _vertexInfluenceMap
    ADD_OBJECT_SERIALIZER( SourceGeometry, osg::Geometry, NULL );  // _geometry
}
<commit_msg>From Jan Ciger, "Here is a little patch to fix a bug in the InfluenceMap serialization. The names of the maps weren't quoted properly and therefore it was breaking loading of rigged models exported from e.g. Blender. Also names that contained spaces wouldn't have been parsed properly. "<commit_after>#include <osgAnimation/RigGeometry>
#include <osgDB/ObjectWrapper>
#include <osgDB/InputStream>
#include <osgDB/OutputStream>

static bool checkInfluenceMap( const osgAnimation::RigGeometry& geom )
{
    return geom.getInfluenceMap()->size()>0;
}

static bool readInfluenceMap( osgDB::InputStream& is, osgAnimation::RigGeometry& geom )
{
    osgAnimation::VertexInfluenceMap* map = new osgAnimation::VertexInfluenceMap;
    unsigned int size = is.readSize(); is >> is.BEGIN_BRACKET;
    for ( unsigned int i=0; i<size; ++i )
    {
        std::string name;
        unsigned int viSize = 0;
        is >> is.PROPERTY("VertexInfluence");
        is.readWrappedString(name);
        viSize = is.readSize(); is >> is.BEGIN_BRACKET;

        osgAnimation::VertexInfluence vi;
        vi.setName( name );
        vi.reserve( viSize );
        for ( unsigned int j=0; j<viSize; ++j )
        {
            int index = 0;
            float weight = 0.0f;
            is >> index >> weight;
            vi.push_back( osgAnimation::VertexIndexWeight(index, weight) );
        }
        (*map)[name] = vi;
        is >> is.END_BRACKET;
    }
    is >> is.END_BRACKET;

    if ( !map->empty() ) geom.setInfluenceMap( map );
    return true;
}

static bool writeInfluenceMap( osgDB::OutputStream& os, const osgAnimation::RigGeometry& geom )
{
    const osgAnimation::VertexInfluenceMap* map = geom.getInfluenceMap();
    os.writeSize(map->size()); os << os.BEGIN_BRACKET << std::endl;
    for ( osgAnimation::VertexInfluenceMap::const_iterator itr=map->begin();
          itr!=map->end(); ++itr )
    {
        std::string name = itr->first;
        const osgAnimation::VertexInfluence& vi = itr->second;
        if ( name.empty() ) name = "Empty";

        os << os.PROPERTY("VertexInfluence");
        os.writeWrappedString(name);
        os.writeSize(vi.size()) ; os << os.BEGIN_BRACKET << std::endl;

        for ( osgAnimation::VertexInfluence::const_iterator vitr=vi.begin();
              vitr != vi.end(); ++vitr )
        {
            os << vitr->first << vitr->second << std::endl;
        }
        os << os.END_BRACKET << std::endl;
    }
    os << os.END_BRACKET << std::endl;
    return true;
}

REGISTER_OBJECT_WRAPPER( osgAnimation_RigGeometry,
                         new osgAnimation::RigGeometry,
                         osgAnimation::RigGeometry,
                         "osg::Object osg::Drawable osg::Geometry osgAnimation::RigGeometry" )
{
    ADD_USER_SERIALIZER( InfluenceMap );  // _vertexInfluenceMap
    ADD_OBJECT_SERIALIZER( SourceGeometry, osg::Geometry, NULL );  // _geometry
}
<|endoftext|>